본문 바로가기
홍TV 홍TV

‘SSRF’, URL 하나 받았는데 서버 내부가 위험해진다

홍TV 읽는 시간 약 11분
4.5
(300)

들어가며

웹 서비스를 개발하다 보면 서버가 다른 서버에 요청을 보내야 하는 경우가 있습니다.

외부 API를 호출하거나, 이미지 URL을 받아 서버에서 다운로드하거나, 사용자가 입력한 URL의 내용을 가져오는 기능이 대표적입니다.

예를 들어 이런 기능이 있다고 생각해 보겠습니다.

이미지 URL을 입력하세요.
https://example.com/image.jpg

서버는 사용자가 입력한 URL로 접속해서 이미지를 가져온 다음 화면에 보여줍니다.

개발자 입장에서는 상당히 평범한 기능입니다.

그런데 여기서 한 가지 질문이 생깁니다.

“사용자가 입력한 URL이라면, 꼭 외부 인터넷 주소만 입력할까?”

만약 서버가 사용자가 지정한 주소로 아무런 제한 없이 요청을 보낼 수 있다면 문제가 될 수 있습니다.

공격자가 서버를 이용해서 원래 외부에서 접근할 수 없어야 하는 내부 시스템이나 다른 서버에 요청을 보내도록 유도할 수 있기 때문입니다.

이런 유형의 취약점을 SSRF(Server-Side Request Forgery), 서버 사이드 요청 위조라고 합니다.

SSRF란?

SSRF를 쉽게 설명하면 다음과 같습니다.

공격자가 서버를 속여서 서버 자신이 원하지 않는 곳으로 요청을 보내도록 만드는 공격입니다.

일반적인 요청은 이런 구조입니다.

사용자
 ↓
웹 서버
 ↓
외부 API

그런데 SSRF가 발생하면 다음과 같은 형태가 될 수 있습니다.

공격자
 ↓
웹 서버
 ↓
의도하지 않은 내부 시스템

중요한 부분은 공격자가 직접 내부 시스템에 접속하는 것이 아니라 취약한 서버를 중간에 이용한다는 것입니다.

서버 입장에서는 자신이 요청을 보내는 것이기 때문에 내부 네트워크에서만 접근할 수 있는 주소에 접근할 가능성이 생깁니다.

SSRF는 어디에서 발생할까?

SSRF는 생각보다 특정 기능에만 한정되어 있지 않습니다.

대표적으로 URL을 사용자가 직접 입력하는 기능에서 발생할 수 있습니다.

예를 들어 다음과 같은 기능입니다.

URL 입력
 ↓
서버에서 URL 접속
 ↓
결과 가져오기
 ↓
화면에 출력

실제 서비스에서는 다음과 같은 기능이 해당될 수 있습니다.

  • 이미지 URL 가져오기
  • 웹 페이지 미리보기
  • URL 기반 파일 다운로드
  • 외부 API 연동
  • URL 검사 기능
  • PDF 또는 문서 변환
  • 서버에서 외부 리소스를 가져오는 기능

이런 기능들의 공통점은 서버가 사용자가 지정한 주소로 요청을 보낸다는 것입니다.

따라서 URL을 사용자가 자유롭게 입력할 수 있다면 SSRF 가능성을 한 번쯤 확인해볼 필요가 있습니다.

왜 단순한 URL 요청이 문제가 될까?

가장 중요한 부분입니다.

서버가 인터넷에 있는 모든 주소로 요청할 수 있다면 개발자는 보통 이렇게 생각할 수 있습니다.

https://example.com
https://api.example.com

정상적인 외부 서비스만 호출하면 아무 문제가 없어 보입니다.

하지만 서버가 위치한 네트워크에는 외부 인터넷에서 직접 접근하기 어려운 시스템도 존재할 수 있습니다.

예를 들어 다음과 같은 주소 영역입니다.

localhost
127.0.0.1
내부 사설 IP
내부 DNS 이름
클라우드 내부 서비스 주소

이런 주소에 서버가 접근할 수 있는 환경이라면 공격자가 서버를 통해 내부 시스템에 요청을 전달하려고 시도할 수 있습니다.

