Back to Blog
critical SEVERITY4 min read

JWT Authentication Disabled Signature Validation in

A critical misconfiguration in JWT authentication explicitly disabled signature validation, allowing attackers to forge valid tokens with arbitrary claims and bypass authentication entirely. The fix re-enables signature validation on all incoming bearer tokens, restoring the security boundary of the authentication layer.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published September 18, 2026•Reviewed September 18, 2026

Answer Summary

The JWT authentication configuration in the authentication setup code had signature validation explicitly disabled via `ValidateIssuerSigningKey = false`. An attacker could forge JWT tokens with any signing key and inject arbitrary claims (such as admin roles) to impersonate any user and bypass authentication on publicly accessible API endpoints. The fix changes `ValidateIssuerSigningKey` to `true`, forcing the authentication handler to validate that tokens are signed with the server's key. CWE-347 (Improper Validation of Certificate with Host Mismatch) tracks certificate validation bypass; this is the cryptographic key validation equivalent.

Vulnerability at a Glance

cweCWE-347 (Improper Validation of Cryptographic Signature)
fixChange ValidateIssuerSigningKey from false to true
riskComplete authentication bypass on publicly accessible endpoints
languageC#
root causeValidateIssuerSigningKey explicitly set to false in JwtBearerOptions
vulnerabilityJWT Signature Validation Disabled

A Critical Flaw in JWT Authentication Configuration

A critical misconfiguration in the JWT authentication setup allowed the most fundamental security property of bearer tokens—their signature—to be completely bypassed. By explicitly disabling cryptographic signature validation, the code accepted forged tokens from any source, turning a multi-step authentication chain into a no-op.

This is the kind of vulnerability that makes security teams lose sleep: it's not a complex attack requiring multiple steps, it's not a race condition or a parsing edge case—it's a single configuration line that inverts the entire security model of the system.

Affected Versions

Affected not applicable (first-party code)
Fixed in not applicable (first-party code)
Ecosystem N/A
CVE / GHSA not assigned
CWE CWE-347

The Vulnerability Explained

The JWT authentication handler in the infrastructure layer was configured with an explicit security flaw:

var tokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = true,
    ValidateAudience = true,
    ValidateLifetime = true,
    ValidateIssuerSigningKey = false  // ← CRITICAL: signature validation disabled
};

This configuration tells the authentication middleware: "Check that the token was issued by a trusted issuer, check the audience claim, and check that it hasn't expired—but do not check the signature."

Here's why that's catastrophic.

A JWT token has three parts: a header, a payload (claims), and a signature. The signature proves that the payload hasn't been tampered with and was created by someone who knows the server's secret key. When ValidateIssuerSigningKey is false, the handler skips the signature check entirely.

An attacker exploits this by:

  1. Creating a forged payload with arbitrary claims:
    json { "sub": "admin", "role": "admin", "iss": "valid-issuer", "aud": "valid-audience", "exp": 2000000000 }

  2. Signing it with any key (even one they control) or no key at all:
    javascript const forgedToken = jwt.sign( { sub: "admin", role: "admin" }, "attacker-secret", // Not the server's key { algorithm: "HS256" } );

  3. Sending it as a bearer token:
    bash curl -H "Authorization: Bearer <forgedToken>" \ http://localhost:3000/api/protected

  4. Bypassing authentication because the handler validates the claims (issuer, audience, expiration) but never checks the signature.

The other validation checks—ValidateIssuer, ValidateAudience, ValidateLifetime—become security theater. An attacker simply includes valid issuer and audience strings and sets an expiration date in the future. All checks pass. The token is accepted as legitimate.

For a publicly accessible API endpoint, this means anyone can create a token claiming to be any user, including administrators, and gain full access to protected resources.

The Fix

The fix is surgical: change one boolean value:

var tokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = true,
    ValidateAudience = true,
    ValidateLifetime = true,
    ValidateIssuerSigningKey = true  // ← Fixed: signature validation enabled
};

By setting ValidateIssuerSigningKey = true, the authentication handler now requires that:
- The token's signature is cryptographically valid
- The signature was created with the server's signing key (stored securely and never shared)
- Any modification to the token's claims would invalidate the signature

This restores the actual security property of JWT tokens: a forged token will have an invalid signature and be rejected immediately, regardless of what claims it contains.

The regression test in the PR explicitly covers the attack scenarios:
- A token signed with an attacker-controlled secret is rejected
- A token using the "none" algorithm (a known bypass technique) is rejected
- A token with valid structure but wrong key is rejected

All three now fail with HTTP 401, as intended.

