Severity: High
File: backend/src/lib/downloadTokens.ts:44-52
CWE: CWE-613 — Insufficient Session Expiration
OWASP: A07:2021 — Identification and Authentication Failures
Description
HMAC-signed download tokens carry no expiration field. Once issued and embedded in chat history, they are permanently valid:
export function signDownload(path: string, filename: string): string {
const payload = JSON.stringify({ p: path, f: filename });
// No `exp` field — token never expires
Impact
Users who later lose access to a project or document — through removal from shared_with, document deletion, or account termination — retain the ability to download files via any download URL saved from their chat history. The download route re-checks ownership at request time (providing partial mitigation), but if the signing secret is ever compromised (see #66), all tokens ever issued become immediately forgeable with no expiry to limit the window.
Fix
Add an exp field to the token payload and validate it at verify time:
export function signDownload(path: string, filename: string, ttlSeconds = 900): string {
const exp = Math.floor(Date.now() / 1000) + ttlSeconds;
const payload = JSON.stringify({ p: path, f: filename, exp });
const enc = b64urlEncode(Buffer.from(payload, "utf8"));
const sig = crypto.createHmac("sha256", getSecret()).update(enc).digest();
return `${enc}.${b64urlEncode(sig)}`;
}
export function verifyDownload(token: string): { path: string; filename: string } | null {
// ... existing HMAC check ...
const parsed = JSON.parse(b64urlDecode(enc).toString("utf8"));
if (!parsed?.p || !parsed?.f) return null;
if (parsed.exp && Math.floor(Date.now() / 1000) > parsed.exp) return null;
return { path: parsed.p, filename: parsed.f };
}
Consider whether download links in chat history should be refreshed on load (client fetches a new signed URL when rendering old messages).
Remediation tier: High — fix before next release.
Severity: High
File:
backend/src/lib/downloadTokens.ts:44-52CWE: CWE-613 — Insufficient Session Expiration
OWASP: A07:2021 — Identification and Authentication Failures
Description
HMAC-signed download tokens carry no expiration field. Once issued and embedded in chat history, they are permanently valid:
Impact
Users who later lose access to a project or document — through removal from
shared_with, document deletion, or account termination — retain the ability to download files via any download URL saved from their chat history. The download route re-checks ownership at request time (providing partial mitigation), but if the signing secret is ever compromised (see #66), all tokens ever issued become immediately forgeable with no expiry to limit the window.Fix
Add an
expfield to the token payload and validate it at verify time:Consider whether download links in chat history should be refreshed on load (client fetches a new signed URL when rendering old messages).
Remediation tier: High — fix before next release.