즉, SSRF의 핵심은 단순히 “URL을 조작한다”가 아닙니다.

외부 사용자가 서버의 네트워크 접근 권한을 대신 사용하는 상황이 만들어지는 것이 핵심입니다.

SSRF와 일반적인 서버 요청의 차이

예를 들어 웹 서버가 외부 날씨 API를 호출한다고 생각해 보겠습니다.

String url = "https://api.example.com/weather";

HttpURLConnection connection =
    (HttpURLConnection) new URL(url).openConnection();

connection.connect();

개발자가 URL을 코드에 고정해 놓았다면 상대적으로 문제가 단순합니다.

하지만 URL을 사용자 입력으로 받으면 이야기가 달라집니다.

String url = request.getParameter("url");

HttpURLConnection connection =
    (HttpURLConnection) new URL(url).openConnection();

이 코드에서 가장 먼저 확인해야 할 부분은 url입니다.

사용자가 입력한 URL이 검증되지 않은 상태로 서버의 HTTP 요청에 사용되고 있기 때문입니다.

물론 실제 SSRF 취약점 여부는 네트워크 구조와 애플리케이션 구현 등에 따라 달라집니다.

하지만 사용자 입력 → 서버의 HTTP 요청이라는 구조가 보인다면 보안 점검 대상이라고 생각하는 것이 좋습니다.

SSRF를 막으려면 어떻게 해야 할까?

가장 중요한 것은 서버가 요청할 수 있는 목적지를 제한하는 것입니다.

예를 들어 특정 외부 API만 호출해야 하는 기능이라면 사용자가 임의의 URL을 입력하도록 만들 필요가 없습니다.

다음처럼 허용된 서버 목록을 정해두는 방식이 훨씬 안전합니다.

허용

api.example.com
image.example.com

차단

localhost
127.0.0.1
내부 IP
허용되지 않은 외부 도메인

이런 방식을 일반적으로 Allowlist(허용 목록) 방식이라고 합니다.

가능하다면 모든 주소를 허용하고 문제가 되는 주소만 차단하는 방식보다, 정말 필요한 목적지만 허용하는 구조가 관리하기 쉽고 안전합니다.

IP 주소만 검사하면 충분할까?

여기서 또 하나 주의해야 할 부분이 있습니다.

개발자가 흔히 다음과 같이 생각할 수 있습니다.

"127.0.0.1만 차단하면 되겠지."

하지만 SSRF 방어는 단순히 특정 문자열 몇 개를 차단하는 것만으로 끝내기 어렵습니다.

도메인이 실제로 어떤 IP 주소로 해석되는지 확인해야 하고, DNS 해석 과정에서 예상하지 못한 주소로 연결되는 문제도 고려해야 합니다.

또한 IPv4뿐 아니라 IPv6 환경까지 함께 고려할 필요가 있습니다.

따라서 SSRF 방어에서는 URL 파싱, DNS 해석, IP 주소 검증, 리다이렉트 처리 등을 종합적으로 살펴봐야 합니다.

리다이렉트도 조심해야 한다

SSRF를 점검할 때 의외로 놓치기 쉬운 부분이 HTTP Redirect입니다.

예를 들어 서버가 처음 요청한 주소는 허용된 도메인이었다고 해보겠습니다.

그런데 해당 서버가 다른 주소로 리다이렉트하도록 응답한다면 어떻게 될까요?

허용된 URL
 ↓
HTTP Redirect
 ↓
다른 주소

애플리케이션이 리다이렉트를 자동으로 따라가도록 설정되어 있다면 처음 검증한 주소와 실제 요청이 전달되는 최종 목적지가 달라질 수 있습니다.

따라서 SSRF 방어를 할 때는 최종 요청 대상까지 안전하게 검증되는지 확인해야 합니다.

네트워크에서도 방어해야 한다

애플리케이션 코드만 수정한다고 끝나는 문제도 아닙니다.

가능하다면 서버에서 접근할 수 있는 네트워크 자체를 제한하는 것이 좋습니다.

예를 들어 외부 이미지 다운로드 서버라면 내부 데이터베이스 서버나 관리자 시스템 등에 접근할 필요가 없을 수 있습니다.

