CRITICAL missing tlsjwt tokens

Missing Tls with Jwt Tokens

How Missing TLS Manifests in JWT Tokens

Missing TLS in JWT token implementations creates several critical attack vectors that directly compromise authentication and authorization systems. The most common manifestation occurs when JWTs are transmitted over HTTP instead of HTTPS, allowing attackers to intercept tokens using network-level attacks like ARP spoofing or man-in-the-middle (MITM) techniques.

Consider a typical JWT authentication flow:

// Vulnerable: JWT transmitted over HTTP
const login = async (username, password) => {
  const response = await fetch('http://api.example.com/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username, password })
  });
  const { token } = await response.json();
  localStorage.setItem('jwt', token); // Token stored insecurely
  return token;
};

When this token is sent over HTTP, an attacker on the same network can capture it using tools like Wireshark or tcpdump. The captured JWT can then be used for session hijacking, granting the attacker full access to the victim's authenticated session.

Another JWT-specific manifestation occurs during token refresh operations. Many applications implement refresh token endpoints that accept HTTP requests:

// Vulnerable refresh endpoint
app.post('/refresh', (req, res) => {
  const refreshToken = req.body.refreshToken;
  // No validation of transport security
  const user = validateRefreshToken(refreshToken);
  if (user) {
    const newToken = generateJWT(user);
    res.json({ token: newToken });
  }
});

Even if the initial authentication uses HTTPS, a refresh endpoint accessible over HTTP creates a backdoor for token theft. Attackers can continuously refresh stolen tokens without ever needing the original credentials.

JWTs are also vulnerable during storage and transmission between microservices. When services communicate over unencrypted channels, tokens can be intercepted mid-flight:

// Vulnerable: service-to-service communication over HTTP
func validateToken(tokenString string) (*User, error) {
  // Token transmitted over HTTP to auth service
  resp, err := http.Post("http://auth-service/validate", 
    "application/json", bytes.NewBuffer([]byte(`{"token":""+tokenString+""}`)))
  if err != nil {
    return nil, err
  }
  defer resp.Body.Close()
  // Process response...
}

This pattern is particularly dangerous in microservices architectures where multiple services handle JWTs across internal networks that may not be properly segmented or secured.

JWT-Specific Detection

Detecting missing TLS in JWT implementations requires both automated scanning and manual code review. middleBrick's black-box scanning approach identifies TLS vulnerabilities by attempting to access JWT endpoints over HTTP and analyzing the responses.

middleBrick specifically tests JWT endpoints by:

  1. Identifying JWT-related endpoints through OpenAPI spec analysis or runtime discovery
  2. Attempting HTTP connections to authentication, token refresh, and validation endpoints
  3. Checking for successful responses when TLS is absent
  4. Analyzing JWT payloads for sensitive information exposure
  5. Testing for token reuse across different transport security contexts

Manual detection should focus on these JWT-specific patterns:

# Check for HTTP JWT endpoints
curl -I http://api.example.com/auth/login
curl -I http://api.example.com/refresh
curl -I http://api.example.com/validate

# Test for token transmission over insecure channels
# Look for Authorization headers in HTTP responses
curl -v http://api.example.com/protected 2>&1 | grep -i authorization

# Check for mixed content in JWT implementations
# Search code for HTTP URLs in authentication contexts
grep -r "http://" --include="*.js" --include="*.ts" --include="*.py" --include="*.go" auth/
grep -r "http://" --include="*.java" --include="*.kt" controllers/

Code review should examine JWT libraries and their configuration:

# Vulnerable: jsonwebtoken without secure defaults
import jwt

def create_token(payload):
    return jwt.encode(payload, 'secret-key', algorithm='HS256')
    # Missing: algorithm validation, secure key storage

# Vulnerable: no TLS enforcement in HTTP clients
def call_api(token):
    headers = {'Authorization': f'Bearer {token}'}
    response = requests.get('http://api.example.com/data', headers=headers)
    # Missing: verify=True, HTTPS enforcement

middleBrick's scanning also identifies JWT-specific issues like:

  • Tokens transmitted in query parameters over HTTP
  • Missing Secure/HttpOnly flags on JWT cookies
  • Weak JWT signing algorithms exposed over insecure channels
  • Token replay attacks across different transport security contexts

JWT-Specific Remediation

Remediating missing TLS in JWT implementations requires both transport security enforcement and JWT-specific security hardening. The foundation is implementing HTTPS everywhere JWTs are transmitted.

Node.js/Express JWT implementation with TLS enforcement:

// Secure JWT implementation with TLS enforcement
const express = require('express');
const jwt = require('jsonwebtoken');
const helmet = require('helmet');
const app = express();

// Enforce HTTPS using helmet
app.use(helmet());
app.use(helmet.hsts({
  maxAge: 31536000,
  includeSubDomains: true,
  preload: true
}));

// JWT authentication middleware
const authenticateJWT = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ message: 'Missing token' });
  }
  
  const token = authHeader.substring(7);
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET, {
      algorithms: ['HS256'] // Algorithm whitelisting
    });
    req.user = decoded;
    next();
  } catch (err) {
    return res.status(401).json({ message: 'Invalid token' });
  }
};

