SSRF 취약점이란? 서버가 내부 주소로 요청하게 되는 원리

SSRF 취약점이 발생하는 원인과 서버가 내부 주소로 요청을 보내는 과정, 취약한 Java 코드 및 URL 검증·네트워크 차단 방법을 알아봅니다.

웹사이트에는 사용자가 입력한 URL의 내용을 서버가 대신 가져오는 기능이 있다. 게시물에 포함된 링크의 미리보기 생성, 외부 이미지 등록, 웹훅 전송, 원격 파일 다운로드와 같은 기능이 대표적이다.

정상적인 사용자는 공개된 웹사이트 주소를 입력하지만 공격자는 서버만 접근할 수 있는 내부 주소를 입력할 수 있다. 애플리케이션이 목적지를 제대로 검사하지 않으면 서버는 공격자가 지정한 내부 시스템으로 요청을 보내게 된다. 이러한 보안 문제를 SSRF 취약점이라고 한다.

SSRF 취약점이란?

SSRF는 ‘Server-Side Request Forgery’의 약자로, 우리말로는 서버 측 요청 위조라고 한다. 공격자가 웹 애플리케이션의 요청 기능을 조작해 서버가 의도하지 않은 주소로 접속하도록 만드는 취약점이다.

일반 사용자는 기업의 내부 관리 페이지나 서버 전용 API에 직접 접근할 수 없다. 방화벽이나 접근통제 정책이 외부 연결을 차단하고 있기 때문이다. 하지만 웹 서버는 서비스 운영을 위해 내부 데이터베이스, 인증 서버, 관리 API와 통신할 수 있다.

공격자는 이러한 네트워크 차이를 이용한다. 자신이 내부 주소로 직접 접속하는 대신 웹 서버에 주소를 전달하고, 웹 서버가 대신 요청하도록 만드는 것이다.

SSRF 취약점의 핵심은 외부 입력값이 서버의 요청 목적지를 결정한다는 점이다.

SSRF 취약점이 발생하는 원인

SSRF는 사용자가 입력한 URL을 검증하지 않고 HTTP 요청 함수에 전달할 때 주로 발생한다. 링크 미리보기, 이미지 가져오기, PDF 변환, 웹훅 등록 기능처럼 서버가 외부 주소에 접속하는 기능에서 발견될 가능성이 크다.

단순히 localhost라는 문자열만 차단하는 방식도 안전하지 않다. 내부 주소는 IP 주소, 호스트 이름, IPv6 등 여러 형태로 표현될 수 있기 때문이다. 처음에는 외부 주소였지만 리다이렉트를 따라간 뒤 내부 주소로 이동할 수도 있다.

도메인 이름을 허용하더라도 실제 DNS 조회 결과가 사설 IP나 링크 로컬 주소로 연결될 수 있다. 애플리케이션이 입력된 문자열만 확인하고 실제 접속 IP를 검사하지 않으면 필터가 예상과 다르게 동작할 수 있다.

사용자에게 전체 URL 입력을 허용하면서 프로토콜을 제한하지 않는 것도 원인이 된다. 일반적인 웹 기능이라면 HTTP와 HTTPS만 필요하지만, 불필요한 프로토콜까지 처리하면 서버 내부 파일이나 다른 서비스에 접근할 가능성이 생긴다.

SSRF에 취약한 Java 코드

다음은 사용자가 입력한 URL의 내용을 가져와 반환하는 Java 코드다.

@GetMapping("/preview")
public String preview(@RequestParam String url) {

    RestTemplate restTemplate = new RestTemplate();

    return restTemplate.getForObject(
        url,
        String.class
    );
}

이 코드는 사용자가 입력한 url 값을 아무런 검증 없이 getForObject()에 전달한다. 정상적인 웹사이트 주소가 입력되면 해당 페이지의 응답을 가져오지만, 내부 IP 주소나 서버 전용 주소가 입력돼도 그대로 요청할 수 있다.

응답 내용까지 사용자에게 반환하므로 내부 관리 페이지나 서버 정보가 외부에 노출될 가능성도 있다. 응답을 화면에 보여주지 않더라도 서버에서 요청 자체가 발생한다면 Blind SSRF 공격에 이용될 수 있다.

코드에서 확인해야 할 핵심은 RestTemplate, HttpClient, URLConnection과 같은 HTTP 요청 함수에 외부 입력값이 직접 전달되는지 여부다.

서버가 내부 주소로 요청하게 되는 원리

SSRF 공격자는 취약한 기능에 외부 웹사이트가 아닌 내부 시스템의 주소를 입력한다. 애플리케이션은 이 값을 정상적인 URL로 판단하고 서버에서 요청을 생성한다.

요청은 외부 사용자의 컴퓨터가 아니라 웹 서버에서 출발한다. 내부 시스템 입장에서는 인터넷의 공격자가 보낸 요청이 아니라 같은 네트워크에 있는 웹 서버가 보낸 요청처럼 보인다.

