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
Published September 18, 2026Reviewed 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

critical

How Hardcoded Secrets Compromise Authentication in JavaScript and How to Fix It

A critical vulnerability in `Tool/QuantumultX/Rewrite/RRSP.js` exposed hardcoded API authentication credentials—a TOKEN and UMID device identifier—directly in source code. Anyone with repository access could extract these credentials to impersonate the legitimate user and gain full account access to the RRTV API service. The fix replaced hardcoded secrets with empty placeholders, forcing users to manually configure credentials through secure channels.

critical

How origin validation bypass happens in Express.js and how to fix it

A `POST /changeData` route in `src/main/server/routes/index.js` guarded state-changing writes with an origin allowlist, but the guard was wrapped in an `if (origin && ...)` truthiness check. Any request that simply omitted both `Origin` and `Referer` — a one-line `curl` command, a local script, a background process — skipped validation entirely and modified application data. The fix removes the truthiness short-circuit so a *missing* header is now treated as a rejection, not a pass.

critical

How CSRF vulnerabilities happen in Node.js API clients and how to fix them

A critical CSRF vulnerability in `lib/client.js` allowed attackers to forge authenticated POST requests to `/remote-ssh/api/*` endpoints. The fix adds the `X-Requested-With: XMLHttpRequest` header to enable proper CSRF token validation, blocking malicious cross-site requests with a minimal one-line change.

critical

How User Enumeration Happens in Django Forms and How to Fix It

A critical user enumeration vulnerability in the volunteers application allowed attackers to systematically discover registered email addresses through distinct error messages in signup and password reset forms. The fix replaces specific error messages with generic ones, preventing information disclosure while maintaining application functionality.

critical

How Authentication Bypass Happens in Node.js WebSocket Services and How to Fix It

The HousePanel push notification service exposed GET and POST endpoints without any authentication checks, allowing unauthenticated attackers to send arbitrary push notifications to connected smart devices. This critical vulnerability was fixed by implementing mandatory token validation on all protected endpoints, ensuring only authenticated requests can trigger push operations.

critical

{sample} Placeholder in shlex.split() Lets Filenames Inject Args

A protocol replay-check CLI built its subprocess argument list by calling `str.format()` on a user-supplied `--command` template and then handing the result to `shlex.split()`, so a sample filename containing spaces, quotes, or shell metacharacters could split into extra argv entries — or execute as shell code when the template wrapped the placeholder in `sh -c`. The fix wraps the interpolated path in `shlex.quote()` before formatting, so the path always survives `shlex.split()` as a single toke