
SSRF: 서버 측 요청 위조
SSRF 공격의 원리와 방어 방법을 경험을 통해 이해한 과정

SSRF 공격의 원리와 방어 방법을 경험을 통해 이해한 과정
HTTP로 접속하는 사용자를 단순히 HTTPS로 리다이렉트하는 것만으로는 부족합니다. 중간자 공격(MITM)에 취약하기 때문이죠. 브라우저에게 '이 사이트는 무조건 HTTPS로만 접속해!'라고 각인시키는 HSTS(HTTP Strict Transport Security)의 원리와 적용 방법을 정리합니다.

어느 날 내 서비스가 낯선 도메인의 iframe 안에서 돌고 있는 걸 발견했습니다. Clickjacking 공격을 막기 위해 HSTS, X-Frame-Options, CSP, Permissions-Policy 등 필수 보안 헤더 6가지를 상세히 설명하고, Nginx와 Next.js에 적용하는 방법을 공유합니다.

서비스 런칭 3일 차, 갑자기 DB CPU가 100%를 찍었습니다. 로그를 보니 SQL Injection 공격이 들어오고 있었죠. 급하게 AWS WAF를 도입하여 공격을 차단하고, 더 나아가 Rate Limiting으로 DDoS까지 방어한 경험담입니다. Positive/Negative 보안 모델과 OWASP CRS의 개념도 함께 다룹니다.

고등학교 시절, 친구의 웹사이트를 'admin' --' 한 줄로 뚫었던 경험과 그 충격을 공유합니다. 웹 개발자가 반드시 알아야 할 10가지 보안 취약점(OWASP Top 10)과 이를 막기 위해 '해커처럼 생각하는 법'을 4가지 핵심 위협(Injection, IDOR, Crypto, Misconfig)을 중심으로 설명합니다.

SSRF (Server-Side Request Forgery)는 서버를 속여서, 공격자가 직접 접근할 수 없는 내부망(Internal Network)의 자원에 접근하게 만드는 공격입니다.
보통 해커는 방화벽 밖에서 공격하지만, SSRF는 "서버가 서버를 공격하게" 만듭니다. 마치 은행원이 보이스피싱에 속아서, 금고 문을 직접 열어주는 것과 같습니다.
많은 개발자들이 "클라우드 썼으니까 안전하겠지"라고 생각하지만, SSRF는 클라우드 환경에서 더 치명적입니다.
AWS EC2 인스턴스는 169.254.169.254라는 특수한 IP를 통해 자신의 정보를 조회할 수 있습니다.
문제는 여기에 IAM Role 자격 증명(Access Key, Secret Key)이 포함되어 있다는 점입니다.
# 공격자가 웹 애플리케이션에 이런 요청을 보냄
GET /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/admin-role
만약 웹 서버가 이 요청을 그대로 실행해서 결과를 공격자에게 보여준다면? 공격자는 서버의 관리자 권한을 탈취하게 됩니다. 그 뒤로는 S3 버킷을 다 지우거나, 채굴기를 돌리는 등 마음대로 할 수 있습니다.
역대급 개인정보 유출 사건으로 꼽히는 Capital One 해킹도 SSRF가 원인이었습니다.
169.254.169.254)에 접근했습니다.이 사건 이후로 AWS는 보안을 강화한 IMDSv2를 내놓았습니다. (세션 토큰 필수).
Java, Python, Node.js 등 언어별로 방어 코드를 짜는 법을 정리해봤다.
Java는 java.net.URL 클래스를 사용할 때 주의해야 합니다. 오픈 소스 라이브러리인 'ssrf-agent'를 사용하는 것이 좋습니다.
// 위험한 코드
URL url = new URL(userInput);
URLConnection conn = url.openConnection();
requests 라이브러리를 많이 쓰지만, 기본적으로 리다이렉트를 따라갑니다. allow_redirects=False 옵션을 꼭 켜세요.
또한 SafeCurl 같은 래퍼 라이브러리를 쓰는 것도 방법입니다.
SSRF는 단순히 "URL 검사"만으로는 막기 어렵습니다. 여러 계층에서 방어해야 합니다.
가장 확실한 방법은 허용 목록(Allow-list)을 쓰는 것입니다.
// ✅ 안전한 코드: 도메인 화이트리스트
const ALLOWED_DOMAINS = ['api.google.com', 'graph.facebook.com'];
function isValidUrl(inputUrl) {
const url = new URL(inputUrl);
return ALLOWED_DOMAINS.includes(url.hostname);
}
블랙리스트(차단 목록)는 우회하기 쉽습니다.
127.0.0.1 차단 -> 2130706433 (십진수), 0x7F000001 (16진수) 등으로 우회 가능.localhost 차단 -> lvh.me, localtest.me (DNS Rebinding) 등으로 우회 가능.애플리케이션이 외부 인터넷으로 나가는 트래픽(Outbound Traffic)을 제어해야 합니다.
요청을 보냈더라도, 응답값을 사용자에게 그대로 보여주지 마세요. 응답의 Content-Type이나 포맷이 예상한 것인지 확인해야 합니다.
내 서버가 취약한지 어떻게 알 수 있을까요? 다음 도구들을 사용해 보세요.
Q. fetch 말고 Axios나 Request 라이브러리는 안전한가요?
A. 아니요. 라이브러리 문제가 아니라, 로직 문제입니다. 어떤 HTTP 클라이언트를 쓰든, 사용자가 입력한 URL을 검증 없이 요청하면 취약합니다. Axios도 기본적으로 리다이렉트를 따라갑니다.
Q. 리다이렉트(Redirect)는 괜찮나요?
A. 위험합니다. http://safe.com으로 요청을 보냈는데, 그 서버가 http://internal-server로 리다이렉트 응답(302)을 줄 수 있습니다. 많은 HTTP 라이브러리가 리다이렉트를 자동으로 따라갑니다. 옵션에서 fail on redirect를 설정하거나, 리다이렉트 된 최종 URL도 검사해야 합니다.
Q. DNS Rebinding 공격이 뭔가요?
A. 처음엔 evil.com이 1.2.3.4(외부 IP)를 가리키다가, 검증을 통과한 직후(Time-of-Check to Time-of-Use), 127.0.0.1(내부 IP)로 DNS 응답을 바꾸는 공격입니다.
방어법:
Q. Gopher 프로토콜은 어디에 쓰나요?
A. Gopher는 옛날 프로토콜이지만, 공격자에게는 강력한 무기입니다. 개행 문자(\r\n)를 포함할 수 있어서, Gopher URL 안에 Redis 명령어, SMTP 이메일 전송 명령 등을 숨겨서 보낼 수 있습니다.
사용하지 않는 프로토콜(gopher, ftp 등)은 라이브러리 차원에서 비활성화하세요.