Skip to content

[Security] Fix hardcoded JWT secret key ??use env var with startup validation (CWE-798)#45

Open
saaa99999999 wants to merge 1 commit into
bfwg:masterfrom
saaa99999999:fix/hardcoded-jwt-secret
Open

[Security] Fix hardcoded JWT secret key ??use env var with startup validation (CWE-798)#45
saaa99999999 wants to merge 1 commit into
bfwg:masterfrom
saaa99999999:fix/hardcoded-jwt-secret

Conversation

@saaa99999999
Copy link
Copy Markdown

Security Audit Report ??springboot-jwt-starter (bfwg)

Manual code audit discovered 1 critical security vulnerability ??CWE-798: Hardcoded JWT Signing Key.


CRITICAL: CWE-798 ??Hardcoded JWT Secret Key in Public Repository (CVSS 9.1)

Location: src/main/resources/application.yml:8

Data Flow:

application.yml:8 jwt.secret: p2H(++:$j*xKfKvK6n5k=},@@]CWkbj7d0iQ%6/;0}x6?[wNWR,$=0/HHx*&pJAq
  ??TokenHelper.java:31 @Value("${jwt.secret}") private String SECRET;
  ??TokenHelper.java:82 Keys.hmacShaKeyFor(SECRET.getBytes()) ??JWT signing (generateToken)
  ??TokenHelper.java:72 Keys.hmacShaKeyFor(SECRET.getBytes()) ??JWT refresh (refreshToken)
  ??TokenHelper.java:88 Keys.hmacShaKeyFor(SECRET.getBytes()) ??JWT verification (getAllClaimsFromToken)
  ??TokenAuthenticationFilter.java:48 tokenHelper.validateToken(authToken, userDetails)
     ??validates every authenticated request
  ??TokenAuthenticationFilter.java:52 SecurityContextHolder.getContext().setAuthentication(authentication)
     ??sets the authenticated principal in Spring Security context
  ??AuthenticationController.java:64 tokenHelper.generateToken(user.getUsername())
     ??returns the signed JWT on successful login

Description:
application.yml contains a hardcoded JWT signing key p2H(++:$j*xKfKvK6n5k=},@@]CWkbj7d0iQ%6/;0}x6?[wNWR,$=0/HHx*&pJAq published in the public repository with 841 stars. The key is used with HS512 (HMAC-SHA512) symmetric algorithm ??the same key for both signing and verifying tokens.

The key looks like a real generated key (63 complex characters), making it especially dangerous because developers are unlikely to recognize it as a default value that must be changed.

Anyone who reads the public repository can:

  1. Forge valid JWT tokens ??Create tokens for any username, gaining full access to all authenticated endpoints
  2. Refresh tokens indefinitely ??The /auth/refresh endpoint uses the same key for re-signing
  3. Impersonate any user ??The TokenAuthenticationFilter extracts username from forged tokens and sets the full Spring Security context

PoC:

// Forge a JWT using the hardcoded key from application.yml
String secret = "p2H(++:$j*xKfKvK6n5k=},@@]CWkbj7d0iQ%6/;0}x6?[wNWR,$=0/HHx*&pJAq";
SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());

String forgedToken = Jwts.builder()
    .issuer("springboot-jwt-demo")
    .subject("admin")  // any username
    .issuedAt(new Date())
    .expiration(new Date(System.currentTimeMillis() + 86400000))
    .signWith(key, Jwts.SIG.HS512)
    .compact();

// curl -H "Authorization: Bearer <forgedToken>" http://localhost:8080/api/whoami
// ??Returns user details for "admin"

Fix:

  1. application.yml:8: Replaced hardcoded secret with ${JWT_SECRET:} ??Spring Boot will read from JWT_SECRET env var, defaults to empty
  2. TokenHelper.java:31: Changed SECRET from public to private
  3. TokenHelper.java:44-65: Added @PostConstruct validateSecret() that rejects empty keys, the original hardcoded key, other known weak values, and keys shorter than 32 characters. Application refuses to start with a clear error message.

Before ??After:

application.yml:8:

# Before:
jwt:
  secret: p2H(++:$j*xKfKvK6n5k=},@@]CWkbj7d0iQ%6/;0}x6?[wNWR,$=0/HHx*&pJAq

# After:
jwt:
  secret: ${JWT_SECRET:}

TokenHelper.java:31:

// Before:
@Value("${jwt.secret}")
public String SECRET;

// After:
@Value("${jwt.secret}")
private String SECRET;

TokenHelper.java:44-65 (added):

@jakarta.annotation.PostConstruct
public void validateSecret() {
    if (SECRET == null || KNOWN_WEAK_SECRETS.contains(SECRET)) {
        throw new IllegalStateException(
            "jwt.secret is not set or uses a known default value. " +
            "Set it via the JWT_SECRET environment variable. " +
            "Generate a secure key: openssl rand -base64 64"
        );
    }
    if (SECRET.length() < 32) {
        throw new IllegalStateException(
            "jwt.secret must be at least 32 characters. Current length: " + SECRET.length()
        );
    }
}

Changes in this PR (2 files)

File Change
src/main/resources/application.yml Replaced hardcoded jwt.secret with ${JWT_SECRET:} env var placeholder
src/main/java/com/bfwg/security/TokenHelper.java Made SECRET private; Added @PostConstruct validateSecret() with known-weak rejection + minimum length check

CVSS 3.1 Vector

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N ??9.1 (Critical)


Manual security audit of Spring Boot JWT authentication configuration

…artup validation (CWE-798)

Replaced the hardcoded jwt.secret with ${JWT_SECRET:} placeholder and added
@PostConstruct validation that rejects empty, known-default, and short keys.
The application now refuses to start with a clear error message if the JWT
secret is not properly configured.
@saaa99999999
Copy link
Copy Markdown
Author

CVE Request — Action Needed from Maintainer

This PR fixes security vulnerabilities. To assign a CVE number:

GitHub only issues CVEs from the official upstream repository, not from forks.

Please:

  1. Go to this repo → SecurityAdvisoriesNew draft security advisory
  2. Add @saaa99999999 as a collaborator
  3. I will populate the full vulnerability details (CVSS, CWE, data flow, PoC) and submit the CVE request

If you prefer, I can submit the CVE via MITRE (cveform.mitre.org) instead — just let me know.

Thank you for reviewing this PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant