우아한테크코스 테코톡
꾹이의 SSRF
카테고리 : 우아한테크코스 테코톡
https://youtu.be/wYIdOgrSW0E?si=bDIvHOkK9l_2fvMI
— 서버가 대신 요청하는 순간, 공격이 시작된다
이 글은 꾹이의 SSRF 영상을 보고 정리한 내용에 실무 관점에서 왜 위험한지, 왜 자주 터지는지를 보강한 글이다.
SSRF는 화려한 해킹 기법이 아니라, “서버가 너무 친절할 때” 발생하는 취약점이다.
꾹이의 SSRF
OWASP
- OWASP(Open Worldwide Application Security Project)는 애플리케이션 보안을 연구·전파하는 비영리 국제 단체
OWASP에서 발표하는 자료 중 가장 널리 알려진 것이 OWASP TOP 10
- OWASP TOP 10은 약 4~5년 주기로 발표
- 실제 공격 통계 + 보안 전문가 의견을 바탕으로 가장 위험하고 빈번한 취약점 10가지를 정리
OWASP TOP 10 (2021)
- 부적절한 인가
- 암호화 실패
- Injection (삽입 공격)
- 안전하지 않은 설계
- 보안 설정 미흡
- 취약하고 업데이트되지 않은 컴포넌트 사용
- 식별 및 인증 실패
- 소프트웨어 및 데이터 무결성 실패
- 보안 로깅 및 모니터링 실패
- 서버 사이드 요청 위조 (SSRF)
👉 SSRF는 특히 클라우드 환경에서 위험도가 급격히 올라간다.
SSRF
- SSRF(Server-Side Request Forgery)는 공격자가 서버가 대신 요청을 하도록 조작하는 공격
중요한 포인트는 이거다.
공격자는 직접 내부망에 접근하지 않는다 서버의 권한을 빌려 접근한다
SSRF 공격 흐름
- 공격자는 먼저 프라이빗 서브넷에 접근하려고 한다
- 이 서브넷은 외부 인터넷에서 직접 접근할 수 없다
- 공격자는 악의적인 요청을 보내 취약 서버가 대신 요청하도록 유도
- 취약 서버는 내부 데이터 서버로 요청
- 데이터 서버는 “신뢰된 서버 요청”이라 판단하고 응답
- 응답은 다시 공격자에게 전달된다
루프백 공격
127.0.0.1,localhost를 이용- 취약 서버가 자기 자신에게 요청
- 내부 관리자 API, 설정 정보 노출 가능
👉 신뢰 경계를 우회하는 구조가 SSRF의 본질이다.
어떠한 요청이 서버로 하여금 대신 요청하게 만드는가
SSRF는 “특별한 공격 요청”이 아니라 취약한 코드 설계에서 시작된다.
SSRF 취약점 예시 코드
@GetMapping("/fetch-data")
public ResponseEntity<String> fetchData(@RequestParam String url) {
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
return ResponseEntity.ok(response);
}
url파라미터는 외부 입력값- 서버는 이 값을 검증 없이 사용
- 공격자는 내부 네트워크 주소를 주입 가능
공격 예시
http://localhost:8080/adminhttp://127.0.0.1:8080/internalhttp://169.254.169.254/latest/meta-data/
클라우드 환경에서 SSRF가 특히 위험한 이유
인스턴스 메타데이터 공격
http://169.254.169.254/latest/meta-data/- EC2 인스턴스 메타데이터 조회 주소
이 주소를 통해 노출될 수 있는 정보:
- IAM Role
- Security Credentials
- Public IP
- Security Group
- 경우에 따라 S3 접근 권한
👉 SSRF = 클라우드 권한 탈취의 출발점
AWS뿐 아니라:
- GCP
- Azure 모두 유사한 메타데이터 서비스가 존재한다.
SSRF 공격 대응 방안
블랙 리스트
- 예외적으로 차단할 대상을 지정하는 방식
차단 권장 항목
- IP 주소
- 도메인
- URL 패턴
- 호스트 이름
- 포트 번호
- 프로토콜
- IP 대역
@GetMapping("/fetch-data")
public ResponseEntity<String> fetchData(@RequestParam String url) {
if (isBlacklisted(url)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body("Access to this URL is forbidden.");
}
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
return ResponseEntity.ok(response);
}
블랙리스트의 한계
- “알고 있는 위험”만 막을 수 있다
- 우회 경로가 존재
블랙리스트 회피 방식 (리다이렉트)
- 공격자는 중간 서버를 준비
- 취약 서버는 공격자 서버로 요청
- 공격자 서버는 302 Redirect로 내부 주소 반환
- 취약 서버가 리다이렉트를 따라가며 내부 접근
// 공격자가 예시로 이렇게 취약 서버에 이렇게 요청 com?url=http://attacker.com/redirect
@GetMapping("/redirect")
public ResponseEntity<Void> redirectToMetadata() {
HttpHeaders headers = new HttpHeaders();
headers.setLocation(
URI.create("http://169.254.169.254/latest/meta-data/")
);
return ResponseEntity.status(HttpStatus.FOUND)
.headers(headers)
.build();
}
👉 리다이렉트 차단은 필수
화이트 리스트
- 명시적으로 허용된 요청만 통과
- 보안 수준은 상대적으로 높음
@GetMapping("/fetch-data")
public ResponseEntity<String> fetchData(@RequestParam String url) {
if (!isSafe(url)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body("Access to this URL is forbidden.");
}
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
return ResponseEntity.ok(response);
}
화이트리스트 회피 — DNS Rebinding
- 공격자는 자신의 DNS 서버 사용
- 최초 DNS 응답 → 정상 IP (TTL 매우 짧음)
- 서버는 검증 통과
- TTL 만료
- 재조회 시 내부 IP 반환
- 서버는 내부 서비스로 요청
👉 검증 시점과 요청 시점의 IP 불일치가 공격 포인트
IMDSv2 (AWS 기준)
- Instance Metadata Service Version 2
- EC2 기본 설정이 IMDSv1(Optional)인 경우 SSRF에 취약
IMDSv1
- GET 요청만으로 메타데이터 조회 가능
IMDSv2
- PUT 요청으로 토큰 발급
- 토큰을 헤더에 포함해야 접근 가능
- 단순 GET 기반 SSRF 방어 효과 큼
Metadata Hop Limit
- 요청이 거칠 수 있는 네트워크 hop 제한
- 기본값: 1
공격자 서버 → 취약 서버 → 메타데이터 → hop 초과로 차단
👉 리다이렉트 기반 SSRF 방어 가능
코드를 설계할 때 반드시 인지해야 할 점
- 외부 입력으로 URL을 받는 순간 SSRF 가능성 존재
- HTTP Client(
RestTemplate,WebClient) 사용 시 항상 의심 - IDE 경고, SAST 분석 결과 무시하지 않기
- “이 요청은 정말 서버가 해야 하는가?” 질문하기
정리
- SSRF는 서버의 권한을 악용하는 공격
- 단일 방어책은 존재하지 않는다
- 블랙리스트 + 화이트리스트 + 리다이렉트 차단
- 클라우드 환경에서는 IMDSv2 필수
- 무엇보다 설계 단계에서의 인식이 가장 중요
SSRF는 서버가 너무 믿어준 결과다.