← Back to list

Usage of DPOP: When extractable is set to true Breaks Your Authentication Model

A Deep Dive into WebCrypto Key Security

Varsha · 2026-02-18 05:56 · 1 claps · 2.4 min read
#dpop #access-token #private-key
Open on Medium ↗
Wiki topics: FT · Fine-tuning & Adaptation CRY · Crypto & Web3

Usage of DPOP: When extractable is set to true Breaks Your Authentication Model

A Deep Dive into WebCrypto Key Security

Modern web applications increasingly rely on the browser’s native cryptography via the Web Cryptography API (commonly called WebCrypto). It allows applications to generate and use asymmetric keys directly in the browser — often for authentication, digital signatures, and identity.

But there’s a subtle configuration choice that can completely undermine the security model:

extractable: true

This single boolean can turn a protected private key into exportable plaintext key material — permanently breaking your authentication guarantees.

Understanding the WebCrypto Trust Model

WebCrypto was designed with a clear security boundary:

The browser is the cryptographic boundary.

Private keys generated inside the browser are meant to remain protected inside the browser’s internal key store. Applications can use the key (e.g., sign data), but they should not be able to read the raw private key bytes.

That protection exists only when keys are created with:

extractable: false

If extractable is set to true, the key can be exported in formats like JWK or PKCS#8 — meaning the raw private key leaves the secure boundary. The Critical Difference

Secure configuration (recommended for authentication)

crypto.subtle.generateKey(
  { name: "ECDSA", namedCurve: "P-256" },
  false,          // NOT extractable
  ["sign"]
);

Insecure configuration (common mistake)

crypto.subtle.generateKey(
  { name: "ECDSA", namedCurve: "P-256" },
  true,           // Extractable
  ["sign"]
);

That true means:

  1. The private key can be exported.
  2. The private key can be serialized to JSON (JWK).
  3. The private key can be copied, stored, or exfiltrated.
  4. The private key can be reused indefinitely.

At that point, the browser is no longer protecting anything.

Why This Is Dangerous for Authentication

When WebCrypto keys are used for:

  • User identity
  • Client authentication
  • Digital signatures
  • WebAuthn-like flows
  • API request signing

…the private key is supposed to remain non-exportable.

If it is extractable, then:

  1. Any JavaScript running in the page can export it.
  2. A single XSS vulnerability can permanently steal it.
  3. Browser debugging or instrumentation can retrieve it.
  4. The key can be copied and used outside the browser.

This leads to permanent user impersonation.

Unlike passwords, private keys:

  • Often have no expiration
  • May not rotate automatically
  • May not be revocable without complex infrastructure changes

Demonstration: Extracting the Private Key (d)

If a key is generated as extractable:

const keyPair = await crypto.subtle.generateKey(
  { name: "ECDSA", namedCurve: "P-256" },
  true, // <-- critical mistake
  ["sign", "verify"]
);

The private key can be exported:

const jwk = await crypto.subtle.exportKey("jwk", keyPair.privateKey);

console.log("Private key scalar (d):", jwk.d);

or

const realExportKey = crypto.subtle.exportKey;

crypto.subtle.exportKey = async function(format, key) {
  console.log("Format:", format);
  console.log("Key:", key);

  const exported = await realExportKey.apply(this, arguments);

  // If this is a private key, it will be a JWK containing "d"
  console.log("Exported material:", exported);
  return exported;
};

Output will look like:

Private key scalar (d): 3p1w05_YN94lX_NQfTEvcRHpy1F7OBs6e3ecjMXXXXX

How a DPoP Proof Is Generated Using a Private Key

DPoP (Demonstration of Proof-of-Possession) works by proving that the client holds the private key corresponding to a public key. If someone has access to the private key (d), they can generate valid DPoP proofs.

DPOP Architecture

DPOP Architecture

Conceptual Flow

  1. Create a JWT header including:
  • typ: "dpop+jwt"
  • alg: "ES256"
  • The public key (jwk) — containing kty, crv, x, y (not d)

2. Create a JWT payload including:

  • htu → HTTP URI
  • htm → HTTP method
  • iat → issued-at timestamp
  • jti → unique token ID

3. Sign the JWT using the private key (d).

The signature proves possession of the private key.


메타데이터
post_id
9353f91e55b8
slug
uasage-of-dpop-when-extractable-is-set-to-true-breaks-your-authentication-model-9353f91e55b8
url
https://medium.com/@varshaseetharam13/uasage-of-dpop-when-extractable-is-set-to-true-breaks-your-authentication-model-9353f91e55b8
canonical_url
https://medium.com/@varshaseetharam13/uasage-of-dpop-when-extractable-is-set-to-true-breaks-your-authentication-model-9353f91e55b8
author_url
https://medium.com/@varshaseetharam13
status
ok
fetched_at
2026-06-22 07:15:07