Add a hash-based password bypass link
All checks were successful
Docker / build-and-push (push) Successful in 3m8s

The upload password gate can now be skipped with a bookmarkable
#psst=<sha256-hash> fragment instead of typing the password each time.
The server accepts either the plaintext password (X-Upload-Password) or
its SHA-256 hash (X-Upload-Password-Hash), compared timing-safely. The
client computes the hash locally after a normal password entry, so the
bypass link never contains the real password — only a one-way digest of
it, kept out of the URL query string (and thus server logs) by living in
the fragment, same as the decryption key.
This commit is contained in:
explewd
2026-07-09 19:48:56 +02:00
parent 22619ebd11
commit 121dbe81f1
5 changed files with 91 additions and 16 deletions

View File

@@ -1,7 +1,15 @@
export async function checkPassword(password: string): Promise<boolean> {
export type Credential = { kind: 'password' | 'hash'; value: string }
function credentialHeaders(cred: Credential): Record<string, string> {
return cred.kind === 'password'
? { 'X-Upload-Password': cred.value }
: { 'X-Upload-Password-Hash': cred.value }
}
export async function checkCredential(cred: Credential): Promise<boolean> {
const res = await fetch('/api/auth/check', {
method: 'POST',
headers: { 'X-Upload-Password': password },
headers: credentialHeaders(cred),
})
const body = (await res.json()) as { ok: boolean }
return body.ok
@@ -9,13 +17,13 @@ export async function checkPassword(password: string): Promise<boolean> {
export async function uploadDrop(
ciphertext: Uint8Array,
password: string,
cred: Credential,
): Promise<{ id: string; ttl: number }> {
const res = await fetch('/api/drops', {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',
'X-Upload-Password': password,
...credentialHeaders(cred),
},
body: ciphertext.slice().buffer,
})