Equal hashes on non-equal JS strings are dangerous to the ecosystem
还没有人认领这个 Issue。
- 主要语言
- JavaScript
- 星标
- 122k
- 派生
- 37.4k
- 平均合并
- 4 天 3 小时
- 30 天内合并 PR
- 272
描述
Instead, anything in crypto module accepting JS strings should verify that the strings are well-formed before converting them to Buffers, and throw
In before: the argument that those strings would be treated as equal in other contexts is not valid
E.g., using bn.js@4:
import BN from 'bn.js' // 4
import { hash } from 'node:crypto'
const [a, b] = JSON.parse(payload) // user input
console.log('Hashes equal?', hash('sha256', a) === hash('sha256', b))
console.log('Strings equal?', a === b)
console.log('bn.js values:', (new BN(a)).toNumber(), (new BN(b)).toNumber())
Output:
Hashes equal? true
Strings equal? false
bn.js values: 55229 57036
If anything anywhere uses string hashes for any purpose like k/nonce calculation (ref: GHSA-vjh7-7g9h-fjfh), e.g. in curve cryptography or in a block/stream cipher, this can lead to private key/data exposure
Also obviously it allows to do things like this:
import { hash } from 'node:crypto'
const [a, b, c, d] = JSON.parse(payload) // user input
const sha256 = (x) => hash('sha256', x)
console.log('a === b?', sha256(a) === sha256(b))
console.log('c === d?', sha256(c) === sha256(d))
console.log('a + c === b + d?', sha256(a + c) === sha256(b + d))
a === b? true
c === d? true
a + c === b + d? false
As a solution, enforce usage of String.prototype.isWellFormed on all string input and throw
Obviously, also applicable to sign/verify and other APIs:
import { generateKeyPairSync, createSign, createVerify } from 'node:crypto'
const { privateKey, publicKey } = generateKeyPairSync('rsa', { modulusLength: 2048 })
const [a, b] = JSON.parse(payload) // user input
const sign = createSign('SHA256')
sign.update(a)
sign.end()
const signature = sign.sign(privateKey)
const verify = createVerify('SHA256')
verify.update(b)
verify.end()
console.log('types:', typeof a, typeof b)
console.log('verified?', verify.verify(publicKey, signature))
console.log('a === b?', a === b)
types: string string
verified? true
a === b? false
WebCrypto is not affected as it doesn't accept strings
Node.js accepts JS strings but operates on byte arrays, and js-string-to-byte-array transform is not injective
There should be a safeguard against transform collisions
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
首先盘点接受 JavaScript 字符串的 crypto 模块入口点,包括 hash、sign 和 verify,并追踪它们将字符串转换为字节数组的过程。将示例与 String.prototype.isWellFormed 的行为进行比较;完成标准是为相关 API 定义明确的保护措施,并通过测试表明格式不正确的字符串不会被视为等价。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- javascript, node.js
- 领域
- backend, cryptography, security
- Issue 类型
- 缺陷
- 难度
- 5/5
- 预计耗时
- 一周以上
- 活跃度
- 冷清
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100