An agent that needs to deliver a private report or receive a large upload should not casually expose its storage credentials. A presigned object URL solves a narrower problem: an authorised server signs a specific storage operation, gives the resulting URL to a client, and lets the storage service enforce the signature and its validity period. The URL is a temporary bearer capability, not proof that the person holding it is still entitled to the file. That distinction drives the entire safe design.
Use a presigned URL only after checking the requester, object, permitted method, intended audience and lifetime. Protect the full URL as a secret; do not assume expiry means one-time use; distinguish the signing credential’s lifetime from the requested duration; and verify the object’s final version, content and recipient after transfer. For sensitive work, use an application-controlled download gateway when immediate revocation or per-recipient access decisions matter more than direct-transfer performance.
Contents
- The reader problem: fast transfer without permanent access
- The security model: a bearer capability rather than a login
- First principles: what is actually signed
- Authorization before signing: the critical trust boundary
- Expiry has more than one clock
- A fully checked expiry calculation
- Choosing GET, PUT and object-specific permissions
- Why a successful HTTP response is not an authorised outcome
- Threat model: the link is a secret in ordinary places
- Revocation: what can and cannot be taken back
- Agent-to-tool contract: narrow and machine-checkable
- Failure diagnosis: classify before retrying
- A practical upload example with distinct identities
- Numerical risk budget for high-volume link issuance
- Changed-context transfer: partner receives a revised report
- Operational monitoring without exposing credentials
- Controls for education and workplace documents
- Source-grounded implementation checklist
- Frequently asked questions
- Reader pathways and final decision rule
The reader problem: fast transfer without permanent access
Imagine a tuition organisation exporting a private learning-progress report from a secure archive. The report may be twenty megabytes, too large for a conventional email attachment, and an educational assistant needs to deliver it to one parent after confirming authority. Copying a permanent cloud-access key into the assistant is dangerous; granting the recipient direct storage permissions may be cumbersome. A presigned GET request allows a service that already has object permission to grant a short-lived retrieval capability without sharing its account credentials. The recipient sends the signed HTTP request directly to storage.
The operational trap is that the link represents the authorised HTTP action, not the evolving social relationship behind it. Someone may forward it. Browser history, logs, referrer handling, chat transcripts or analytics may store its query parameters. A person whose ordinary application access was revoked may still hold a link that is valid under its separate signature rules. Storage normally checks whether the request is cryptographically permitted, not whether the human story still makes sense. The safe system therefore treats issuance, delivery and subsequent use as separate decisions.
The practical objective is a transfer with bounded exposure and a verified outcome. Bound exposure by operation, object key, signing principal, expiry and any available policy conditions. Verify the outcome by checking the correct object version, checksum or metadata and recording an application-side evidence trail. This article concentrates on capability issuance and lifecycle; it is distinct from multipart upload integrity and storage retention and delete markers.
The security model: a bearer capability rather than a login
A bearer capability gives the holder permission to perform a particular operation while defined conditions hold. The key feature is transferability: possession may suffice. A presigned URL often carries a signature, credential identifier, timestamp, signed headers and expiry information inside its query string. A storage service recomputes or verifies the signature according to the provider’s signing scheme. The service need not receive the signing secret itself. This is the reason presigning is useful, and also why an unredacted URL belongs in the same security conversation as a sensitive token.
The signature is not encryption of the document, and an HTTPS URL is not magically private once pasted into the wrong conversation. TLS protects transmission between endpoints, while authorization happens through the signed request. Encryption at rest and malware screening are different controls. A recipient who can GET an ordinary object can typically keep a downloaded copy; letting the URL expire cannot erase it. If the content must stop being usable after a business event, a different delivery mechanism, watermarking strategy or legal process may be required.
Do not describe the capability as a one-time code unless the application adds a genuine one-use enforcement mechanism. Amazon S3 documentation explicitly states that a presigned URL can be used multiple times until expiry. A server can maintain one-time issuance records, but direct object requests do not automatically consult that application database. Calling a link ‘single-use’ without controlling every read route is a harmful mismatch between the interface and the underlying protocol.
First principles: what is actually signed
A robust mental model is an input tuple: provider endpoint, HTTP method, canonical resource path, canonical query parameters, selected request headers, credential scope, timestamp and expiration. The signing algorithm turns a canonical representation of the request into a signature. Storage then decides whether the presented request matches what was signed and whether all other permission and policy checks pass. Change a signed element and validation usually fails. The exact canonicalization and required fields vary by provider and signing version; an agent must not invent its own generic URL-signing recipe.
For Google Cloud Storage V4 signing, the primary documentation describes a canonical request with an HTTP method, canonical URI, canonical query string, canonical headers, signed-header names and payload marker. Its required query fields include X-Goog-Algorithm, X-Goog-Credential, X-Goog-Date, X-Goog-Expires and X-Goog-SignedHeaders, plus the signature. X-Goog-Expires has a documented maximum of 604800 seconds. This is a protocol statement, not advice to issue week-long links. Reference: Google Cloud Storage canonical requests.
An agent should call an official provider SDK or an independently reviewed signing service instead of assembling query strings conversationally. The service should construct the final method and headers, enforce a validated object identifier, limit expiration by policy, and return a structured record identifying the operation. Every signed field is part of the contract. A Content-Type bound into a PUT signature must match the client’s actual header. An omitted or changed header can produce an access failure that looks like a storage outage but is really a contract mismatch.
Authorization before signing: the critical trust boundary
The key security decision occurs before the URL exists. The application receives a requester identity, determines which case or file is being requested, checks the requesting person’s role and current rights, and resolves the authorised object by a server-controlled identifier. It must not accept a free-form storage key supplied by the agent or user and assume the key is safe merely because it resembles a report filename. The difference between tenant-A/report.pdf and tenant-B/report.pdf may be a single character.
Separate the human-readable file label from the actual object location. A service can expose report_id=R-204 to the application while resolving the exact internal storage key under the authenticated tenant. The signing principal should have narrowly scoped permission to the required bucket or container and operation. Validate that the object belongs to the intended tenant, that the version is the approved one, and that the link will be delivered only through an authorised channel. This is the point to apply organisation rules, not after the URL has leaked.
An AI agent can propose the next action, but the server should independently enforce the authorization decision. A natural-language instruction such as ‘send me my child’s record’ cannot be trusted as sufficient evidence of parent identity or legal entitlement. Authentication, relationship verification and data-minimisation rules remain application concerns. A tool that returns a presigned URL should accept explicit, validated identifiers, not arbitrary prose claiming that the caller has permission.
Expiry has more than one clock
Suppose a service asks AWS S3 for a link with a twelve-hour URL expiration. That does not guarantee twelve hours of usability. The effective validity also depends on the credentials used to sign the request, current permissions, relevant storage policies and potentially a maximum signature age. AWS states that links signed with temporary credentials cannot outlive those credentials, and revoked or deactivated signing credentials can invalidate links earlier. AWS checks expiry at the time an HTTP request starts; a download that starts just before expiry may continue, while a new request after expiry can fail.
This matters for operators who repeatedly receive an early 403 response and assume the signing library is unreliable. The signer might use a one-hour assumed-role session. The requested twelve-hour value is a wish constrained by the real credential lifetime. The service should calculate an internal ‘not after’ deadline as the minimum of its own risk-based TTL, the provider’s protocol bound and the signer credential’s remaining validity, allowing an additional safety margin for network delay.
Google Cloud Storage also documents bounded signed-URL expiry, but one provider’s signing and invalidation rules are not interchangeable with another’s. Do not copy ‘7 days’ into a universal capability setting. Inspect the provider’s current documentation and test behaviour under the precise credential type. Official references: Amazon S3 presigned URL guide and Google Cloud Storage signed URLs.
A fully checked expiry calculation
Consider a fictional report gateway at 09:00 Singapore time. Its internal rule allows a maximum of fifteen minutes for this report class. The client asks for ninety minutes. The current signing role is expected to become invalid at 09:12. A three-minute operational margin is required so a recipient has a reasonable chance of completing request setup. The independent limits are ninety minutes requested, fifteen minutes permitted, and twelve minus three equals nine minutes available under the credential constraint. The issuance TTL is min(90, 15, 9) = 9 minutes, so the stated internal deadline is 09:09, not 10:30.
Now assume the recipient opens the link at 09:08:50, a few seconds before 09:09. For an S3 GET the request may be accepted at the start while the transfer finishes after the signature’s expiry, subject to other rules. If the connection breaks and the browser sends a new request at 09:09:20, that fresh request may fail. An agent should not tell the recipient that the service is broken; it should identify a likely expired capability and request a new authorization check. The old URL should never be retried in an unbounded loop.
The calculation is deliberately more conservative than a bare expiration field. It distinguishes requested TTL from permitted TTL and operationally usable TTL. The three-minute margin is a fictional policy choice, not a universal standard. In practice, the application should ask the signing service for its actual credential expiry where obtainable, log time-zone-aware instants, and avoid promising the full interval if the underlying credential’s lifetime is uncertain.
Choosing GET, PUT and object-specific permissions
A GET capability retrieves an object; a PUT capability writes to a particular key or resource. That distinction must be visible in the issuance request and resulting workflow. A recipient given a GET URL cannot safely be assumed to have only ‘view in browser’ permission; the content can be downloaded and retained. A PUT URL may be reusable, and if the same key already exists the upload may replace its contents. Amazon S3 explicitly documents that potential overwrite behaviour. This creates an integrity concern unrelated to whether the recipient is trusted.
For uploads, prefer unique, server-generated object keys in a quarantine location, immutable transaction identifiers and post-upload validation. Avoid authorizing a PUT to a canonical report key such as /final/current.pdf. Require an application state transition before the object is treated as trusted. If content length, MIME type, digest or server-side encryption headers are supposed to be part of the contract, bind them using the provider’s supported mechanisms and verify them after receipt. Do not claim a signed Content-Type proves the bytes are a PDF; MIME labels are easy to spoof.
For downloads, consider a pinned object generation or version identifier when the provider supports it and your consistency needs require it. Signing /report/current.pdf without a stable version can retrieve different content if another process updates the key during the capability lifetime. A snapshot-oriented report delivery service should store the expected object version and digest in its manifest. Version semantics must be tested against the exact provider and URL form, not assumed from generic cloud terminology.
Why a successful HTTP response is not an authorised outcome
A 200 or 206 response answers a transport question, not every business question. Did the intended parent receive the right document? Was it the approved version? Did the application expose another tenant’s key? Did the file contain private details for a second learner? Did a link get copied to an unintended participant? The object store cannot answer all these questions, because some of them concern the application’s authority model and human recipients.
Design a completion receipt with fields for issuance record ID, requester, intended recipient channel, validated object identity, version or digest, HTTP operation, issuance time and the result that the application can actually observe. Be precise about what evidence means. A storage access event may show a retrieval by an IP address or request identifier, but it does not prove a named person read and understood a report. A browser can prefetch, and shared networks can obscure identity. Label ‘retrieved’ as retrieved, not ‘read by parent’.
This same evidence discipline appears in citation anchoring and evidence claims. The observation and the inference should be kept apart. For a sensitive transfer, a named recipient should acknowledge receipt through an authenticated application flow rather than relying solely on object-access logs.
Threat model: the link is a secret in ordinary places
Signed URLs often appear in places that engineers do not initially regard as secret stores: chat histories, task traces, browser history, server access logs, error-report screenshots, third-party analytics dashboards and support tickets. Some clients display the entire query string in crash or performance telemetry. A malicious instruction inside an untrusted document could prompt an agent to paste the link into a different tool, inadvertently extending access to someone else. Protect the query string before it is logged or rendered.
Use short TTLs suited to the transfer, suppress URL query parameters in logs, and consider issuing the URL as a direct response to an authenticated client rather than pasting it into a general conversation. Keep sensitive links out of URL-shortening services and marketing measurement systems. Avoid untrusted redirects that would carry the entire signed URL to another host. Do not rely on generic stripping of the path while retaining the query string: many signing tokens live in the query.
An application can store an opaque issuance identifier and the non-secret object metadata for audit while deliberately never storing the complete reusable URL. When troubleshooting, log a cryptographic hash of a carefully canonicalized identifier if correlation is necessary, or a generated issuance UUID that does not confer access. A full URL should not be placed in a public issue tracker even if its lifetime seems short.
Revocation: what can and cannot be taken back
Short expiry reduces the window of misuse, but is not the same as immediate revocation. If a recipient loses authorization after a URL is issued, the storage layer may still accept the request while its independent rules remain satisfied. Provider-supported revocation options may include invalidating the signing credentials, denying future requests through resource policy changes, or changing object access conditions. Each option has blast-radius and caching implications, and the effect must be tested. Revoking a shared signing credential can break many unrelated transfers.
If immediate per-recipient revocation is a central requirement, put an application-controlled authorization gateway in the request path. The gateway checks the requester on every download, then streams the object or issues a micro-lived storage capability after fresh policy evaluation. This adds latency, costs and failure modes, but it changes the revocation unit from a broad signing credential to an individual business entitlement. For high-risk documents, that can be a worthwhile trade.
One-time download semantics are similarly an application problem. If an agent claims a document was ‘opened once and then destroyed’ while it merely issued a reusable presigned GET link, the claim is false. A gateway can atomically consume a one-time token before streaming, but retries, partial responses and user experience still require careful design. A recipient who completed a legitimate download already has bytes; no access system can make a normal saved copy disappear merely by changing future permission.
Agent-to-tool contract: narrow and machine-checkable
A sensible signing tool takes fields such as authenticated_actor_id, tenant_id, document_id, operation, recipient_context, requested_ttl_seconds and optional validated version. It returns issuance_id, operation, exact approved resource identity, effective_not_after and a link delivered through a protected output channel. Fields that have serious consequences must have bounded enums or validated IDs. Free-form object paths should never be silently accepted as authority.
The server should compute the effective TTL, not trust the model’s arithmetic. It should independently verify that the signing principal can perform the requested storage action; no client-generated signature value should be accepted as proof of entitlement to another object. Limit the signing scope to one environment and use permission separation between uploads and downloads. For PUT, the tool can allocate the quarantine key itself and return it alongside expected headers and a later finalize operation.
The agent’s natural-language confirmation should be derived from the tool’s definitive result. If the signing service returned a nine-minute effective lifetime, the assistant should say nine minutes, not the original ninety requested. If the tool refuses due to a missing parent-authorization record, the agent should not route around it by calling an unrestricted storage interface. Authority must be implemented in code and policy rather than in conversational persuasion.
Failure diagnosis: classify before retrying
403 is an outcome category, not a diagnosis. It can result from expired credentials, elapsed signature age, permission denial, an invalid signature, blocked policy conditions, a changed signed header or an endpoint mismatch. A missing object, wrong version or incorrect region can produce different failure patterns by provider. Do not teach an agent to regenerate links blindly until one works. That can conceal a real authorization failure and create more leaked capabilities.
A safe diagnostic sequence records the relevant issuance ID, expected provider, method, object version, effective expiration and request timestamp. It checks whether the current instant is past the internal deadline; whether the signer credential remains valid; whether the client sent the required headers; and whether the intended object is still the approved resource. If the cause is uncertain, stop at a safe error message and route to a human or a constrained diagnostic tool. Keep raw signed query parameters out of the ticket.
Distinguish a request that never started from one that began and then failed midway. A failed large GET may need a fresh presigned link before resuming, and it may also need an object version check so the resumed bytes match the original. The separate download resumption guide covers range requests and changed-resource hazards; presigning must not obscure those checks.
A practical upload example with distinct identities
Consider a fictional student uploading a 12 MB assignment. The upload service authenticates the learner to course C-41 and issues an operation record U-810. It allocates the opaque quarantine key uploads/C-41/U-810/blob, never a name typed by the learner. It signs a PUT with a ten-minute limit and any required provider-supported headers. The client uploads directly. At this point the object exists, but the official assignment record does not yet point to the uploaded bytes.
The finalize endpoint checks U-810 belongs to the authenticated learner, verifies the blob exists, inspects its length and digest, applies malware and file-format validation where relevant, and then atomically records the accepted version in the course system. If the uploaded 12 MB item is replaced under the same key before finalization, digest or version verification must reveal the mismatch. A strict service may deny additional PUTs by using one-use application flow or conditional storage operations where the provider supports them. The design is safe because acceptance follows independent verification.
One important limit: a file hash establishes byte equality to the expected hash, not that the student’s content is truthful, safe or pedagogically adequate. A content-security scanner can miss threats, and server-reported Content-Type is not a definitive format identification. The storage capability serves transfer; grading, malware analysis and admission decisions remain different operations.
Numerical risk budget for high-volume link issuance
A fictional organisation issues 800 report links per weekday. Suppose 2 percent are accidentally copied into support channels that retain messages longer than intended. That means 800 × 0.02 = 16 potentially exposed links each day, not 16 confirmed breaches. If a link has a 15-minute lifetime and the support channel is viewed only after an average of 60 minutes, many such links may have expired, but one cannot infer zero risk without an actual timestamp distribution. Exposure, attempted use and successful unauthorized access are three different denominators.
Now compare policies in a controlled example. Suppose a measured sample of 100 accidental copies contains 30 that were retrieved within five minutes, 20 between five and fifteen minutes, and 50 later. A five-minute TTL could leave the first 30 inside its time window; a fifteen-minute TTL could leave 50. These are upper bounds on temporal eligibility, not predictions of unauthorized success, because object policy and requester behavior still matter. The useful calculation is 30/100 versus 50/100 time-eligible observations, a twenty-percentage-point difference, not ‘a 40 percent drop in all breaches’.
This arithmetic guides a testable improvement rather than a magic default. Shorten the TTL when legitimate download completion data permit it, measure false expiry failures, and offer safe reissue after renewed authorization. If shortening the lifetime increases support-ticket volume dramatically, consider controlled direct streaming instead. A risk budget should track both confidentiality exposure and the reliability of legitimate access.
Changed-context transfer: partner receives a revised report
Move the same pattern from a parent report to an external reviewer receiving a draft grant assessment. This time the document is revised twice while the reviewer is away. The first presigned GET URL refers to the key assessments/2026/current.pdf, and a newer version is uploaded under the same key before the reviewer starts. The original signing action may still grant GET access to the current key contents, depending on version binding and provider behaviour. A signed URL alone is not an immutable approval receipt.
The repair is to approve a specific object version and include its immutable reference in the issuance manifest and, where supported, the signed request itself. When the document is revised, the application creates a new approval and a new capability; the old approval no longer purports to cover the new bytes. Suppose version A hashes to H-A and version B to H-B. A review receipt for version A must not be accepted against B merely because the filename is identical. The reviewer confirmation can name the approved version ID and digest.
This example changes the primary failure mode from leaked access to meaning drift. The core principle still transfers: specify exactly which action on which bytes is authorized, verify the result, and never confuse a signed transport request with endorsement of later changes. Where a provider cannot bind the version appropriately, place immutable objects under distinct server-generated keys rather than reusing ‘current.pdf’.
Operational monitoring without exposing credentials
Monitor issuance volume, failed signature verification, expired links, unexpected method attempts, duplicate issuance for the same business event, unusual destination channels, and repeated downloads where the use case expects little reuse. Aggregate by issuance ID, actor role, operation, resource class and observed status. Do not add a monitoring system that republishes the entire presigned query string to a dashboard. Your observability layer must preserve confidentiality even during incidents.
Useful alerts should be tied to interpretable questions. A sudden spike in expired links could indicate a clock skew, an unexpectedly short signer session, a delayed email queue or a client retry bug. A spike in 403 responses across one tenant could mean policy changed, not an intrusion. Audit record fields should distinguish issuance from delivery, delivery from fetch, and fetch from human acknowledgment. A dashboard called ‘documents read’ should not count link generation events as reads.
Test denial cases before the service is relied upon: forbidden tenant ID, mismatched operation, stale credential, expired TTL, modified signed header, wrong object version, revoked role and repeated use. Include a legitimate slow transfer that starts before expiry and continues afterward, plus a broken connection that restarts after expiry. These tests make policy assumptions observable rather than rhetorical.
Controls for education and workplace documents
For educational records, classify documents before choosing a transfer mechanism. A public worksheet needs no bearer-secret ceremony. A student progress record, exam accommodation file or personal contact list requires clear purpose, recipient verification and constrained retention. A school or tuition administrator should decide whether temporary direct transfer is appropriate; the agent should not invent legal authority. Provide a way for a recipient to request a new link through the authenticated portal rather than teaching them to share expired tokens.
In workplaces, common high-risk cases include payroll exports, recruitment documents, customer lists and incident evidence. Some files are so sensitive that per-request identity checks and authenticated streaming are preferable to bearer URLs. The correct tradeoff is not ‘presigned URLs are insecure’ versus ‘presigned URLs are safe’. It is whether the capability’s permission, lifetime, sharing properties and audit granularity fit the document’s confidentiality and workflow.
Readers interested in education as a life-long transfer problem can explore learning that transfers into real decisions. The transferable lesson here is to distinguish a technical transport permission from the human authority behind it. For related system controls, the How Superintelligence Works hub connects narrower mechanisms; no claim is made that artificial superintelligence currently exists.
Source-grounded implementation checklist
Use the official signing implementation for the actual object-store provider. Confirm which method, path, headers, query fields and expiration unit are signed. Confirm whether a role session can expire sooner than the URL and whether storage policies constrain signature age. Treat every signed URL as a secret and record only non-sensitive issuance metadata. Require a server-side authorization check for the exact actor and object before signing, and a post-transfer verification step before promoting uploaded bytes.
Choose a maximum TTL from task evidence, not from a provider’s protocol ceiling. For recurring work, build a small test corpus of successful and failed transfers across timeouts, changed versions, reused links and revoked rights. Preserve an explanation of who may issue capabilities and who approves policy exceptions. When policy and technical behaviour disagree, repair the product contract rather than changing the explanation to hide the problem.
Primary references verified for this guide: AWS: download and upload with presigned URLs; Google Cloud: signed URLs; Google Cloud: canonical requests. These providers describe their own systems; always verify the current documentation for the service and credential mode actually deployed.
Frequently asked questions
Does a presigned link mean the storage bucket is public? No. A private bucket can serve a narrowly signed request without making all its objects public. That does not make the bearer link itself private after distribution. Does expiry revoke a copy that has already been downloaded? No. Expiry normally governs future HTTP request authorization, not bytes the recipient saved. Can I issue a signed URL to everyone in a class? Only if the material is appropriate for all recipients and the risk of forwarding fits the policy; for private student records, that would usually be an inappropriate shortcut.
Why does a link fail before the requested expiry? The underlying signing credential may expire, permission may change, a restrictive bucket policy may apply, or a signed request component may differ. Does an upload URL guarantee a clean file? No; transfer permission is separate from verification, scanning and final admission. Can I make an S3 presigned link truly one-time by setting a one-time flag? Not by assumption: repeated requests are possible unless an additional enforceable control prevents them.
Should an AI agent construct signatures itself? Generally no. Give it a constrained tool backed by official SDKs and independent authorization. Should all downloads use a gateway? Not necessarily. Direct storage transfers are efficient and useful when temporary bearer access fits the use case. The choice depends on revocation needs, identity assurance, document sensitivity, scale and failure-recovery requirements. The smallest sound design is the one whose guarantees can be demonstrated, not merely advertised.
Reader pathways and final decision rule
If you are a developer, begin by mapping the authorization boundary, exact signed request shape and effective expiry. If you operate a data-sensitive service, begin with recipient identity, logging exposure and revocation requirements. If you are an educator or parent, ask whether a link may be forwarded and whether the file contains information about anyone else. If you are debugging a failed transfer, compare the effective deadline and signed headers before generating a replacement token.
The final rule is simple but demanding: create a capability only for an already-authorized operation; preserve its exact meaning; keep it confidential; constrain its lifetime; and independently verify what happened. An agent is helpful when it coordinates these steps and describes their evidence accurately. It becomes hazardous when it mistakes possession for entitlement, requested expiry for guaranteed expiry, or a successful HTTP response for a successful human handover.