feat: Add optional URL signing for upload and download URLs - #259
Open
isaacsu wants to merge 2 commits into
Open
feat: Add optional URL signing for upload and download URLs#259isaacsu wants to merge 2 commits into
isaacsu wants to merge 2 commits into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hello 👋. Would love to get your feedback on whether this is a good idea?
Summary
The server-proxied upload (
/devstoreaccount1/upload/{id}) and download (/download/{id}) URLs are unauthenticated by default. This PR adds an optional, opt-in expiring HMAC signature on those URLs to limit enumeration and leakage.What changed
lib/url-signing.ts— the core signing/verification module:signQuery()mints an?exp=<unixMs>&sig=<base64url>suffix binding a canonical path + expiry, signed with the active secret.verifySignedRequest()strictly verifiesexp/sigagainst the canonical path, throwing401on any failure (missing/malformed/mismatched/expired). It supports a secondary verify-only secret for rotation.redactSignedPath()strips thesigcredential from any logged path.Storage.getDownloadUrl(proxied path) and theCreateCacheEntryTwirp handler appends a signature when signing is enabled. The upload signature is minted once and covers every chunk PUT plus the finalcomp=blocklistPUT within the TTL (mutable Azure query params are excluded from the signed material).verifySignedRequestbefore doing any work, verifying against the invariant canonical path (/upload/{id},/download/{id}) rather thanevent.path, so route aliases and reverse-proxy prefixes verify consistently.plugins/setup.tsandroutes/[...path].tsroute all path logging throughredactSignedPathso the replayablesignever lands in logs.URL_SIGNING_ENABLED,URL_SIGNING_SECRET,URL_SIGNING_SECRET_SECONDARY) with cross-field validation viaenvSchemaValidated(secrets required and ≥ 16 chars when enabled; boot fails otherwise).tests/url-signing.test.ts(enabled/disabled/rotation/tamper/expiry cases) andtests/url-signing-e2e.test.ts(end-to-end upload/download flow).Configuration
URL_SIGNING_ENABLEDboolean, defaultfalse. Whentrue, upload/download URLs are signed and strictly verified (no unsigned fallback).URL_SIGNING_SECRETURL_SIGNING_SECRET_SECONDARY