Files
suaveplan/packages/security-stack/security/jws/src/algorithms/none.md
2026-05-27 23:47:53 +01:00

2.6 KiB

none algorithm

Purpose

RFC 7518 §3.6 "none" algorithm. The none value declares "no integrity protection," which is virtually never what a verifier wants but is occasionally needed for non-security-critical contexts (signed-by-policy local development, deliberately-unsigned debugging payloads). This implementation exists only so the registry is complete; the verify pipeline rejects none by default and requires BOTH algorithms: ["none"] AND acceptNone: true to opt in, neutralising the classic "alg: none" downgrade attack.

Features

  • verify returns true if-and-only-if the signature segment is empty (zero bytes after base64url-decode).
  • sign always emits empty signature bytes.
  • Default rejection: pipeline throws JWS.ALG_NONE_DISABLED unless both flags are set.

Usage: Basic Example

import { compactSign, compactVerify } from "@suaveplan/jws";

const token = await compactSign("hello", undefined, "none");
// token has the form "<header>.<payload>." — empty signature segment.

await compactVerify(token, undefined, {
    algorithms: ["none"],
    acceptNone: true,
});

Usage: Advanced Example

import { compactVerify, JwsError } from "@suaveplan/jws";

const noneToken = "eyJhbGciOiJub25lIn0.eyJzdWIiOjF9.";

// Default: rejected.
try {
    await compactVerify(noneToken, undefined, { algorithms: ["HS256"] });
} catch (err) {
    if (err instanceof JwsError && err.code === "JWS.ALG_NONE_DISABLED") {
        // CVE-class defence in depth.
    }
}

// Allowlisted but no acceptNone — also rejected.
try {
    await compactVerify(noneToken, undefined, { algorithms: ["none"] });
} catch (err) {
    if (err instanceof JwsError && err.code === "JWS.ALG_NONE_DISABLED") {
        // Two flags required.
    }
}

API Reference

  • none: AlgorithmImpl<undefined> — verify (input, sig) returns Promise<boolean> (true iff sig.length === 0); sign (input, _) returns Promise<Uint8Array> of length 0.

Implementation Notes

The two-flag opt-in design is a deliberate defence against the historic JWT-CVE class where a caller passes algorithms: ["HS256"] AND a public key, then an attacker mints a token with alg: "none" and the library accepts it because the allowlist is parsed permissively. Here, even a deliberate algorithms: ["none"] requires the second flag acceptNone: true — the second flag is intentionally separate so a single typo or merged configuration cannot accidentally accept unsigned tokens. The implementation also requires the signature segment to be empty, not "not present" — a token like "<hdr>.<payload>.deadbeef" with alg: "none" is rejected because the signature bytes are non-zero.