
보안 취약점 & 대응
Open Redirect오픈 리다이렉트
오픈 리다이렉트(Open Redirect)는 웹 애플리케이션이 사용자가 제공한 URL을 검증 없이 리다이렉트해, 피싱이나 OAuth 토큰 탈취에 악용되는 취약점이다.
취약 코드와 공격
python
# 취약한 코드 (Flask)
@app.route('/redirect')
def redirect_to():
url = request.args.get('next') # 사용자 입력 직접 사용
return redirect(url) # 검증 없이 리다이렉트
# 공격 URL 예시:
# https://legitimate.com/redirect?next=https://evil.com
# https://legitimate.com/redirect?next=//evil.com
# https://legitimate.com/redirect?next=https://legitimate.com.evil.com
# 피싱 시나리오:
# 1. 이메일: "계정 확인: https://bank.com/redirect?next=https://phishing.com"
# 2. 사용자: bank.com URL 신뢰하여 클릭
# 3. evil.com으로 이동, 가짜 로그인 페이지OAuth에서의 오픈 리다이렉트
OAuth2 인가 코드 흐름 공격:
1. 정상 요청:
GET /oauth/authorize?
client_id=app&
redirect_uri=https://app.com/callback& ← 검증됨
response_type=code
2. 공격 시나리오:
- 앱 내부에 오픈 리다이렉트 존재:
https://app.com/redirect?to=https://evil.com
- 공격자 요청:
GET /oauth/authorize?
client_id=app&
redirect_uri=https://app.com/redirect?to=https://evil.com&
response_type=code
- redirect_uri가 app.com이므로 검증 통과
- 인가 코드가 evil.com으로 전달됨!안전한 리다이렉트 구현
python
from urllib.parse import urlparse, urljoin
ALLOWED_DOMAINS = {'example.com', 'api.example.com'}
def safe_redirect(url: str, default: str = '/') -> str:
if not url:
return default
# 절대 URL 검증
parsed = urlparse(url)
# 상대 URL은 허용 (같은 사이트)
if not parsed.netloc:
# 경로 기반 리다이렉트만 허용
if url.startswith('/') and not url.startswith('//'):
return url
return default
# 허용 도메인 화이트리스트 확인
if parsed.netloc in ALLOWED_DOMAINS:
return url
return default
# 사용
@app.route('/login')
def login():
# 로그인 후 안전한 리다이렉트
next_url = safe_redirect(request.args.get('next', '/'))
return redirect(next_url)관련 문서
- •[[clickjacking|클릭재킹]]
- •[[saml|SAML]]
- •[[openid-connect|OpenID Connect]]