Topics
Recent articles

DevOps & Cloud

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
A public receipt request passes through token verification before returning a deliberately limited set of fields.
A public receipt URL is a bearer capability, so the response should reveal only the fields the page needs.

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.

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

Continue Exploring

You Might Also Like

View all articles
Half-Open Circuit Breaker Probes in Serverless Workers
3 min read

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.