서버 측 요청 위조
서버 사이드 요청 위조(SSRF) 취약점은 사용자가 애플리케이션을 조작하여 공격자가 지정한 도메인으로 HTTP 요청을 보내게 할 때 발생합니다. 애플리케이션이 사설 또는 내부 네트워크에 접근할 수 있는 경우, 공격자는 이를 이용해 내부 서버로 요청을 보내도록 만들 수도 있습니다.
실제 사례를 통해 이 취약점이 어떻게 나타나는지 더 자세히 알아보겠습니다.
사용자로부터 URL을 입력받아 해당 페이지의 미리보기나 내보내기용 이미지를 생성하는 API를 예로 들어보겠습니다.
ts
let url = request.params.url;
let response = http.get(url);
let render = response.render();
return render.export();
URL 매개변수를 사용자가 제어할 수 있기 때문에, 공격자는 원하는 모든 URL에 쉽게 접근할 수 있습니다. 사용하는 라이브러리에 따라서는 'file://' 스키마를 사용하여 로컬 파일에 접근하는 것까지 가능할 수 있습니다.
AWS 환경에서 SSRF 취약점을 악용하는 흔한 방법 중 하나는 AWS 메타데이터 API에 접근하여 AWS API 자격 증명을 탈취하는 것입니다. 이는 계정 내 다른 AWS 리소스에 대한 추가적인 접근으로 이어질 수 있으며, 이는 매우 위험한 상황을 초래합니다.
완화 방법
SSRF 취약점 완화는 때때로 매우 까다로우며, 해당 코드가 수행하려는 목적에 따라 크게 달라집니다. 요구 사항에 따라 적용할 수 있는 다양한 완화 전략이 존재합니다.
사용자 결정 URL 피하기
경우에 따라 사용자가 임의의 URL을 제공하지 않아도 되도록 기능을 구현할 수 있습니다. 이것이 가능하다면 SSRF 위험을 완화하는 가장 효과적인 방법입니다.
기능 최소화
PDF 내보내기 기능을 구현할 때, 단순히 헤드리스 브라우저를 사용하여 페이지 스크린샷을 찍는 방식을 택하기 쉽습니다. 하지만 브라우저의 복잡성과 그로 인해 노출되는 수많은 기능 및 공격 표면을 고려할 때, 항상 권장되는 방식은 아닙니다.
작업에 적합한 도구를 사용하는 것이 중요합니다. 어떤 경우에는 간단한 HTTP 클라이언트만으로도 충분할 수 있습니다. 기능과 공격 표면/벡터를 최소화하기 위해 취할 수 있는 조치는 항상 존재합니다. 예를 들어, HTTP 클라이언트의 경우 다음과 같은 조치가 가능합니다.
- 리다이렉트 팔로우 비활성화
- HTTPS 이외의 모든 스키마 비활성화
주의해야 할 함정들
리다이렉트 및 아이프레임
사설 리소스(IP 주소 또는 내부 호스트명)에 대한 SSRF를 방어하는 일반적인 방법은 사용자가 제공한 URL을 파싱한 뒤 '거부 목록(deny-list)'을 사용하여 민감한 리소스에 대한 접근을 차단하는 것입니다.
하지만 이 방법은 대부분의 경우 효과적이지 않다는 점에 유의해야 합니다. 클라이언트가 HTTP 리다이렉트, HTML/JavaScript 리다이렉트를 따르거나 아이프레임(iframe)과 같은 복잡한 요소를 렌더링할 수 있는 환경에서는 우회될 수 있기 때문입니다.
공격자는 취약한 애플리케이션에 URL을 제공할 수 있습니다. 이 URL은 HTTP 301/302 리다이렉트, HTML 메타 리다이렉트, 로드 시 자바스크립트를 통한 URL 변경, 또는 내부 리소스를 보여주는 아이프레임 삽입 등을 통해 민감한 리소스로 연결되는 페이지를 호스팅합니다. 이는 원래의 URL 검증 시도를 효과적으로 우회합니다.
DNS 리바인딩
또 다른 "유형"의 리다이렉트는 DNS를 통해서도 수행될 수 있습니다. SSRF 공격을 방지하기 위한 일반적인 방법 중 하나는 다음과 같습니다.
- 제공된 URL 파싱
- 호스트 이름을 가져와 DNS 조회 수행
- 내부/사설 IP로 확인되면 URL 거부, 공인 IP이면 URL 허용
이 방법은 "DNS 리바인딩"에 취약하기 때문에 사실상 효과적이지 않습니다. DNS 리바인딩은 원격 호스트에 의해 TCP 연결이 종료될 때 대부분의 네트워크 스택(Linux 및 Windows 등)이 보이는 표준 동작 때문에 가능합니다.
원격 호스트가 강제로 연결을 종료하면, IP 주소를 다시 확인하기 위해 DNS 쿼리를 다시 수행한 후 재연결을 시도합니다.
이를 통해 공격자는 다음과 같은 작업을 수행할 수 있습니다:
- `rebinding.attacker.com`에 대해 매우 짧은 TTL(Time-to-live)을 가진, HTTP 포트가 열려 있는 공인 IP 주소로 DNS 항목 생성
- 취약한 애플리케이션에 URL(예: https://rebinding.attacker.com/) 제출. 애플리케이션은 확인된 IP가 사설 IP가 아님을 확인하고, 안전하다고 판단하여 요청된 작업을 진행
- HTTP 연결이 이루어지면 “rebinding.attacker.com”에 대한 DNS 항목을 내부 IP 주소로 변경
- HTTP 연결을 강제로 종료
- 이로 인해 애플리케이션은 “rebinding.attacker.com”의 IP 주소를 다시 확인하게 되며, 이제 내부 IP 주소를 가리키게 됨
- IP 확인 보호 조치는 이미 완료되었고, DNS 항목의 재확인 및 재연결은 모두 커널에서 발생하므로 애플리케이션은 IP가 변경되었다는 사실을 인지하지 못함
이는 사설 IP 주소로 확인되는 URL 제공을 차단하는 보호 조치를 효과적으로 우회합니다.
IPv4 대 IPv6
‘거부 목록(deny-list)’을 우회하는 또 다른 일반적인 방법은 IPv6를 사용하는 것입니다. 모든 IPv4 주소는 IPv6 주소로 표현될 수 있기 때문에, 접근하려는 IPv4 주소에 매핑되는 IPv6 주소를 사용하여 거부 목록을 우회하는 경우가 많습니다.
실무에 바로 적용하는 보안 코딩
OWASP Top 10과 같은 소프트웨어 취약점을 이해하고 완화하기 위한 보안 코딩 가이드라인을 살펴보고, 실제 앱 시뮬레이션을 통해 실습할 수 있는 가이드형 교육 미션을 시작해 보세요.