# (취약점 제거됨) Spring Boot: 2024 JJWT 취약점(v0.12.5 이하) 및 'signWith(java.security.Key, io.jsonwebtoken.SignatureAlgorithm)' is deprecated 해결 (v0.12.0 이상)

# JJWT Impl의 취약점 발견

## 취약점 보고(CVE)

> 참고: `signWith()` 함수의 deprecated는 0.12.0 버전에 되어 CVE 보고와 무관합니다. 취약점은 사소한 것이며, 0.11 버전 이하를 사용하더라도 반드시 올려야 하는 것은 아닙니다.  
> 벤더 측에서 릴리스한 버전을 권장하기 위하여 정보를 공유합니다.

해당 취약점은 CVE에 [**CVE-2024-31033**](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-31033)로 보고되었습니다. (2024-03-27, 논쟁 있음)

이 보고의 내용은 이렇습니다. (논쟁)

> 0.12.5 이하 JJWT(Java JWT)는 특정 문자를 무시하므로 사용자에게 강력한 키(key)가 있다고 잘못 판단했을 수 있습니다.

JJWT 공급자는 다음처럼 이의를 제기합니다.

> JJWT 사용 방식에서 사용자의 오류가 없는 한 '무시(ignores)' 동작이 어떤 버전에서도 발생할 수 없으며, 실제 테스트된 버전은 6년 이상 지나야 하기 때문에 이의를 제기하고 있습니다.

이 취약점의 영향을 받는 코드는 다음과 같습니다.

* `DefaultJwtParser` 클래스 내의 `setSigningKey()` 메서드
    
* `DefaultJwtBuilder` 클래스 내의 `signWith()` 메서드
    

## 새 버전 릴리스

MVN Repository에서 다음처럼 0.12.5 이하 버전은 모두 취약점 하나가 체크되어 있으며, 올해 6월 21일에 그 다음 버전인 0.12.6 버전이 릴리스되었습니다.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721147376330/96d8023b-1bb6-4211-8552-b6a3cfade0f1.png align="center")

---

# 새 버전에서 유효한 방식 (v0.12.0 이상)

## 의존성 추가

다음처럼 v0.12.6으로 의존성을 추가합니다.

```kotlin
// build.gradle에서 관련 모듈의 dependencies
dependencies {
    // jjwt
    implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
    runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
    runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
}
```

## JWT Provider 작성

deprecated 예시

```java
// 0.11 버전에서 deprecated 되었습니다.
// (io.jsonwebtoken.SignatureAlgorithm, String)
.signWith(SignatureAlgorithm.HS256, secret)

// 0.12.0 버전에서 deprecated 되었습니다.
// (java.security.Key, io.jsonwebtoken.SignatureAlgorithm)
.signWith(secretKey, SignatureAlgorithm.HS256)
```

유효한 함수 (기본 알고리즘: HS256)

```java
// signWith(java.security.Key)
.signWith(secretKey)
```

전체 코드 예시 (generateToken() 함수 위주로 보세요.)

```java
import example.demo.common.jwt.properties.DemoJwtProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtProvider {

    private final Key secretKey;
    private final long maxAge;

    // 구성 속성을 받아 올 때는 편한 방식(@Value 등)을 사용하면 됩니다.
    public JwtProvider(DemoJwtProperties demoJwtProperties) {
        secretKey = generateSecretKey(demoJwtProperties.secret());
        maxAge = demoJwtProperties.maxAge();
    }

    @Override
    public String generateToken(String subject, Map<String, ?> payload) {
        // header, payload, signature -> base64
        Claims claims = generateClaims(subject, payload);

        return Jwts.builder()
                .signWith(secretKey)
                .claims(claims)
                .compact();
    }

    // DemoJwtProperties 파일에 작성할 수도 있지만, 속성 파일에 외부 기술에 대한 종속성을 만들지 않기 위해 여기서 작성.
    private Key generateSecretKey(String secret) {
        byte[] keyBytes = Decoders.BASE64.decode(secret);
        return Keys.hmacShaKeyFor(keyBytes);
    }

    private Claims generateClaims(String subject, Map<String, ?> payload) {
        Date now = new Date();
        Date expirationAt = new Date(now.getTime() + maxAge);

        return Jwts.claims()
                .subject(subject)
                .issuedAt(now)
                .expiration(expirationAt)
                .add(payload)
                .build();
    }
}
```
