DEV Community

StarkMan
StarkMan

Posted on

DPoP makes a token sender-constrained, which changes what a stolen token is worth

DPoP makes a token sender-constrained, which changes what a stolen token is worth

Every bearer token in an OAuth deployment carries the same implicit promise: whoever holds the string can use it. That promise is what makes a leaked access token useful to an attacker and awkward for a defender. Revoking a token works, but revocation is a reaction. The better question is whether a token can be made useless to anyone who merely copies it. Demonstrating Proof of Possession, standardised as RFC 9449, is the practical answer that has reached general adoption.

Bearer semantics are the root cause

A bearer token is a credential in the same sense a session cookie is. If it appears in a log, a browser extension, a misconfigured proxy cache, a crash dump, or a support ticket, it is usable until it expires or is revoked. The OAuth 2.0 security best current practice, RFC 9700, states the consequence plainly: possession is sufficient, so the token must be protected in transit and at rest. That advice does not change the underlying property.

What DPoP adds

DPoP binds the token to a private key held by the client. The client generates an asymmetric key pair and sends the public key thumbprint in a DPoP header on the token request. The authorisation server records that thumbprint in the access token's cnf claim. On each later resource request, the client signs a small proof JWT with the private key, covering the HTTP method, the request URL, a timestamp, and a server-provided nonce. The resource server verifies the signature and compares the token's confirmation claim with the key used in the proof.
The consequence is narrow and precise: an attacker who copies the access token but not the private key cannot construct a valid proof. The token stops being a bearer credential and becomes a holder-of-key credential.

Where implementations go wrong

Sender-constrained tokens move complexity to the verifier. Four failure modes recur.
Partial enforcement is the first. A resource server that validates the proof header on some routes and not others has enforced nothing, because the unguarded route still accepts the raw token. Enforcement has to key off the token type rather than off individual handlers.
Nonce handling is the second. DPoP uses a server-provided nonce to limit replay. A verifier that skips the nonce check, or accepts arbitrarily old iat values, weakens replay resistance. Clock skew tolerance is necessary; unlimited tolerance is not.
Client-side key storage is the third. If the client writes the private key somewhere an attacker who can read the token can also read, such as the same process memory, profile directory, or backup, the binding collapses to the weaker of the two secrets.
Audience confusion is the fourth. A proof generated for one resource server should not be replayable at another, and the token's audience claim has to line up with the proof's htu and htm values.

Operational consequences

The defensive value concentrates in the scenarios that dominate incident reports. Token theft from logs, from a compromised front end, from an over-broad proxy cache, and from a third-party analytics integration all become far less useful. The attacker still holds the token and can still present it, but the request is rejected because the proof cannot be produced.
This does not remove the need for short lifetimes, revocation, audience restriction, and scope minimisation. It changes the marginal cost of a copied string. Treat DPoP as one layer in the token-hardening sequence, not a replacement for the rest.

Next steps

Inventory where access tokens are currently stored and logged before adopting DPoP, because the binding only helps if the private key is protected at least as well as the token was. Then pick one resource server, enable proof validation, and verify with a deliberately replayed request that the unsigned replay is rejected.

References

  • RFC 9449, OAuth 2.0 Demonstrating Proof of Possession (DPoP)
  • RFC 9700, Best Current Practice for OAuth 2.0 Security
  • RFC 7515, JSON Web Signature

Top comments (0)