Merge pull request #20 from j93es/refactor/access-token

[REFACTOR]: 요청 별 검증 함수를 분리하여 오탐률 개선
This commit is contained in:
김민곤 2025-06-29 21:03:30 +09:00 committed by GitHub
commit 949b156f19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,9 +1,9 @@
import re import re
from typing import Optional, Any
import asyncio import asyncio
from typing import Optional, Any
from mitmproxy.http import HTTPFlow from mitmproxy.http import HTTPFlow
from urllib.parse import urlparse, parse_qs
from lib.report_vuln import report_vuln from lib.report_vuln import report_vuln
@ -24,13 +24,12 @@ class AccessTokenScanner:
print("[TOKENDEBUG] ==scan request==") print("[TOKENDEBUG] ==scan request==")
# URL 검사 # URL 검사
token_result = self._extract_token(request.url) if self._is_implicit_flow(request.url):
if token_result: print("[TOKENDEBUG] OAuth Implicit Flow detected.")
token, has_bearer = token_result
report_vuln( report_vuln(
title="Token Leak in Request URL", title="Token Leak in Request URL",
desc=f"요청 URL에 토큰이 포함되어 있습니다 (앞 20자): {token[:20]}", desc="취약한 Grant Type입니다 (Implicit Grant Type)",
status="MEDIUM" if has_bearer else "LOW", status="MEDIUM",
uri=request.url uri=request.url
) )
@ -39,11 +38,10 @@ class AccessTokenScanner:
body_text = request.get_text(strict=False) body_text = request.get_text(strict=False)
token_result = self._extract_token(body_text) token_result = self._extract_token(body_text)
if token_result: if token_result:
token, has_bearer = token_result
report_vuln( report_vuln(
title="Token Leak in Request Body", title="Token Leak in Request Body",
desc=f"요청 본문에 토큰이 포함되어 있습니다 (앞 20자): {token[:20]}", desc=f"요청 본문에 토큰이 포함되어 있습니다 (앞 20자): {token_result[:20]}",
status="MEDIUM" if has_bearer else "LOW", status="LOW",
uri=request.url uri=request.url
) )
@ -56,28 +54,24 @@ class AccessTokenScanner:
if location_header := response.headers.get("Location"): if location_header := response.headers.get("Location"):
token_result = self._extract_token(location_header) token_result = self._extract_token(location_header)
if token_result: if token_result:
token, has_bearer = token_result report_vuln(
if has_bearer: title="Token Leak in Redirect URL (Location header)",
report_vuln( desc=f"Location 헤더에 토큰이 노출되었습니다 (앞 20자): {token_result[:20]}",
title="Token Leak in Redirect URL (Location header)", status="MEDIUM",
desc=f"Location 헤더에 토큰이 노출되었습니다 (앞 20자): {token[:20]}", uri=location_header,
status="MEDIUM", )
uri=location_header,
)
# Body 검사 (텍스트 컨텐츠인 경우) # Body 검사 (텍스트 컨텐츠인 경우)
if response.content: if response.content:
body_text = response.get_text(strict=False) body_text = response.get_text(strict=False)
token_result = self._extract_token(body_text) token_result = self._extract_token(body_text)
if token_result: if token_result:
token, has_bearer = token_result report_vuln(
if has_bearer: title="Token Leak in Response Body",
report_vuln( desc=f"응답 본문에 토큰이 노출되었습니다 (앞 20자): {token_result[:20]}",
title="Token Leak in Response Body", status="LOW",
desc=f"응답 본문에 토큰이 노출되었습니다 (앞 20자): {token[:20]}", uri=request_url,
status="MEDIUM", )
uri=request_url,
)
# 토큰 탐지 키워드드 # 토큰 탐지 키워드드
_TOKEN_KEYS = [ _TOKEN_KEYS = [
@ -87,8 +81,6 @@ class AccessTokenScanner:
"refreshtoken", "refreshtoken",
"auth_token", "auth_token",
"session_token", "session_token",
"secret_token",
"ssoauth",
] ]
# "bearer" 표시가 동시에 존재할 때만 토큰으로 판단하여 false positive를 줄임 # "bearer" 표시가 동시에 존재할 때만 토큰으로 판단하여 false positive를 줄임
@ -122,6 +114,38 @@ class AccessTokenScanner:
if (m := pattern.search(text)) and m.group(1): if (m := pattern.search(text)) and m.group(1):
print(f"[TOKENDEBUG] token: {m.group(1)}") print(f"[TOKENDEBUG] token: {m.group(1)}")
print(f"[TOKENDEBUG] has_bearer: {has_bearer}") print(f"[TOKENDEBUG] has_bearer: {has_bearer}")
return m.group(1), has_bearer if has_bearer:
return m.group(1)
print("[TOKENDEBUG] No matched.") print("[TOKENDEBUG] No matched.")
return None return None
def _is_implicit_flow(request_url: str) -> bool:
"""
URL의 파라미터에서 OAuth Implicit Flow 패턴을 체크합니다.
Args:
request_url: 체크할 요청 URL
Returns:
bool: client_id, redirect_uri, response_type이 모두 존재하고
response_type 값이 'token' 경우 True, 그렇지 않으면 False
"""
try:
parsed_url = urlparse(request_url)
query_params = parse_qs(parsed_url.query)
# 필요한 파라미터들이 모두 존재하는지 확인
required_params = ['client_id', 'redirect_uri', 'response_type']
for param in required_params:
if param not in query_params:
return False
# response_type 값이 'token'인지 확인
response_type_values = query_params.get('response_type', [])
# response_type 파라미터가 존재하고 값 중에 'token'이 있는지 확인
return 'token' in response_type_values
except Exception:
return False