내부 서비스가 “외부에서는 접속할 수 없다”는 이유로 별도의 인증을 적용하지 않았다면 문제가 커질 수 있다. 웹 서버를 통해 내부 관리 기능이 호출되거나 민감한 정보가 조회될 수 있기 때문이다.

클라우드 환경에서는 서버에서만 접근할 수 있는 메타데이터 서비스도 주의해야 한다. 메타데이터에는 시스템 구성이나 임시 인증 정보가 포함될 수 있으므로 애플리케이션 서버가 해당 주소에 접근하지 못하도록 별도의 제한이 필요하다.

SSRF 공격으로 발생할 수 있는 피해

SSRF 공격이 성공하면 외부에서는 보이지 않던 내부 서버와 API가 노출될 수 있다. 공격자는 서버의 응답 내용이나 응답 시간 차이를 이용해 내부 네트워크 구조와 서비스 상태를 추측할 수 있다.

내부 관리 페이지가 인증 없이 동작한다면 설정 조회나 변경 요청으로 이어질 수 있다. 클라우드 메타데이터에서 인증 정보가 노출되면 다른 저장소나 서비스에 접근하는 데 악용될 가능성도 있다.

웹 서버가 받아온 응답을 사용자에게 그대로 보여주는 구조라면 내부 문서나 설정 정보가 직접 유출될 수 있다. 응답을 보여주지 않는 Blind SSRF도 안전한 것은 아니다. 요청 도착 여부와 처리 시간 등을 통해 내부 서비스의 존재를 확인할 수 있기 때문이다.

SSRF가 항상 서버 명령 실행으로 이어지는 것은 아니다. 그러나 접근 대상인 내부 서비스에 별도의 취약점이 존재하면 정보 유출이나 시스템 침해로 피해가 확대될 수 있다.

SSRF를 예방하는 안전한 Java 코드

가장 안전한 방법은 사용자가 전체 URL을 입력하지 못하도록 하는 것이다. 서버에서 허용할 외부 서비스 주소를 미리 등록하고 사용자는 서비스 식별자만 선택하도록 구현할 수 있다.

private static final Map<String, URI> ALLOWED_SERVICES =
        Map.of(
            "weather",
            URI.create("https://api.example.com/weather"),
            "news",
            URI.create("https://api.example.com/news")
        );

@GetMapping("/preview")
public String preview(@RequestParam String service) {

    URI target = ALLOWED_SERVICES.get(service);

    if (target == null) {
        throw new IllegalArgumentException(
            "허용되지 않은 서비스입니다."
        );
    }

    RestTemplate restTemplate = new RestTemplate();

    return restTemplate.getForObject(
        target,
        String.class
    );
}

이 코드는 사용자가 URL을 직접 전달하지 않는다. 사용자는 weathernews처럼 서버에서 정한 식별자만 선택할 수 있고, 실제 요청 주소는 서버 내부의 허용 목록에서 결정된다.

사용자 입력값이 HTTP 요청의 호스트와 프로토콜을 변경할 수 없으므로 임의의 내부 주소로 요청이 전송될 가능성을 크게 줄일 수 있다.

실제 운영 코드에서는 연결 시간과 응답 시간을 제한하고, 최대 응답 크기도 설정해야 한다. 서버가 받은 원본 응답을 그대로 사용자에게 보여주기보다 업무에 필요한 데이터만 추출해 제공하는 것이 안전하다.

URL 입력이 필요한 경우의 검증 방법

서비스 특성상 사용자가 URL을 직접 입력해야 한다면 표준 URL 파서를 사용해 프로토콜, 호스트, 포트와 사용자 정보 영역을 분리한 뒤 검증해야 한다. 문자열이 특정 값으로 시작하는지만 확인하는 방식은 피해야 한다.

일반적인 웹 콘텐츠 수집 기능이라면 httphttps만 허용한다. 업무상 필요한 도메인과 포트가 정해져 있다면 허용 목록을 적용하고, URL에 사용자 정보 영역이나 예상하지 못한 포트가 포함된 경우 요청을 거부하는 것이 좋다.

도메인 이름은 DNS 조회 후 실제 IP 주소를 확인해야 한다. 조회 결과가 루프백, 사설 네트워크, 링크 로컬, 멀티캐스트 또는 예약 주소라면 요청을 차단한다. IPv4뿐 아니라 IPv6도 함께 검사해야 한다.

URL 리다이렉트는 가능하면 허용하지 않는 것이 안전하다. 반드시 필요한 경우에는 최초 URL만 확인하지 말고 이동할 때마다 새로운 주소와 DNS 조회 결과를 다시 검증해야 한다.

네트워크 수준의 접근 차단

애플리케이션에서 URL을 검사하더라도 SSRF 우회 가능성이 완전히 사라지는 것은 아니다. 웹 서버가 업무상 필요하지 않은 내부망이나 외부 주소로 연결하지 못하도록 방화벽과 보안 그룹에서도 통신 범위를 제한해야 한다.

