서버사이드 요청 위조(Server-Side Request Forgery, SSRF)는 공격자가 서버가 다른 시스템으로 HTTP 요청을 보내도록 함으로써 이루어지는 공격입니다. 이 공격은 서버의 네트워크를 탐색하거나 내부 자원에 비인가 접근을 시도할 수 있습니다. 이 공격의 가장 흔한 표적은 내부망에 위치한 서비스, 메타데이터 서비스, 그리고 클라우드 기반의 리소스입니다. 이하에서는 SSRF 공격이 어떻게 이루어질 수 있는지와, 이를 방어하기 위한 방법을 설명합니다.
---
**SSRF 공격 예시**
SSRF 공격은 주로 사용자가 입력한 URL을 그대로 요청할 수 있게 하는 기능에서 발생합니다. 예를 들어, 서버는 다음과 같은 코드로 특정 URL의 콘텐츠를 가져와야 할 필요가 있을 수 있습니다:
```python
import requests
def fetch_url_content(url):
response = requests.get(url)
return response.text
# 사용자 입력
user_input = "http://example.com/resource"
fetch_url_content(user_input)
```
공격자는 사용자 입력에 본인의 컨트롤 하에 있는 서버를 입력하여 내부 네트워크의 정보를 수집할 수 있습니다:
```bash
user_input = "http://169.254.169.254/latest/meta-data/"
```
위 예시에서 클라우드 메타데이터 서비스를 공격해서 EC2 인스턴스의 메타데이터 접근을 시도할 수 있습니다.
---
**SSRF 방어 방법**
1. **허용된 목록(Allowlist)을 사용한 URL 필터링**
외부 서비스에 대한 요청을 제한합니다. 예를 들어, 사전에 허용된 도메인 목록을 사용하여 요청을 필터링합니다.
```python
import requests
ALLOWED_DOMAINS = ["api.example.com", "service.example.com"]
def is_url_allowed(url):
from urllib.parse import urlparse
hostname = urlparse(url).hostname
return hostname in ALLOWED_DOMAINS
def fetch_url_content(url):
if not is_url_allowed(url):
raise ValueError("URL not allowed.")
response = requests.get(url)
return response.text
```
2. **RFC 1918 비공개 IP 주소 차단**
내부 네트워크로의 요청을 방지하기 위해 RFC 1918 비공개 IP 주소 범위를 차단합니다.
```python
import ipaddress
import requests
from urllib.parse import urlparse
def is_private_ip(ip):
return ipaddress.ip_address(ip).is_private
def fetch_url_content(url):
hostname = urlparse(url).hostname
ip = requests.get(f"https://api.ipify.org?format=json&hostname={hostname}").json().get('ip')
if is_private_ip(ip):
raise ValueError("Internal IP not allowed.")
response = requests.get(url)
return response.text
```
3. **RESTful API 구축 시 HTTP 메소드 검증**
GET, POST 등 의도된 HTTP 메소드만 허용하여 의도치 않은 요청 실행을 방지합니다.
```python
from flask import Flask, request, abort
app = Flask(__name__)
@app.route('/fetch', methods=['POST'])
def fetch():
if request.method != 'POST':
abort(405) # Method Not Allowed
url = request.form.get('url')
# URL validation logic here
return fetch_url_content(url)
```
위와 같이 SSRF로부터 서버를 보호하기 위해 여러 방법을 결합하여 방어 체계를 강화할 수 있습니다. 항상 입력값을 조사하고, 외부 요청에 대한 엄격한 제한을 두는 것이 중요합니다.