오픈 리디렉션은 실제 도메인으로 시작하는 URL이 방문자를 완전히 다른 사이트로 보내는 결함입니다. 동작 원리와 확실하게 막는 방법을 설명합니다(공격 절차 없음).
왜 위험한가
핵심 피해는 도메인의 신뢰를 빌려주는 것입니다.
| 악용 방식 | 일어나는 일 |
|---|---|
| 도메인을 이용한 피싱 | 링크가 실제 도메인으로 시작 → 사용자와 필터가 신뢰 → 가짜 사이트로 이동 |
| 인증 흐름 탈취 | OAuth 형태의 흐름에서 리디렉션 대상을 변조해 토큰/코드를 가로챔 |
| 다른 버그 증폭 | SSRF 나 XSS 와 결합해 영향 범위를 넓힘 |
왜 통하는가
"로그인 후 사용자를 원래 있던 곳으로 되돌린다" 같은 기능에서 나타나며, 이때 복귀 URL이 신뢰되어 그대로 전달됩니다. 사용자가 보는 URL의 "시작"은 실제 도메인이므로, 끝까지 의심하기가 어렵습니다.
방어: 외부 URL을 받지 않기
상대 경로만 허용 (가장 중요)
/dashboard 같은 복귀 대상은 상대 경로만 받아들이고, 전체 URL(http:// 나 // 로 시작하는 것)은 거부하세요. 사용자가 목적지 도메인을 지정할 여지를 전혀 주지 마세요.
허용 목록과 대조
동적 대상이 필요하다면, 알려진 목적지의 고정된 집합(매핑)에 있는 항목만 허용하세요. 그 외에는 기본 페이지로 폴백합니다.
문자열 검사에 의존하지 않기
//evil, 역슬래시, @, 다중 인코딩이 단순한 필터를 우회합니다. 필터에 땜질하지 말고 받아들이는 대상 자체를 바꾸세요(상대 경로 + 허용 목록).
외부로 나가야 한다면 명시적으로
외부 리디렉션이 의도된 것이라면 안내 페이지("외부 사이트로 이동합니다")를 표시하세요. 조용히 전달하지 마세요.
본 사이트의 견해: 단독으로는 '낮음'이지만, 피싱과 결합하면 심각해진다
오픈 리디렉션은 단독으로는 위험도가 낮게 평가되는 경우가 많지만, 공격자가 도메인의 신뢰를 빌릴 수 있기 때문에 피싱이나 인증 탈취와 결합하면 심각해집니다. 그래서 설계 차원의 조치(상대 경로 + 허용 목록)가 원인을 효율적으로 제거합니다. 이는 SSRF(서버가 임의의 곳에 접속하게 만드는 것)와 닮아 있습니다. 둘 다 목적지를 사용자가 결정하게 두지 않는다는 방어 원칙을 공유합니다.
오픈 리디렉션 vs SSRF vs 피싱
오픈 리디렉션은 그 자체가 공격이라기보다 다른 공격을 더 그럴듯하게 만드는 결함입니다. 관련 용어와 나란히 놓으면 위치가 보입니다.
| 용어 | 일어나는 일 | 고치는 쪽 |
|---|---|---|
| 오픈 리디렉션 | 진짜 사이트의 URL이 사용자의 브라우저를 외부 사이트로 넘겨 보낸다 | 리디렉션 기능이 있는 사이트의 개발자 |
| SSRF | 브라우저가 아니라 서버가 지정된 목적지로 요청을 보낸다 | URL 가져오기 기능이 있는 사이트의 개발자 |
| 피싱 | 가짜 사이트에서 ID, 비밀번호, 카드 정보를 입력하게 한다 | 사용자, 메일 수신 측, 사칭당한 조직 |
피싱 링크에 진짜 도메인을 쓸 수 있으면 사용자도 메일 필터도 알아채기 어려워집니다. 오픈 리디렉션을 없애면 내 도메인이 사기에 쓰이는 것을 막을 수 있습니다.
사례: 가장 문제가 되는 곳, 로그인 연동(OAuth)
오픈 리디렉션이 가장 문제가 되는 곳은 로그인 연동(OAuth) 주변입니다. 인가 후 사용자는 인가 코드나 토큰을 가진 채 등록된 리디렉션 URI로 돌아갑니다. 돌아간 사이트에 오픈 리디렉션이 있으면 코드나 토큰이 외부 사이트로 넘어갈 수 있습니다.
그래서 OAuth 2.0 보안 모범 사례(RFC 9700, 2025년 1월)는 두 가지를 요구합니다.
- 인가 서버는 리디렉션 URI를 미리 등록된 것과 완전 일치로 비교한다
- 클라이언트도 인가 서버도 쿼리 파라미터에서 받은 임의의 URL로 넘겨 보내는 페이지를 두지 않는다
오픈 리디렉션은 CWE-601로 분류됩니다. 단독으로는 높은 심각도로 평가되는 일이 드물지만, 로그인 연동 옆에 있으면 심각한 문제가 됩니다.
심각도는 어느 정도인가
오픈 리디렉션은 단독으로는 보통 중간으로 평가됩니다. 흔히 보이는 평가는 CVSS 3.1 6.1입니다.
- 사용자가 링크를 열어야 한다(UI:R)
- 영향은 사용자를 다른 사이트로 보내는 것이며, 사이트의 데이터를 직접 읽게 하지는 않는다
- 영향이 리디렉션하는 사이트 밖으로 미치므로, 범위 변경(S:C)으로 채점된다
숫자만 보고 미루지 마세요. 로그인 연동의 복귀 경로에 있거나 이미 피싱에 쓰이고 있다면 먼저 고치세요. 내 사례를 채점하려면 CVSS 계산기를 쓰세요.
프레임워크에 이미 있는 안전한 헬퍼
프레임워크 기능은 직접 만든 문자열 검사보다 빠뜨리는 경우가 적습니다. 흔한 두 가지 예입니다.
| 프레임워크 | 헬퍼 | 하는 일 |
|---|---|---|
| Ruby on Rails | redirect_to의 기본 동작과 url_from | 기본적으로 다른 호스트로의 리디렉션을 막으며, 외부로 보내려면 allow_other_host: true를 명시해야 한다. url_from은 같은 호스트의 URL일 때만 그 URL을, 아니면 nil을 반환한다 |
| ASP.NET Core | LocalRedirect와 Url.IsLocalUrl | 같은 사이트 안의 URL로만 리디렉션하고, 외부 URL이면 예외를 던진다 |
Rails 문서는 allow_other_host: true를 쓰더라도 사용자가 보낸 파라미터를 그대로 넘기는 것은 안전하지 않다고 경고합니다. 외부 리디렉션 기능을 추가할 때는 그 조합을 피하세요.
내 사이트에서 확인하는 방법
목적지를 받는 파라미터를 찾는다
코드와 URL 목록에서 next, url, redirect, return_to, continue, callback 같은 이름의 파라미터를 찾아, 리디렉션에 그대로 넘겨지지 않는지 확인합니다.
목적지 검증 방식을 확인한다
검증이 문자열 앞부분 일치나 "http로 시작하는가"에 기대고 있지 않은지 봅니다. 대신 URL 파서로 스킴, 호스트, 포트를 나눈 뒤 허용 목록과 비교하거나, 프레임워크의 안전한 헬퍼(ASP.NET Core의 LocalRedirect 등)를 쓰세요.
로그인 연동의 등록 내용을 점검한다
OAuth나 SSO 설정에서 등록된 리디렉션 URI에 와일드카드나 더 이상 필요 없는 옛 URL이 없는지 확인합니다.
목적지를 ID로 고르는 방식을 검토한다
외부로 꼭 리디렉션해야 한다면 URL 자체가 아니라 짧은 ID를 받고, 목적지는 서버 쪽 표에서 찾으세요. OWASP가 권장하는 방식입니다.
액세스 로그에서 외부로의 리디렉션을 찾는다
액세스 로그에서 목적지 파라미터에 내 도메인이 아닌 호스트 이름이 들어 있는 요청을 검색하세요. 발견되면 그 리디렉션이 실제로 외부로 보내는지 확인하고, 누군가 그런 링크를 퍼뜨리고 있을 수 있다는 신호로 다루세요.
출처(1차 자료)
- OWASP, "Unvalidated Redirects and Forwards Cheat Sheet" — cheatsheetseries.owasp.org(서버 쪽에서 매핑하는 ID, 파싱한 URL의 허용 목록 검사, 안내 페이지)
- IETF RFC 9700, "Best Current Practice for OAuth 2.0 Security"(2025년 1월) — datatracker.ietf.org(리디렉션 URI의 완전 일치, 오픈 리디렉터 금지)
- Ruby on Rails API, "ActionController::Redirecting" — api.rubyonrails.org(다른 호스트로의 리디렉션에 대한 기본 보호,
allow_other_host,url_from) - Microsoft, "Prevent open redirect attacks in ASP.NET Core" — learn.microsoft.com(
LocalRedirect,IsLocalUrl, 로컬이 아닌 URL의 로그 기록) - MITRE, "CWE-601: URL Redirection to Untrusted Site ('Open Redirect')" — cwe.mitre.org