─━ IT ━─

SSRF 공격 및 방어 방법과 예시 설명

DKel 2024. 11. 3. 19:45
반응형

 
서버사이드 요청 위조(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로부터 서버를 보호하기 위해 여러 방법을 결합하여 방어 체계를 강화할 수 있습니다. 항상 입력값을 조사하고, 외부 요청에 대한 엄격한 제한을 두는 것이 중요합니다.

반응형