Key Takeaways

  • Never set ValidateIssuerSigningKey = false in production JWT configurations. The issuer, audience, and expiration checks are worthless without signature validation. Signature validation is the root of JWT security, not an optional layer.

  • Signature validation is orthogonal to other JWT checks. Validating claims like iss and exp does not validate the signature. An attacker can forge tokens with valid claims and any signature.

  • Bearer token security is binary. Either the token is cryptographically signed by the server and verified on every request, or any attacker can create valid tokens. There is no middle ground or "good enough" partial validation.

  • Configuration mistakes in authentication are the highest-impact vulnerabilities. A single boolean can disable an entire security mechanism. Code review, static analysis, and automated security scanning are essential for authentication infrastructure.

How Orbis AppSec Detected This

  • Source: Bearer tokens arrive in the HTTP Authorization header and reach the JWT authentication handler's parameter options.TokenValidationParameters
  • Sink: The ValidateIssuerSigningKey property in the token validation configuration, which controls whether SecurityTokenHandler.ValidateToken() checks the signature
  • Missing control: No enforcement that ValidateIssuerSigningKey must be set to true; the configuration was set to false, completely disabling signature validation
  • CWE: CWE-347 (Improper Validation of Cryptographic Signature)
  • Fix: Changed ValidateIssuerSigningKey from false to true, forcing signature validation on all bearer tokens

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

Authentication misconfigurations are some of the most dangerous vulnerabilities because they silently invert security guarantees. Setting ValidateIssuerSigningKey = false explicitly disables the cryptographic validation that makes JWT tokens trustworthy. This fix restores that validation, ensuring that only tokens signed by the server are accepted and forged tokens are rejected outright. For any API handling sensitive operations or user data, this change is essential.

Prevention and further reading

Frequently Asked Questions

Why is disabling ValidateIssuerSigningKey dangerous if other JWT validation is enabled?

ValidateIssuer, ValidateAudience, and ValidateLifetime check the *claims* inside the token, but not the token's *signature*. With signature validation disabled, an attacker can create a token with valid claims but sign it with any key—the token will pass validation because the signature is never checked against the server's key.

Can an attacker forge a token without knowing the server's secret key?

Yes—that's the entire point of the vulnerability. The attacker simply signs the forged token with *any* key (even one they control), and since signature validation is disabled, the authentication layer accepts it without verifying the signature matches the server's key.

If ValidateLifetime is true, won't expired tokens still be rejected?

Correct—expired tokens will be rejected. However, an attacker can forge a token with a future expiration date, so this provides no real protection. All other JWT validation becomes meaningless once signature validation is disabled.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #258

Related Articles

high

undici 8.10.0 Cache Poisoning: CVE-2026-85152 Auth Bypass

undici, the HTTP client used by Node.js's `fetch()` implementation, shipped a caching layer that did not properly isolate cached responses by origin, letting a response poisoned on one origin be served to requests for another. This created a path to cross-origin authentication bypass, tracked as CVE-2026-85152 and fixed in undici 8.10.2.

critical

Hybridauth Telegram OAuth Timing Attack in `authenticateCheckError()`

The Telegram OAuth provider in Hybridauth relied on `strcmp()` to validate HMAC-SHA256 signatures, exposing a timing side-channel that could allow attackers to forge authentication tokens character by character. The fix replaces this with PHP's timing-safe `hash_equals()` function, eliminating the information leak.

critical

Node.js Auth Query SQL Injection via Group Code Interpolation

A sign-in authorization check built its SQL query by mapping an `authorizedGroups` array into quoted string literals and joining them directly into a template literal, creating a classic SQL injection point in a critical authentication path. The fix replaces every interpolated value — including the previously "typed" parameter — with `?` placeholders bound through a value-builder helper, closing off the injection vector entirely.

high

PHP session_start() Missing Secure Cookie Flags in lasturl.php

The `session_start()` call in the last URL tracking component failed to set secure session cookie parameters, allowing session hijacking via man-in-the-middle attacks on HTTP connections or XSS exploitation. The fix configures `session_set_cookie_params()` with `httponly`, `secure`, and `samesite` attributes before starting the session.

critical

Tung Tung Tracker Admin API: Missing Authentication in `/api/sync`

The Tung Tung Tracker administrative API endpoints (`/api/sync`, `/api/backfill`, `/api/show`, `/api/overlay/hide`) relied solely on localhost origin and a static `X-Tracker` header for protection, allowing any local process to execute privileged operations. The fix introduces proper session-based authentication and a random IPC secret for desktop-to-server communication.

critical

POST /api/generate Lacks Authentication, Allowing Unauthenticated

A resume generation endpoint in a Node.js backend accepted requests from any caller with network access, allowing attackers to consume OpenAI API quota without restriction. The vulnerability stemmed from missing authentication middleware on a cost-bearing endpoint. The fix adds mandatory API key validation via HTTP headers before processing any generation requests.