Signierte Datei-Links (15 min) fuer Anhaenge im Ticketsystem
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
acf6ff9ee1
commit
5f5df7ba1a
2 files changed
+58
No files matched your search
@@ -0,0 +1,27 @@
|
||||
import { createHmac, timingSafeEqual } from 'node:crypto'
|
||||
import { config } from '../config.js'
|
||||
|
||||
/**
|
||||
* Signierte Datei-Links fuer das Ticketsystem (Spec 2026-10-01, Abschnitt 4 "Dateien ohne Header"): <img>, <audio>
|
||||
* und Downloads koennen keinen Authorization-Header tragen. Der Link gilt 15 Minuten und nur fuer genau diese
|
||||
* Datei dieses Kunden; signiert mit dem Sitzungs-Geheimnis des Portals.
|
||||
*/
|
||||
export const FILE_LINK_TTL_MS = 15 * 60 * 1000
|
||||
|
||||
const digest = (kind, customerId, id, exp) =>
|
||||
createHmac('sha256', config.sessionSecret).update(`${kind}:${customerId}:${id}:${exp}`).digest('hex')
|
||||
|
||||
export function signFileLink({ kind, customerId, id, now = Date.now() }) {
|
||||
const exp = now + FILE_LINK_TTL_MS
|
||||
return { exp, sig: digest(kind, customerId, id, exp) }
|
||||
}
|
||||
|
||||
/** exp kommt aus der Adresse (Text oder Zahl); falsch bei Ablauf, fremder Datei oder kaputter Signatur. */
|
||||
export function verifyFileLink({ kind, customerId, id, exp, sig, now = Date.now() }) {
|
||||
const expiry = typeof exp === 'number' ? exp : /^\d+$/.test(String(exp ?? '')) ? Number(exp) : NaN
|
||||
if (!Number.isFinite(expiry) || now >= expiry) return false
|
||||
if (typeof sig !== 'string' || !/^[0-9a-f]{64}$/.test(sig)) return false
|
||||
const expected = Buffer.from(digest(kind, customerId, id, expiry), 'hex')
|
||||
const given = Buffer.from(sig, 'hex')
|
||||
return given.length === expected.length && timingSafeEqual(given, expected)
|
||||
}
|
||||
Reference in new issue
Block a user