jwt 훅은 알고리즘을 고정하지 않은 JWT 검증과 아예 꺼 버린 검증을 잡아냅니다. 알고리즘을 고정하지 않은 검증은 검증하는 쪽이 토큰에 적힌 alg 헤더를 그대로 믿게 되는 알고리즘 혼동 허점입니다. 언어를 인식해 파일마다 그 파일이 쓰는 JWT 라이브러리에 맞춰 읽습니다. 또한 federation 전문 지식을 뒷받침하는 결정론적 엔진이며, scan 모드나 check 모드로 직접 실행할 수도 있습니다.
실행하기
- 코드베이스 스캔
- 호출 검사
npx lagune read jwt # scans the whole project
npx lagune read jwt --dir src # scans a directory
npx lagune read jwt --file auth.ts # scans a single file
npx lagune read jwt --lang javascript --pattern 'jwt.verify(token, secret)' # => unpinned
npx lagune read jwt --lang python --pattern 'jwt.decode(t, k, options={"verify_signature": False})' # => unpinned
npx lagune read jwt --lang python --pattern 'jwt.decode(t, k, algorithms=["HS256"])' # => safe
Unpinned JWT verification found 섹션 아래의 각 줄은 algorithms 허용 목록을 지정하지 않았거나(라이브러리가 요구하는 경우), 안전하지 않은 허용 목록(none, 또는 비대칭 알고리즘과 HMAC의 혼용)을 받아들이거나, 서명 검사를 아예 꺼 버려서 토큰에 적힌 alg 값을 믿게 되는 검증 호출입니다. 확정된 노출이므로 훅은 0이 아닌 종료 코드를 반환합니다. 아무것도 발견되지 않으면 no unpinned JWT verification found 메시지를 출력합니다.
판정 해석하기
| 판정 | 의미 |
|---|---|
unpinned | 알고리즘 허용 목록이 없거나(필요한 경우), 허용 목록이 안전하지 않거나, 검증이 꺼진 검증 호출 |
safe | 안전한 알고리즘 허용 목록을 고정했거나, 라이브러리가 암묵적으로 고정하는 검증 호출 |
invalid | 판정할 수 있는 검증 호출이 없는 코드 조각 |
CLI 옵션
| 옵션 | 값 | 설명 |
|---|---|---|
--pattern | 코드 조각 | 호출 하나 검사. 반복해 지정하면 여러 호출을 검사해 한 줄에 판정 하나씩 출력 |
--lang | 언어 | --pattern 옵션 사용 시 필수: javascript, python, go, java, kotlin, php, ruby, rust, csharp 중 하나 |
--dir | 디렉터리 | 스캔 범위를 디렉터리로 한정. 반복 지정 가능, --file 옵션과 함께 사용 가능 |
--file | 파일 | 스캔 범위를 단일 파일로 한정. 반복 지정 가능, --dir 옵션과 함께 사용 가능 |
옵션을 지정하지 않으면 프로젝트 전체를 스캔합니다. --pattern 옵션에는 --lang 옵션이 필요하며, --dir 또는 --file 옵션과 함께 쓸 수 없습니다.
지원 언어
스캔은 파일 확장자로 다음 라이브러리를 구분해 읽습니다. 일부 라이브러리는 알고리즘을 암묵적으로 고정해 명시적인 목록이 없어도 안전하므로, "허용 목록 없음 ⇒ unpinned" 규칙은 라이브러리가 명시적인 목록을 기대하는 경우에만 적용됩니다. 그래서 올바르게 설정된 호출을 잡아내는 일은 없습니다.
| 언어 | 라이브러리 | 허용 목록이 없을 때 unpinned 판정 여부 | 항상 잡아내는 것 |
|---|---|---|---|
| JavaScript / TypeScript | jsonwebtoken, jose | 예 | none, 비대칭 알고리즘과 HMAC의 혼용 |
| Python | PyJWT, python-jose | 예(PyJWT는 필수, python-jose는 기본값이 algorithms=None) | none, verify_signature: False |
| Go | golang-jwt | 예(WithValidMethods 옵션이 없는 경우) | none, ParseUnverified |
| Ruby | ruby-jwt | 예 | none, verify = false |
| Java / Kotlin | jjwt, nimbus | 아니요(키에서 도출) | none, 서명 없는/unsecured 파서 |
| PHP | firebase/php-jwt | 아니요(Key 객체가 알고리즘을 담음) | none |
| Rust | jsonwebtoken | 아니요(Validation 구조체가 알고리즘 지정을 요구) | none, insecure_disable_signature_validation |
| C# | Microsoft.IdentityModel | 아니요(매개변수가 여러 줄에 걸침) | none, RequireSignedTokens = false |
훅이 판단하는 것은 알고리즘 고정뿐입니다. 클레임, 키, 흐름 검사처럼 판단이 필요한 부분은 여전히 federation 전문 지식이 다룹니다.
자주 묻는 질문
검증 코드가 토큰 헤더에서 알고리즘을 읽으면, 공격자가 RS256을 HS256으로 바꿔치기하고 공개 키로 서명할 수 있습니다. 안전한 알고리즘 허용 목록을 코드에 고정하면 이를 막을 수 있으며, jwt 훅은 허용 목록을 지정하지 않았거나 안전하지 않은 허용 목록을 쓰는 검증 호출을 잡아냅니다.
