Skip to content

Accept a SAS scoped to a blob version - #2893

Open
Andrew Gaul (gaul) wants to merge 4 commits into
Azure:mainfrom
gaul:blob-version-sas
Open

Andrew Gaul (gaul) wants to merge 4 commits into
Azure:mainfrom
gaul:blob-version-sas

Conversation

@gaul

@gaul Andrew Gaul (gaul) commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

A service SAS for a single blob version has signed resource bv and signs the version ID in the string-to-sign field where a snapshot SAS signs the snapshot time. The SDKs generate one whenever a SAS is requested for a client bound to a version, for example blobClient.withVersion(id).generateSasUrl(...). Azurite refused the resource type outright, so such a SAS URL could not read the version it named. Copy Blob and Put Blob From URL fetch their source over that same SAS path, so a copy from a version SAS URL failed with CannotVerifyCopySource. #2735 lists this among its limitations.

This change accepts bv and reads the signed value from the request's versionid query parameter. It signs the blob name in the canonicalized resource. Because the version ID is in the signature, the SAS authorizes the version it names and nothing else: another version, or the current blob, gets 403.

A version SAS is checked against a permission table of its own. The table lists only the operations whose handlers act on the version that versionid names: Get Blob, Get Blob Properties, Get/Set Blob Tags, Set Blob Tier and Delete Blob. Any other operation would act on the base blob, so it gets AuthorizationResourceTypeMismatch. Deleting the version takes the Delete version permission (x), as Azure requires, rather than d.

A snapshot SAS (bs) gets the same treatment. It used to be checked against the container table, so a writable one could set the base blob's metadata. Its table lists Get Blob, Get Blob Properties, Get Block List, Get Page Ranges, Delete Blob and Get/Set Blob Tags. Set Blob Tier is left out because its handler ignores the snapshot selector. A bs or bv SAS with a signed version before 2018-11-09 is refused, because those signing formats bind neither the resource nor the snapshot or version.

Get Block List on a snapshot listed the base blob's uncommitted blocks for all or uncommitted, under any authorization, because both stores looked staged blocks up by blob name alone. A snapshot has no uncommitted blocks, so they are now listed only for the base blob.

The user delegation string-to-sign builders left the snapshot-time field empty for every resource, and two of them signed the blob name only for b. So a user delegation SAS was refused for a version and for a snapshot alike. They now sign both, as the shared-key builders already did for snapshots.

Tests in blockblob.versioning.test.ts:

  • a version SAS reads its version;
  • it does not read another version or the current blob;
  • it cannot write the base blob or read its block list;
  • it deletes its version with x, but not with d alone;
  • a snapshot SAS, whether signed with the account key or a user delegation key, cannot set the base blob's metadata;
  • a snapshot or version SAS signed in the 2017-11-09 format is refused, while the same token for the blob itself is accepted;
  • a snapshot SAS reads the snapshot's block list and page ranges, and Get Block List on a snapshot lists no uncommitted blocks (blockblob.test.ts, @loki @sql);
  • Put Blob From URL and Copy Blob copy from a version SAS URL;
  • a user delegation SAS reads a version and a snapshot.

Every new test except the one about other versions fails without the change. That one also passed before, when every version SAS was refused. The full @loki blob suite passes, and lint and tsc are clean. I could not run the @sql suite locally, so the SQL store change is covered only by CI.

I found this while testing S3Proxy's Azure versioning support (gaul/s3proxy#1139), which copies a version by its SAS URL, against Azurite main.

References #665.

A service SAS for one blob version carries signed resource "bv" and signs
the version ID in the field where a snapshot SAS signs the snapshot time.
Azurite refused the resource type outright, so no SAS URL for a version
could read it, and Copy Blob and Put Blob From URL, which fetch their
source through that same SAS path, failed with CannotVerifyCopySource.

Accept "bv", read the value it signs from the request's versionid query
parameter, sign the blob name in its canonicalized resource, and judge
it by the blob permissions.  The signature then binds the version, so
the SAS authorizes no other version and not the current blob.

The user delegation builders left that field empty for every resource,
which refused a user delegation SAS for a version and for a snapshot
alike, and two of them omitted the blob name for anything but "b".  They
now sign both, as the service SAS builders already did for snapshots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pJPwDB8sUdbqTgi5xEtRx

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical SAS authorization flaws allow version-scoped tokens to access or modify the current blob and permit version deletion with d while rejecting the required x permission.

2 open issues
What changed in this PR

This PR adds version-scoped SAS support to Azurite’s Blob authentication, addressing a limitation of the existing Loki-backed versioning implementation.

