크로스 사이트 요청 위조

Cross-Site Request Forgery (CSRF)

설명

크로스 사이트 요청 위조(Cross-Site Request Forgery, CSRF)는 악의적인 웹사이트가 사용자를 속여 다른 사이트에 대해 비정상적인 요청을 보내게 하는 공격입니다. 쿠키 등 브라우저가 자동으로 보내는 인증 정보를 이용해 공격자가 의도한 작업(예: 데이터 변경, 삭제 등)을 수행하게 만듭니다.

잠재적 영향

  • 데이터 변조: 사용자의 의지와 상관없이 데이터가 변경될 수 있습니다.
  • 사용자 권한 악용: 피해자에게 허용된 계정 설정 변경이나 관리 작업이 실행될 수 있습니다.
  • 서비스 악용: 공격자가 서비스 기능을 악용하여 피해를 일으킬 수 있습니다.

해결 방법

  • CSRF 방지 토큰 사용: 모든 상태 변경 요청에 대해 CSRF 방지 토큰을 사용합니다.
  • 출처 검증: 서버에서 Origin 등을 검증하여 신뢰할 수 없는 출처에서의 요청을 차단합니다.
  • 보안 설정 강화: GET 요청으로 상태를 변경하지 말고, HTTPS와 적절한 SameSite 쿠키 설정을 함께 적용합니다.

예시

Django

첫 예시는 CsrfViewMiddleware나 별도의 CSRF 보호가 없는 경우를 가정합니다. 데코레이터가 없다는 사실만으로 보호 부재를 판단할 수는 없습니다.

변경 전

python
# Unsafe Django code
from django.http import HttpResponse

def my_view(request):
    if request.method == 'POST':
        # CSRF 검증 없이 요청 처리
        return HttpResponse("Request processed")

변경 후

python
# Safe Django code
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_protect

@csrf_protect
def my_view(request):
    if request.method == 'POST':
        # CSRF 토큰을 검증하여 요청 처리
        return HttpResponse("Request processed")

설명:

  • 변경 전: CSRF 검증 없이 POST 요청을 처리하여 CSRF 공격에 취약합니다.
  • 변경 후: @csrf_protect 데코레이터를 사용하여 CSRF 토큰을 검증합니다.

Flask

Flask 예시는 your_secret_key 환경 변수에 충분히 강한 비밀 키가 설정되어 있다고 가정합니다. 보호된 폼이나 AJAX 요청에는 CSRF 토큰을 포함해야 합니다.

변경 전

python
# Unsafe Flask code
from flask import Flask, request

app = Flask(__name__)

@app.route('/submit', methods=['POST'])
def submit():
    # CSRF 검증 없이 요청 처리
    return "Request processed"

변경 후

python
# Safe Flask code
import os
from flask import Flask, request
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv("your_secret_key").encode()
csrf = CSRFProtect(app)

@app.route('/submit', methods=['POST'])
def submit():
    # CSRF 토큰을 검증하여 요청 처리
    return "Request processed"

설명:

  • 변경 전: CSRF 검증 없이 POST 요청을 처리하여 CSRF 공격에 취약합니다.
  • 변경 후: flask_wtf.csrf.CSRFProtect를 사용하여 CSRF 토큰을 검증합니다.

FastAPI

쿠키 인증을 사용하는 상태 변경 경로를 가정합니다. 두 번째 예시는 Origin이 없거나 허용 목록과 다르면 거부하는 출처 검증 예시이며, 인증·권한 확인은 별도로 구현해야 합니다.

변경 전

python
# Unsafe FastAPI code
from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/submit")
async def submit(request: Request):
    # CSRF 검증 없이 요청 처리
    return {"message": "Request processed"}

변경 후

python
# Safe FastAPI code
from fastapi import FastAPI, Request, HTTPException
from starlette.middleware.trustedhost import TrustedHostMiddleware

app = FastAPI()

# 신뢰할 수 있는 호스트 설정
app.add_middleware(
    TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]
)

@app.post("/submit")
async def submit(request: Request):
    origin = request.headers.get('origin')
    if origin not in ["https://example.com", "https://www.example.com"]:
        raise HTTPException(status_code=403, detail="Invalid origin")
    # 요청 처리
    return {"message": "Request processed"}

설명:

  • 변경 전: CSRF 검증 없이 POST 요청을 처리하여 CSRF 공격에 취약합니다.
  • 변경 후: Origin 검증이 허용하지 않은 출처의 요청을 차단합니다. TrustedHostMiddleware는 대상 Host 헤더를 검사하며, 그 자체로 CSRF를 방어하지는 않습니다.

참조