Preview: GCM-Tag-Laenge fest, Inhalt validiert
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
20ca1de694
commit
def490ceca
2 files changed
+31
-3
No files matched your search
@@ -15,7 +15,7 @@ export function envKey() {
|
||||
|
||||
export function encryptEnv(obj, key) {
|
||||
const iv = crypto.randomBytes(12)
|
||||
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv)
|
||||
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv, { authTagLength: 16 })
|
||||
const ct = Buffer.concat([cipher.update(JSON.stringify(obj || {}), 'utf8'), cipher.final()])
|
||||
const tag = cipher.getAuthTag()
|
||||
return ['v1', iv, tag, ct].map((p) => (typeof p === 'string' ? p : p.toString('base64'))).join(':')
|
||||
@@ -26,10 +26,16 @@ export function decryptEnv(str, key) {
|
||||
const parts = String(str).split(':')
|
||||
if (parts.length !== 4 || parts[0] !== 'v1') throw new Error('Ungueltiges Variablenformat')
|
||||
const [, iv, tag, ct] = parts.map((p, i) => (i === 0 ? p : Buffer.from(p, 'base64')))
|
||||
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv)
|
||||
if (iv.length !== 12 || tag.length !== 16) throw new Error('Ungueltiges Variablenformat')
|
||||
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv, { authTagLength: 16 })
|
||||
decipher.setAuthTag(tag)
|
||||
const plain = Buffer.concat([decipher.update(ct), decipher.final()]).toString('utf8')
|
||||
return JSON.parse(plain)
|
||||
const obj = JSON.parse(plain)
|
||||
if (!obj || typeof obj !== 'object' || Array.isArray(obj)
|
||||
|| !Object.values(obj).every((v) => typeof v === 'string')) {
|
||||
throw new Error('Ungueltiges Variablenformat')
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
export function envNames(str, key) {
|
||||
|
||||
@@ -79,3 +79,25 @@ test('maskLog', () => {
|
||||
assert.equal(maskLog('', env), '')
|
||||
assert.equal(maskLog('text', {}), 'text')
|
||||
})
|
||||
|
||||
test('gekuerzter Tag, falsche IV-Laenge, manipulierter Ciphertext => Fehler', () => {
|
||||
const p = encryptEnv({ A: 'geheim' }, key).split(':')
|
||||
const b = (i) => Buffer.from(p[i], 'base64')
|
||||
const join = (x) => x.join(':')
|
||||
const short = [...p]; short[2] = b(2).subarray(0, 8).toString('base64')
|
||||
assert.throws(() => decryptEnv(join(short), key))
|
||||
const iv = [...p]; iv[1] = b(1).subarray(0, 8).toString('base64')
|
||||
assert.throws(() => decryptEnv(join(iv), key))
|
||||
const ct = [...p]; const c = b(3); c[0] ^= 1; ct[3] = c.toString('base64')
|
||||
assert.throws(() => decryptEnv(join(ct), key))
|
||||
})
|
||||
|
||||
test('entschluesselter Inhalt muss Objekt mit String-Werten sein', () => {
|
||||
for (const bad of [['x'], 'text', { A: 1 }, null]) {
|
||||
const iv = crypto.randomBytes(12)
|
||||
const c = crypto.createCipheriv('aes-256-gcm', key, iv, { authTagLength: 16 })
|
||||
const ct = Buffer.concat([c.update(JSON.stringify(bad), 'utf8'), c.final()])
|
||||
const s = ['v1', iv, c.getAuthTag(), ct].map((x) => (typeof x === 'string' ? x : x.toString('base64'))).join(':')
|
||||
assert.throws(() => decryptEnv(s, key))
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user