Secure Public Receipt Link with Access Token
Learn how to build a privacy-safe public receipt link using high-entropy bearer tokens, SHA-256 hashing at rest, constant-time verification, no-store caching, and an explicit allowlisted response DTO.
Table of Contents8 sections

Sharing a customer receipt via an unauthenticated public link risks exposing internal database models or unrelated order history unless you enforce strict boundary controls, as outlined in the OWASP API Security Top 10 guidelines.
Quick solution: Use a cryptographically secure random token, store only its SHA-256 hash in the database, compare incoming tokens in constant time, and serialize a dedicated public DTO.
public class ReceiptSecurity {
public static boolean verifyToken(byte[] storedHash, byte[] suppliedToken) {
byte[] computedHash = sha256(suppliedToken);
return MessageDigest.isEqual(storedHash, computedHash);
}
}
Prerequisites require a secure storage backend capable of persisting hash digests and a routing framework that can attach explicit cache-control response headers.
Threat Model for Shareable Receipts
When an application generates a URL for a customer to view a receipt outside of an authenticated session, that URL acts as a bearer capability. Anyone who obtains the link can read the contents of the receipt. If the endpoint serializes the internal database model directly, it may leak customer records, internal identifiers, or administrative flags that have no business being on a payment confirmation page.
Treating the URL token as a capability means separating the identifier of the receipt from the secret token required to view it. The server must never store the raw token in plaintext. If the database is compromised, a plaintext token would allow immediate unauthorized access to every associated receipt.
Choosing an Implementation Pattern for Public Receipts
| Option | When to Use It | Trade-off or Failure Mode | Recommendation |
|---|---|---|---|
| Raw Database ID in URL | Internal intranets or fully authenticated sessions only. | Leaks sequential records and invites IDOR exploits. | Never use for public receipt links. |
| Plaintext Token in DB | Prototyping or short-lived staging environments. | Token leaks if database is read or compromised. | Avoid in production systems. |
| Hashed Token with DTO | Public customer receipt links and payment confirmations. | Requires hash computation on every request lookup. | Recommended standard pattern. |
Generating and Storing Scoped Tokens
The implementation generates 32 random bytes from a secure random source, encoding them as a URL-safe and unpadded string for the user. On the backend, only the SHA-256 hash of this token is stored alongside the order record.
public String generateReceiptToken() {
byte[] randomBytes = new byte[32];
SecureRandom.getInstanceStrong().nextBytes(randomBytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes);
}
Storing only the digest protects against token theft via database read access. When a user requests the receipt URL, the application extracts the raw token from the request query parameter or path, computes its SHA-256 digest, and queries the database for a matching hash.
Verifying Without Leaking Enumeration Details
Attackers often probe endpoints by cycling through identifiers to see which ones exist. A secure public receipt endpoint must prevent enumeration by returning identical responses for missing orders and invalid tokens.
- Compute the hash of the incoming token.
- Perform a constant-time comparison of the byte arrays to prevent timing attacks.
- Return a generic not-found status code whether the token hash does not match or the order ID does not exist.
Minimizing and Masking Response Fields
Serializing a raw database entity exposes internal fields. A public receipt endpoint should map internal data into a dedicated response DTO that contains only necessary fields such as the order date, due date, package name, price, payment status, and a limited theme profile.
Customer phone numbers or internal user identifiers should be masked or omitted entirely. If a phone number must appear for recognition purposes, obscure most of the digits so it cannot be abused if the link is shared improperly.
public class PublicReceiptResponse {
private String orderDate;
private String dueDate;
private String packageName;
private double price;
private String paymentStatus;
private String maskedPhone;
}
Managing Caching, Expiry, and Abuse Limits
Publicly accessible URLs are prone to being cached by intermediate proxies, content delivery networks, or browser history. The endpoint must explicitly disable caching for sensitive views in accordance with RFC 9111 HTTP Caching standards.
Cache-Control: no-store
While Cache-Control: no-store prevents compliant caches from retaining the payload, it is not a complete privacy guarantee on its own. The implementation should also consider hardening measures such as token expiration policies, rate limiting on the verification route, and manual revocation paths for users who need to invalidate an old link. For a separate OAuth integration, revoking a receipt token and revoking an OAuth grant are different operations; uninstalling a connector does not revoke OAuth access.
Verification Checklist
- Token material is generated from 32 random bytes using a secure random source.
- Only a SHA-256 digest is stored and compared using constant-time evaluation.
- Bad tokens and missing orders return the identical not-found response to prevent enumeration.
- The route explicitly sets the Cache-Control header to no-store.
- Response data is built entirely through a dedicated public DTO with masked sensitive fields.
Continue Exploring
You Might Also Like

Gating Cloudflare Worker Deployments With CI
Learn how to separate CI validation from Cloudflare Worker deployments by enforcing branch protection, required status checks, and quality gates.

Fix Cloudflare Workers Fetch Illegal Invocation
Learn how to resolve illegal invocation errors caused by detached fetch calls losing their receiver context inside the Cloudflare Workers runtime.

Half-Open Circuit Breaker Probes in Serverless Workers
Learn how to prevent thundering-herd recovery in serverless workers by coordinating half-open circuit breaker probes through a SQLite-backed Durable Object instead of eventually consistent key-value stores.