Changes:

  • Accepts bv resources and includes the version ID in signature validation.
  • Corrects snapshot and version signing for user delegation SAS.
  • Adds read, isolation, and copy tests and updates support documentation.
  1. Impact analysis

    • Package: Azurite Blob service; no package version change shown.
    • Usage: Reading and copying blob versions through SAS URLs.
    • Impact and risk: Authorization boundaries remain sensitive.
  2. Build and conflict resolution

    • Commands run: None; verification was limited to source inspection.
    • Result: Build and merge-conflict status were not independently verified.
  3. Tests

    • Added: Version reads, selector tampering, copy operations, and user delegation reads.
    • Coverage: Additional authorization regression tests are needed.
  4. Test suite

    • Commands run: None.
    • Passing: The PR description reports a passing Loki Blob suite, lint, and TypeScript checks.
    • Pending: Independent execution and follow-up regression coverage.
  5. Changelog

    • Entry added: Version SAS support and corrected snapshot user delegation signing.
    • Location: ChangeLog.md, Blob section.
  6. PR comments

    • Threads reviewed: Authentication and authorization feedback.
    • Actions taken: Clarified required changes and fix guidance.
    • Unresolved items: Two actionable comments remain.
  7. Self-rating

    • Score: Not assigned.
    • Reasoning: Source evidence supports the comments; runtime validation was not performed.
  8. Iteration

    • Improvements made after self-review: Tightened explanations and checked proposed fixes against existing permission handling.
  9. Summary

    • Why the update matters: Enables SDK-generated SAS URLs for individual versions.
    • Risk if not applied: Version-SAS reads and copies remain unsupported.
    • Final status: Follow-up authorization changes and tests are needed.
File Description
tests/​blob/​apis/​blockblob.versioning.test.ts Adds version-SAS read, isolation, copy, and delegation tests.
src/​blob/​authentication/​IBlobSASSignatureValues.ts Updates canonical resource and snapshot/version signing.
src/​blob/​authentication/​BlobSASResourceType.ts Defines the bv resource type.
src/​blob/​authentication/​BlobSASAuthenticator.ts Accepts bv, reads versionid, and selects blob permissions.
README.md Removes version-SAS support limitations.
docs/​designs/​2025-12-blob-versioning.md Documents version-SAS signing and updates limitations.
ChangeLog.md Records version and snapshot SAS support changes.

Review effort: Balanced

A version SAS was judged by the blob permission table, which lists every
blob operation.  Most of their handlers ignore versionid, so a version
SAS reached the base blob through them: with r it read the base blob's
block list, and with w it set the base blob's metadata or overwrote it.
It also let d delete the version, where Azure takes the Delete version
permission, x, to delete one, and refused x.

Judge a version SAS by a table of its own that lists only the operations
acting on the version the request names -- Get Blob, Get Blob
Properties, Get and Set Blob Tags, Set Blob Tier and Delete Blob -- with
Delete Blob taking x.  Anything else answers
AuthorizationResourceTypeMismatch rather than the internal error an
operation missing from a table produces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pJPwDB8sUdbqTgi5xEtRx
Copilot AI balanced review requested due to automatic review settings November 7, 2026 02:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical SAS isolation flaws allow legacy version tokens to access other blob versions and writable user delegation snapshot tokens to modify the current blob, requiring authorization fixes and regression tests.

2 open issues
2 resolved since last review

Review effort: Balanced

A snapshot SAS was judged by the container permission table, and most of
the operations in it ignore the snapshot parameter, so a writable one
reached the base blob through them: it could set the base blob's
metadata.  That held for a service SAS before, and the user delegation
SAS for a snapshot, accepted since the previous commits, inherited it.
Give the snapshot SAS a table of its own, as the version SAS has, listing
the operations that act on the named snapshot -- Get Blob, Get Blob
Properties, Delete Blob, and Get and Set Blob Tags.  Set Blob Tier stays
out, its handler acting on the base blob whatever snapshot is named.

Signing formats before 2018-11-09 sign neither the signed resource nor
the snapshot time or version ID, so a snapshot or version SAS in one of
them binds nothing: its snapshot or versionid could be changed or dropped
and the signature still verified.  The bs and bv resources date from
2018-11-09, so refuse them in older formats.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pJPwDB8sUdbqTgi5xEtRx
Copilot AI balanced review requested due to automatic review settings November 7, 2026 02:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The snapshot SAS allowlist rejects valid Get Page Ranges and committed block-list reads; restoring access also requires guarding against exposure of the base blob’s uncommitted blocks and adding regression tests.

1 open issue
2 resolved since last review

Review effort: Balanced

The snapshot permission table left out Get Block List and Get Page
Ranges, which a snapshot SAS was allowed before through the container
table, and whose handlers read the snapshot named.  Restore both, at
Read.

Get Block List on a snapshot also listed the base blob's uncommitted
blocks for blocklisttype=all or uncommitted, whatever the
authorization: both stores look staged blocks up by blob name alone,
and they belong to the base blob.  A snapshot or version is committed
and has none, so list them only for the base blob.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014pJPwDB8sUdbqTgi5xEtRx
Copilot AI balanced review requested due to automatic review settings November 7, 2026 02:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are recorded for the version-scoped SAS and snapshot authorization changes.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants