mirror of
https://github.com/j93es/oauth-backend.git
synced 2026-06-04 05:21:51 +09:00
Update open_redirect_check.py
This commit is contained in:
parent
905db47d8a
commit
7d378fa91f
1 changed files with 26 additions and 13 deletions
|
|
@ -1058,7 +1058,7 @@ class OpenRedirectChecker:
|
||||||
self.session = None
|
self.session = None
|
||||||
|
|
||||||
""" 우회된 URL에 GET 요청을 보내고, 서버 응답 정보 반환 """
|
""" 우회된 URL에 GET 요청을 보내고, 서버 응답 정보 반환 """
|
||||||
async def _send_request(self, url, headers=None):
|
async def _send_request(self, url, headers=None, record_to_limiter=True):
|
||||||
try:
|
try:
|
||||||
session = await self._get_session() # 세션 준비
|
session = await self._get_session() # 세션 준비
|
||||||
request_headers = headers or {} # 요청 헤더 설정 - headers가 주어지지 않았다면 빈 딕셔너리 사용
|
request_headers = headers or {} # 요청 헤더 설정 - headers가 주어지지 않았다면 빈 딕셔너리 사용
|
||||||
|
|
@ -1072,10 +1072,11 @@ class OpenRedirectChecker:
|
||||||
}
|
}
|
||||||
|
|
||||||
# 성공/실패 기록 (rate limiter용)
|
# 성공/실패 기록 (rate limiter용)
|
||||||
if response.status in [200, 301, 302, 303, 307, 308]:
|
if record_to_limiter:
|
||||||
redirect_limiter.record_success()
|
if response.status in [200, 301, 302, 303, 307, 308]:
|
||||||
else:
|
redirect_limiter.record_success()
|
||||||
redirect_limiter.record_failure(f"HTTP {response.status}")
|
else:
|
||||||
|
redirect_limiter.record_failure(f"HTTP {response.status}")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
@ -1116,6 +1117,9 @@ class OpenRedirectChecker:
|
||||||
'/oauth/callback', '/auth/callback', '/login/callback',
|
'/oauth/callback', '/auth/callback', '/login/callback',
|
||||||
'/oauth/redirect', '/auth/return', '/sso/callback',
|
'/oauth/redirect', '/auth/return', '/sso/callback',
|
||||||
'/signup', '/register',
|
'/signup', '/register',
|
||||||
|
'/api/auth/v3/social/callback', # velog.io
|
||||||
|
'/social/callback', # 일반화
|
||||||
|
'/auth/social/callback', # 변형
|
||||||
]
|
]
|
||||||
|
|
||||||
path = parsed.path.lower()
|
path = parsed.path.lower()
|
||||||
|
|
@ -1134,7 +1138,11 @@ class OpenRedirectChecker:
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# 패턴 1: 간단한 소셜 식별자
|
# 패턴 1: 간단한 소셜 식별자
|
||||||
simple_patterns = ['from=fb', 'from=facebook', 'from=google', 'from=github']
|
simple_patterns = [
|
||||||
|
'from=fb', 'from=facebook', 'from=google', 'from=github',
|
||||||
|
'provider=google', 'provider=facebook',
|
||||||
|
'callback/google', 'callback/github', 'callback/facebook' # ← URL 경로 패턴
|
||||||
|
]
|
||||||
has_simple = any(pattern in query_string.lower() for pattern in simple_patterns)
|
has_simple = any(pattern in query_string.lower() for pattern in simple_patterns)
|
||||||
|
|
||||||
# 패턴 2: URL 형태
|
# 패턴 2: URL 형태
|
||||||
|
|
@ -1151,7 +1159,7 @@ class OpenRedirectChecker:
|
||||||
'next', 'return_to', 'continue', 'redirect_uri', 'redirect_url',
|
'next', 'return_to', 'continue', 'redirect_uri', 'redirect_url',
|
||||||
'destination', 'success_url', 'callback_url', 'goto', 'forward_to',
|
'destination', 'success_url', 'callback_url', 'goto', 'forward_to',
|
||||||
'redirectUrl', 'redirectURL', 'redirect_to', 'returnUrl', 'returnURL',
|
'redirectUrl', 'redirectURL', 'redirect_to', 'returnUrl', 'returnURL',
|
||||||
'from', 'target', 'targetUrl', 'targetURL' # 일반적인 변형들 추가
|
'from', 'target', 'targetUrl', 'targetURL',
|
||||||
]
|
]
|
||||||
|
|
||||||
has_redirect_param = any(param in query for param in client_redirect_params)
|
has_redirect_param = any(param in query for param in client_redirect_params)
|
||||||
|
|
@ -1203,7 +1211,11 @@ class OpenRedirectChecker:
|
||||||
'/oauth2/v1/authorize',
|
'/oauth2/v1/authorize',
|
||||||
'/authorize', # 일반적인 authorize 추가
|
'/authorize', # 일반적인 authorize 추가
|
||||||
'/auth/realms', # Keycloak 패턴
|
'/auth/realms', # Keycloak 패턴
|
||||||
'/connect/authorize' # IdentityServer 패턴
|
'/connect/authorize', # IdentityServer 패턴
|
||||||
|
'/idp/', # ← Identity Provider 패턴
|
||||||
|
'/signin', # ← Sign-in 패턴
|
||||||
|
'/auth/signin', # ← Auth Sign-in 패턴
|
||||||
|
'/identity/', # ← Identity 패턴
|
||||||
]
|
]
|
||||||
|
|
||||||
path = parsed.path.lower()
|
path = parsed.path.lower()
|
||||||
|
|
@ -1263,6 +1275,9 @@ class OpenRedirectChecker:
|
||||||
if 'state' in query:
|
if 'state' in query:
|
||||||
await self._test_state_parameter_manipulation(url, parsed, query)
|
await self._test_state_parameter_manipulation(url, parsed, query)
|
||||||
|
|
||||||
|
# 일반 리다이렉트 파라미터 테스트를 위해 Rate Limiter 리셋
|
||||||
|
redirect_limiter.reset_for_new_target()
|
||||||
|
|
||||||
# 2. 테스트할 파라미터들 찾기
|
# 2. 테스트할 파라미터들 찾기
|
||||||
test_params = []
|
test_params = []
|
||||||
|
|
||||||
|
|
@ -1289,8 +1304,7 @@ class OpenRedirectChecker:
|
||||||
print(f"[OAUTH] 📍 발견된 파라미터들: {test_params}")
|
print(f"[OAUTH] 📍 발견된 파라미터들: {test_params}")
|
||||||
print(f"[OPEN_REDIRECT] 총 우회 패턴: {len(self.bypass_payloads)}")
|
print(f"[OPEN_REDIRECT] 총 우회 패턴: {len(self.bypass_payloads)}")
|
||||||
print("-" * 50)
|
print("-" * 50)
|
||||||
|
|
||||||
redirect_limiter.reset_for_new_target()
|
|
||||||
success_count = 0
|
success_count = 0
|
||||||
|
|
||||||
# 3. 각 파라미터별로 모든 우회 패턴 테스트
|
# 3. 각 파라미터별로 모든 우회 패턴 테스트
|
||||||
|
|
@ -1368,7 +1382,7 @@ class OpenRedirectChecker:
|
||||||
test_url = urlunparse(parsed._replace(query=new_query_string))
|
test_url = urlunparse(parsed._replace(query=new_query_string))
|
||||||
|
|
||||||
# 요청 전송
|
# 요청 전송
|
||||||
response = await self._send_request(test_url)
|
response = await self._send_request(test_url, record_to_limiter=False)
|
||||||
|
|
||||||
# State 전용 취약점 분석
|
# State 전용 취약점 분석
|
||||||
if await self._analyze_state_vulnerability(url, test_url, malicious_state, response, attack_name):
|
if await self._analyze_state_vulnerability(url, test_url, malicious_state, response, attack_name):
|
||||||
|
|
@ -1377,8 +1391,7 @@ class OpenRedirectChecker:
|
||||||
else:
|
else:
|
||||||
print(f"[STATE_TEST] ✓ {attack_name}")
|
print(f"[STATE_TEST] ✓ {attack_name}")
|
||||||
|
|
||||||
# 레이트 리미팅
|
await asyncio.sleep(1) # 1초 대기
|
||||||
await redirect_limiter.wait_if_needed(f"state_{attack_name}")
|
|
||||||
|
|
||||||
print(f"[STATE_TEST] ✅ State 테스트 완료: {success_count}/{len(state_attacks)} 취약점 발견")
|
print(f"[STATE_TEST] ✅ State 테스트 완료: {success_count}/{len(state_attacks)} 취약점 발견")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue