HTTP 응답 헤더는 응답 분할 공격에 취약하지 않아야 합니다
설명
HTTP 응답 분할(HTTP Response Splitting) 공격은 공격자가 HTTP 응답 헤더에 개행 문자를 주입하여, 원래의 응답과 추가적인 임의의 HTTP 응답을 분리하는 공격 기법입니다. 프레임워크나 서버가 해당 문자를 허용하면 XSS(Cross-Site Scripting), 캐시 오염(Cache Poisoning) 등으로 이어질 수 있습니다. Django와 Werkzeug는 헤더의 개행을 거부하므로 아래 검증 없는 예제가 곧바로 응답 분할을 허용하는 것은 아니며, 잘못된 입력은 예외를 일으킬 수 있습니다.
잠재적 영향
- XSS (Cross-Site Scripting): 악의적인 스크립트가 삽입되어 클라이언트 측에서 실행될 수 있습니다.
- 캐시 오염(Cache Poisoning): 공격자가 악의적인 데이터를 캐시에 저장하여, 이후의 사용자들이 잘못된 데이터를 받을 수 있습니다.
- 중요 데이터 노출: 악의적인 응답으로 인해 중요한 데이터가 노출될 수 있습니다.
해결 방법
- 입력값 검증: 응답 헤더에 삽입되는 모든 입력값에 대해 개행 문자(
\r,\n)를 필터링합니다. - 헤더 설정 API 사용: 프레임워크 API로 헤더를 설정하고 개행 문자가 거부되는지 확인합니다.
예시
Django
변경 전
python
# Django without application input validation
from django.http import HttpResponse
def unsafe_view(request):
user_input = request.GET.get('input', '')
response = HttpResponse()
response['X-My-Header'] = user_input
return response
변경 후
python
# Django with newline removal
from django.http import HttpResponse
import re
def safe_view(request):
user_input = request.GET.get('input', '')
sanitized_input = re.sub(r'[\r\n]', '', user_input)
response = HttpResponse()
response['X-My-Header'] = sanitized_input
return response
설명:
- 변경 전: 입력을 그대로 헤더에 전달합니다. 개행 허용 여부에 따라 오류나 헤더 삽입 위험이 생길 수 있습니다.
- 변경 후: 헤더에 넣기 전에 개행 문자(
\r,\n)를 제거합니다. 실제 헤더의 용도에 맞는 값 검증도 필요합니다.
Flask
변경 전
python
# Flask without application input validation
from flask import Flask, request, Response
app = Flask(__name__)
@app.route('/unsafe')
def unsafe():
user_input = request.args.get('input', '')
response = Response()
response.headers['X-My-Header'] = user_input
return response
변경 후
python
# Flask with newline removal
from flask import Flask, request, Response
import re
app = Flask(__name__)
@app.route('/safe')
def safe():
user_input = request.args.get('input', '')
sanitized_input = re.sub(r'[\r\n]', '', user_input)
response = Response()
response.headers['X-My-Header'] = sanitized_input
return response
설명:
- 변경 전: 입력을 그대로 헤더에 전달합니다. 개행 허용 여부에 따라 오류나 헤더 삽입 위험이 생길 수 있습니다.
- 변경 후: 헤더에 넣기 전에 개행 문자(
\r,\n)를 제거합니다. 실제 헤더의 용도에 맞는 값 검증도 필요합니다.
FastAPI
변경 전
python
# FastAPI without application input validation
from fastapi import FastAPI, Request, Response
app = FastAPI()
@app.get("/unsafe")
async def unsafe(request: Request):
user_input = request.query_params.get('input', '')
response = Response()
response.headers['X-My-Header'] = user_input
return response
변경 후
python
# FastAPI with newline removal
from fastapi import FastAPI, Request, Response
import re
app = FastAPI()
@app.get("/safe")
async def safe(request: Request):
user_input = request.query_params.get('input', '')
sanitized_input = re.sub(r'[\r\n]', '', user_input)
response = Response()
response.headers['X-My-Header'] = sanitized_input
return response
설명:
- 변경 전: 입력을 그대로 헤더에 전달합니다. 개행 허용 여부에 따라 오류나 헤더 삽입 위험이 생길 수 있습니다.
- 변경 후: 헤더에 넣기 전에 개행 문자(
\r,\n)를 제거합니다. 실제 헤더의 용도에 맞는 값 검증도 필요합니다.