외부 API 연동이 필요한 서버는 지정된 프록시를 통해서만 외부 요청을 보내도록 구성할 수 있다. 프록시에서 허용된 도메인과 포트만 연결하도록 제한하면 애플리케이션 검증이 우회됐을 때도 요청을 다시 차단할 수 있다.

클라우드 메타데이터 주소와 내부 관리망은 웹 애플리케이션 서버에서 직접 접근하지 못하도록 분리하는 것이 좋다. SSRF는 서버의 네트워크 권한을 이용하는 취약점이므로 최소 권한과 네트워크 분리가 중요한 대응 방법이다.

요청 시간과 응답 크기 제한

공격자는 응답이 지나치게 크거나 연결이 오랫동안 유지되는 주소를 입력해 서버 자원을 소모시킬 수도 있다. 따라서 연결 시간, 읽기 시간, 전체 처리 시간과 최대 응답 크기를 제한해야 한다.

리다이렉트 횟수도 제한하고, 예상한 콘텐츠 형식이 아닌 응답은 즉시 중단하는 것이 좋다. 이미지 수집 기능이라면 실제 이미지 형식인지 확인하고 필요한 크기로 다시 처리해야 한다.

웹훅처럼 응답 본문이 필요하지 않은 기능에서는 전체 내용을 저장하거나 사용자에게 반환하지 말고 상태 코드 등 필요한 결과만 처리한다.

SSRF 취약점 점검 항목

SSRF를 점검할 때는 URL, 호스트, IP 주소를 입력받는 모든 기능을 찾아야 한다. 링크 미리보기나 이미지 수집처럼 겉으로 드러나는 기능뿐 아니라 관리자 페이지의 웹훅 설정과 외부 API 연동 기능도 확인할 필요가 있다.

소스코드에서는 RestTemplate, HttpClient, URLConnection, WebClient처럼 외부 요청을 보내는 함수를 검색한 뒤 URL을 구성하는 값이 어디에서 들어오는지 추적한다. 사용자가 조작할 수 있는 파라미터, 쿠키, HTTP 헤더 또는 데이터베이스 값이 요청 목적지에 영향을 주는지 확인해야 한다.

허용 프로토콜과 도메인이 정해져 있는지, DNS 조회 결과에서 내부 IP를 차단하는지, IPv6와 리다이렉트 주소까지 검사하는지도 점검한다. 웹 서버의 내부망 접근 범위와 비정상적인 외부 요청 로그도 함께 확인해야 한다.

자주 묻는 질문

SSRF와 CSRF는 같은 취약점인가요?

같은 취약점이 아니다. CSRF는 로그인한 사용자의 브라우저가 의도하지 않은 요청을 보내도록 만드는 공격이다. SSRF는 웹 애플리케이션 서버가 공격자가 지정한 주소로 요청을 보내도록 만드는 공격이다.

localhost 문자열만 차단하면 SSRF를 막을 수 있나요?

충분하지 않다. 내부 주소는 IP 주소와 IPv6 등 여러 형태로 표현될 수 있으며, 도메인의 DNS 조회 결과가 내부 IP로 연결될 수도 있다. 실제 접속 IP와 리다이렉트 주소까지 확인해야 한다.

응답 내용을 화면에 표시하지 않으면 안전한가요?

안전하다고 볼 수 없다. 응답이 보이지 않더라도 서버에서 요청이 발생하면 Blind SSRF에 악용될 수 있다. 네트워크 요청 자체를 허용된 목적지로 제한해야 한다.

외부 URL 입력이 반드시 필요하면 어떻게 해야 하나요?

허용할 프로토콜, 도메인과 포트를 최소화하고 표준 URL 파서를 이용해 주소를 검증해야 한다. DNS 조회 결과와 리다이렉트 목적지를 확인하고, 방화벽에서도 내부 주소 접근을 차단해야 한다.

마무리

SSRF 취약점은 사용자가 입력한 URL을 서버가 그대로 요청할 때 발생한다. 공격자는 서버의 네트워크 위치를 이용해 외부에서는 접근할 수 없는 내부 시스템이나 서버 전용 서비스에 요청을 보내려고 시도한다.

가장 안전한 방법은 사용자가 전체 URL을 지정하지 못하게 하고, 서버에 등록된 허용 주소만 사용하도록 구성하는 것이다. URL 입력이 필요하다면 프로토콜, 도메인, 포트와 DNS 조회 결과를 검사하고 리다이렉트도 제한해야 한다.

SSRF 방어의 핵심은 외부 입력값이 서버의 요청 목적지를 자유롭게 결정하지 못하게 하는 것이다. 애플리케이션의 URL 검증과 네트워크 접근통제를 함께 적용해야 내부 주소로 전송되는 비정상적인 요청을 효과적으로 차단할 수 있다.

핵심 키워드: SSRF 취약점, 서버 측 요청 위조, 내부 주소 요청, SSRF 공격 원리, URL 검증, 내부망 접근 차단, Blind SSRF, SSRF 대응 방법

댓글 남기기