// Secure login endpoint (HTTPS only)
app.post('/auth/login', express.json(), (req, res) => {
  const { username, password } = req.body;
  
  // Authenticate user
  const user = authenticateUser(username, password);
  if (!user) {
    return res.status(401).json({ message: 'Invalid credentials' });
  }
  
  // Create JWT with secure options
  const token = jwt.sign(
    { userId: user.id, username: user.username },
    process.env.JWT_SECRET,
    { 
      expiresIn: '1h',
      issuer: 'your-domain.com',
      audience: 'your-app'
    }
  );
  
  // Set secure cookie for JWT
  res.cookie('jwt', token, {
    httpOnly: true,
    secure: true, // Only over HTTPS
    sameSite: 'strict',
    maxAge: 3600000
  });
  
  res.json({ token, user: { id: user.id, username: user.username } });
});

// Refresh endpoint with TLS enforcement
app.post('/auth/refresh', express.json(), (req, res) => {
  // Verify TLS connection
  if (!req.secure) {
    return res.status(400).json({ 
      message: 'Refresh endpoint requires HTTPS' 
    });
  }
  
  const { refreshToken } = req.body;
  
  try {
    const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET, {
      algorithms: ['HS256']
    });
    
    // Create new access token
    const newToken = jwt.sign(
      { userId: decoded.userId },
      process.env.JWT_SECRET,
      { expiresIn: '1h' }
    );
    
    res.json({ token: newToken });
  } catch (err) {
    res.status(401).json({ message: 'Invalid refresh token' });
  }
});

app.listen(443, () => {
  console.log('Server running on HTTPS port 443');
});

Python/FastAPI implementation with comprehensive TLS and JWT security:

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
from jose import JWTError, jwt
from datetime import datetime, timedelta
import os

app = FastAPI()

# Enforce HTTPS redirect
app.add_middleware(HTTPSRedirectMiddleware)

# JWT security scheme
security = HTTPBearer()

def get_current_user(token: str = Depends(security)):
    if not token:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="No token provided"
        )
    
    try:
        payload = jwt.decode(
            token.credentials,
            os.getenv('JWT_SECRET'),
            algorithms=['HS256']
        )
        return payload
    except JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or expired token"
        )

@app.post("/auth/login")
async def login(credentials: dict):
    # Authenticate user (implementation omitted)
    user = authenticate_user(credentials.get("username"), credentials.get("password"))
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid credentials"
        )
    
    # Create JWT with secure configuration
    access_token_expires = timedelta(hours=1)
    access_token = jwt.encode(
        {
            "sub": user.id,
            "username": user.username,
            "iat": datetime.utcnow(),
            "exp": datetime.utcnow() + access_token_expires,
            "iss": "your-domain.com",
            "aud": "your-app"
        },
        os.getenv('JWT_SECRET'),
        algorithm="HS256"
    )
    
    return {"access_token": access_token, "token_type": "bearer"}

@app.get("/protected")
async def protected_route(current_user: dict = Depends(get_current_user)):
    return {"message": "Access granted", "user": current_user}

@app.post("/auth/refresh")
async def refresh_token(refresh_payload: dict):
    # Verify TLS connection
    if not app.state.is_https:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Refresh endpoint requires HTTPS"
        )
    
    # Refresh token logic with HTTPS enforcement
    try:
        payload = jwt.decode(
            refresh_payload.get("refresh_token"),
            os.getenv('REFRESH_SECRET'),
            algorithms=['HS256']
        )
        
        # Create new access token
        new_token = jwt.encode(
            {
                "sub": payload["sub"],
                "username": payload["username"],
                "iat": datetime.utcnow(),
                "exp": datetime.utcnow() + timedelta(hours=1),
                "iss": "your-domain.com"
            },
            os.getenv('JWT_SECRET'),
            algorithm="HS256"
        )
        
        return {"access_token": new_token}
    except JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid refresh token"
        )

Related CWEs: encryption

CWE IDNameSeverity
CWE-319Cleartext Transmission of Sensitive Information HIGH
CWE-295Improper Certificate Validation HIGH
CWE-326Inadequate Encryption Strength HIGH
CWE-327Use of a Broken or Risky Cryptographic Algorithm HIGH
CWE-328Use of Weak Hash HIGH
CWE-330Use of Insufficiently Random Values HIGH
CWE-338Use of Cryptographically Weak PRNG MEDIUM
CWE-693Protection Mechanism Failure MEDIUM
CWE-757Selection of Less-Secure Algorithm During Negotiation HIGH
CWE-261Weak Encoding for Password HIGH

Frequently Asked Questions

Can JWT tokens be transmitted securely over HTTP if they're encrypted?
No. Even encrypted JWTs should never be transmitted over HTTP. TLS provides confidentiality, integrity, and authentication for the entire transmission, while JWT encryption only protects the token payload. An attacker can still intercept encrypted tokens over HTTP and use them for replay attacks or session hijacking. Always use HTTPS for JWT transmission regardless of token encryption.
How does middleBrick detect missing TLS in JWT implementations?
middleBrick uses black-box scanning to test JWT endpoints over both HTTP and HTTPS. It identifies JWT-related endpoints through OpenAPI spec analysis or runtime discovery, then attempts HTTP connections to authentication, token refresh, and validation endpoints. If these endpoints respond successfully over HTTP or redirect insecurely, middleBrick flags missing TLS. The scanner also checks for JWTs in query parameters, insecure cookie flags, and weak signing algorithms exposed over unencrypted channels.