feat: add uriChecksum handling and sha256Base64 function for enhanced security

This commit is contained in:
shuaiplus
2026-05-16 16:22:43 +08:00
parent b7878ffe01
commit e641da517d
3 changed files with 16 additions and 4 deletions
+2 -2
View File
@@ -19,7 +19,7 @@ interface CiphersImportRequest {
sshKey?: any | null; sshKey?: any | null;
key?: string | null; key?: string | null;
login?: { login?: {
uris?: Array<{ uri: string | null; match?: number | null }> | null; uris?: Array<{ uri: string | null; uriChecksum?: string | null; match?: number | null }> | null;
username?: string | null; username?: string | null;
password?: string | null; password?: string | null;
totp?: string | null; totp?: string | null;
@@ -195,7 +195,7 @@ export async function handleCiphersImport(request: Request, env: Env, userId: st
uris: login.uris?.map((u: any) => ({ uris: login.uris?.map((u: any) => ({
...u, ...u,
uri: u.uri ?? null, uri: u.uri ?? null,
uriChecksum: null, uriChecksum: u.uriChecksum ?? null,
match: u.match ?? null, match: u.match ?? null,
})) || null, })) || null,
totp: login.totp ?? null, totp: login.totp ?? null,
+8 -2
View File
@@ -1,4 +1,4 @@
import { base64ToBytes, decryptBw, decryptBwFileData, decryptStr, encryptBw, encryptBwFileData } from '../crypto'; import { base64ToBytes, decryptBw, decryptBwFileData, decryptStr, encryptBw, encryptBwFileData, sha256Base64 } from '../crypto';
import type { import type {
Cipher, Cipher,
CipherPasswordHistoryEntry, CipherPasswordHistoryEntry,
@@ -574,12 +574,18 @@ async function encryptUris(
entry?.extra && typeof entry.extra === 'object' entry?.extra && typeof entry.extra === 'object'
? { ...entry.extra } ? { ...entry.extra }
: {}; : {};
if (String(entry?.originalUri || '').trim() !== trimmed) { const canReuseChecksum = String(entry?.originalUri || '').trim() === trimmed;
if (!canReuseChecksum) {
delete preservedExtra.uriChecksum; delete preservedExtra.uriChecksum;
} }
const preservedChecksum = typeof preservedExtra.uriChecksum === 'string' && looksLikeCipherString(preservedExtra.uriChecksum)
? preservedExtra.uriChecksum
: null;
const uriChecksum = preservedChecksum || await encryptTextValue(await sha256Base64(trimmed), enc, mac);
out.push({ out.push({
...preservedExtra, ...preservedExtra,
uri: await encryptTextValue(trimmed, enc, mac), uri: await encryptTextValue(trimmed, enc, mac),
uriChecksum,
match: typeof entry?.match === 'number' && Number.isFinite(entry.match) ? entry.match : null, match: typeof entry?.match === 'number' && Number.isFinite(entry.match) ? entry.match : null,
}); });
} }
+6
View File
@@ -22,6 +22,12 @@ export function toBufferSource(bytes: Uint8Array): ArrayBuffer {
return new Uint8Array(bytes).buffer; return new Uint8Array(bytes).buffer;
} }
export async function sha256Base64(value: string): Promise<string> {
const bytes = new TextEncoder().encode(value);
const hash = await crypto.subtle.digest('SHA-256', toBufferSource(bytes));
return bytesToBase64(new Uint8Array(hash));
}
const hmacSha256KeyCache = new WeakMap<Uint8Array, Promise<CryptoKey>>(); const hmacSha256KeyCache = new WeakMap<Uint8Array, Promise<CryptoKey>>();
const aesCbcEncryptKeyCache = new WeakMap<Uint8Array, Promise<CryptoKey>>(); const aesCbcEncryptKeyCache = new WeakMap<Uint8Array, Promise<CryptoKey>>();
const aesCbcDecryptKeyCache = new WeakMap<Uint8Array, Promise<CryptoKey>>(); const aesCbcDecryptKeyCache = new WeakMap<Uint8Array, Promise<CryptoKey>>();