fix: stop accepting backup blob credentials from the URL

This commit is contained in:
shuaiplus
2026-07-26 16:00:39 +08:00
parent 34fd2f0259
commit 652952379b
2 changed files with 25 additions and 15 deletions
+8 -13
View File
@@ -1247,19 +1247,14 @@ export async function handleDownloadAdminBackupAttachment(request: Request, env:
if (!isAdmin(actorUser)) return errorResponse('Forbidden', 403); if (!isAdmin(actorUser)) return errorResponse('Forbidden', 403);
try { try {
const url = new URL(request.url); // Read the request body only. Accepting these fields from the query string
let input: { blobName?: unknown; masterPasswordHash?: unknown } = {}; // would put the master-password authentication hash in the URL, where it is
if (request.method === 'POST') { // captured by request logs, browser history and Referer headers.
try { let input: { blobName?: unknown; masterPasswordHash?: unknown };
input = await request.json<{ blobName?: unknown; masterPasswordHash?: unknown }>(); try {
} catch { input = await request.json<{ blobName?: unknown; masterPasswordHash?: unknown }>();
return errorResponse('Backup attachment download payload is invalid', 400); } catch {
} return errorResponse('Backup attachment download payload is invalid', 400);
} else {
input = {
blobName: url.searchParams.get('blobName') || '',
masterPasswordHash: url.searchParams.get('masterPasswordHash') || '',
};
} }
const verificationError = await requireBackupUserVerification( const verificationError = await requireBackupUserVerification(
+17 -2
View File
@@ -14,6 +14,7 @@ import {
handleRunAdminConfiguredBackup, handleRunAdminConfiguredBackup,
handleUpdateAdminBackupSettings, handleUpdateAdminBackupSettings,
} from './handlers/backup'; } from './handlers/backup';
import { errorResponse } from './utils/response';
export async function handleAdminBackupRoute( export async function handleAdminBackupRoute(
request: Request, request: Request,
@@ -26,8 +27,22 @@ export async function handleAdminBackupRoute(
return handleAdminExportBackup(request, env, actorUser); return handleAdminExportBackup(request, env, actorUser);
} }
if (path === '/api/admin/backup/blob' && (method === 'GET' || method === 'POST')) { if (path === '/api/admin/backup/blob') {
return handleDownloadAdminBackupAttachment(request, env, actorUser); // POST only: this endpoint requires master-password verification, and a GET
// could only carry that credential in the query string, where it would leak
// into request logs, proxy logs, browser history and Referer headers.
// The credential is the same value clients send to /identity/connect/token,
// so a leaked copy is enough to sign in as this admin.
if (method === 'POST') {
return handleDownloadAdminBackupAttachment(request, env, actorUser);
}
if (method === 'GET') {
return errorResponse(
'Use POST with a JSON body for this endpoint. Credentials must not be sent in the URL.',
405
);
}
return null;
} }
if (path === '/api/admin/backup/settings') { if (path === '/api/admin/backup/settings') {