From 9db92d13aba18a081be6e9f6280ecf215398eb58 Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Thu, 5 Mar 2026 01:31:02 +0800 Subject: [PATCH] feat: enhance send file download token with JTI for improved validation --- src/handlers/sends.ts | 9 ++++++++- src/utils/jwt.ts | 11 +++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/handlers/sends.ts b/src/handlers/sends.ts index 58b0988..582114c 100644 --- a/src/handlers/sends.ts +++ b/src/handlers/sends.ts @@ -1195,11 +1195,19 @@ export async function handleDownloadSendFile( return errorResponse('Token mismatch', 401); } + const storage = new StorageService(env.DB); const object = await env.ATTACHMENTS.get(getSendFilePath(sendId, fileId)); if (!object) { return errorResponse('Send file not found', 404); } + // Reuse the existing one-time token store used by attachment downloads. + // Prefix avoids accidental cross-domain JTI collisions. + const firstUse = await storage.consumeAttachmentDownloadToken(`send:${claims.jti}`, claims.exp); + if (!firstUse) { + return errorResponse('Invalid or expired token', 401); + } + return new Response(object.body, { headers: { 'Content-Type': 'application/octet-stream', @@ -1287,4 +1295,3 @@ export async function issueSendAccessToken( const token = await createSendAccessToken(send.id, jwt.secret); return { token }; } - diff --git a/src/utils/jwt.ts b/src/utils/jwt.ts index 01a1533..c15f603 100644 --- a/src/utils/jwt.ts +++ b/src/utils/jwt.ts @@ -181,6 +181,7 @@ export async function verifyFileDownloadToken( export interface SendFileDownloadClaims { sendId: string; fileId: string; + jti: string; exp: number; } @@ -194,6 +195,7 @@ export async function createSendFileDownloadToken( const payload: SendFileDownloadClaims = { sendId, fileId, + jti: createRefreshToken(), exp: now + LIMITS.auth.fileDownloadTokenTtlSeconds, }; @@ -240,6 +242,15 @@ export async function verifySendFileDownloadToken( if (!valid) return null; const payload: SendFileDownloadClaims = JSON.parse(new TextDecoder().decode(base64UrlDecode(payloadB64))); + if ( + typeof payload.sendId !== 'string' || + typeof payload.fileId !== 'string' || + typeof payload.jti !== 'string' || + !payload.jti || + typeof payload.exp !== 'number' + ) { + return null; + } const now = Math.floor(Date.now() / 1000); if (payload.exp < now) return null;