그렇다면 네트워크 방화벽이나 보안 그룹 등을 이용해서 서버가 불필요한 내부 시스템에 접근하지 못하도록 제한할 수 있습니다.

이렇게 하면 애플리케이션에서 실수가 발생하더라도 피해 범위를 줄일 수 있습니다.

결국 보안에서는 한 가지 방어책에만 의존하지 않는 것이 중요합니다.

애플리케이션 검증
        +
URL / IP 검증
        +
리다이렉트 제한
        +
네트워크 접근 제어

여러 방어 계층을 함께 구성하는 것이 좋습니다.

개발하면서 SSRF를 점검한다면

기존 프로젝트에서 SSRF 취약점을 확인하고 싶다면 먼저 다음과 같은 코드를 찾아보는 것이 좋습니다.

URL url = new URL(userInput);
HttpClient 요청
RestTemplate 요청
WebClient 요청
URLConnection 요청
외부 이미지 다운로드
URL 기반 파일 처리

특히 request.getParameter()나 JSON 요청에서 받은 URL이 HTTP 클라이언트의 요청 주소로 바로 사용되는지 확인해보면 좋습니다.

예를 들어 다음 구조라면 주의해서 살펴봐야 합니다.

String targetUrl = request.getParameter("url");

return restTemplate.getForObject(
    targetUrl,
    String.class
);

이 코드만 보고 실제 취약점이라고 단정할 수는 없지만, 사용자 입력이 서버의 요청 목적지가 되고 있다는 점에서 보안 검토가 필요한 코드입니다.

SSRF를 한 문장으로 이해하기

SSRF를 처음 접했다면 복잡하게 외우기보다 다음 문장 하나를 기억해도 충분합니다.

“사용자가 지정한 URL로 서버가 대신 접속하게 만드는 기능은 SSRF 위험을 확인해야 한다.”

특히 외부 API 연동이나 URL 미리보기 같은 기능을 만들 때 “서버에서 URL을 호출하면 되겠지”라고 쉽게 구현하기 전에 한 번 더 생각해야 합니다.

사용자가 입력한 URL을 그대로 믿어도 되는지, 어디까지 접근을 허용할 것인지, 리다이렉트를 따라가도 되는지, 내부 네트워크에 접근할 수 있는지를 함께 확인해야 합니다.

마무리

SSRF는 코드 몇 줄만 보면 별것 아닌 것처럼 보일 수 있습니다.

하지만 실제 위험은 애플리케이션 코드 자체보다 서버가 위치한 네트워크 환경과 연결되어 있다는 점에 있습니다.

사용자가 입력한 URL을 서버가 대신 요청하는 구조라면 단순한 URL 처리 기능으로 생각하지 말고 보안 관점에서 살펴봐야 합니다.

특히 다음 네 가지는 기억해두면 좋습니다.

1. 사용자 입력 URL을 그대로 신뢰하지 않는다.
2. 가능하면 Allowlist 방식으로 목적지를 제한한다.
3. DNS와 IP 주소, 리다이렉트까지 고려한다.
4. 애플리케이션뿐 아니라 네트워크에서도 접근을 제한한다.

결국 SSRF의 핵심은 “누가 요청을 보내는가?”입니다.

사용자는 단순히 URL 하나를 전달했을 뿐인데, 실제 요청을 보내는 주체가 서버라면 이야기가 달라집니다.

웹 개발에서 URL을 입력받아 서버가 대신 처리하는 기능을 만들고 있다면, 오늘 작성한 코드에 SSRF 위험이 없는지 한 번쯤 확인해보는 것이 좋습니다.

이 게시물이 얼마나 유용했나요?

별점을 클릭하여 평가하세요!

평균 평점 4.5 / 5. 투표 수: 300

아직 투표가 없습니다! 첫 번째로 평가해보세요.

홍TV

홍TV
함께 보면 좋은 글

댓글 0

첫 댓글을 남겨보세요.

error: Content is protected !!

광고 차단 알림

광고 클릭 제한을 초과하여 광고가 차단되었습니다.

단시간에 반복적인 광고 클릭은 시스템에 의해 감지되며, IP가 수집되어 사이트 관리자가 확인 가능합니다.