OPEN HYPER STEP
← 목록으로 (화이트햇 보안)
SECURITY · 81 / 84
security
CHAPTER 81 / 84
읽기 약 2
FUNCTION

CSRF와 기타 웹 취약점


핵심 개념

⚠️ 방어 관점. CSRF 토큰·SSRF·Path Traversal·파일 업로드 4종 취약점의 Python 감지 + 방어 패턴.

본문

OWASP Top 10 매핑

취약점OWASP 분류대표 위험
CSRFA01 (Access Control)인증된 사용자 사칭
SSRFA10 (Server-Side Request Forgery)내부 네트워크 노출
Path TraversalA01 (Access Control)임의 파일 읽기
파일 업로드A04 (Insecure Design)RCE (원격 코드 실행)

CSRF 방어 — 토큰 기반

PYTHON📋 코드 (39줄)
# ⚠️ 교육 목적, 허가된 환경에서만 사용.
from flask import Flask, session, request, abort
from secrets import token_urlsafe
from functools import wraps

app = Flask(__name__)
app.secret_key = token_urlsafe(32)


def csrf_protect(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        if request.method == 'POST':
            token = request.form.get('csrf_token') or request.headers.get('X-CSRF-Token')
            session_token = session.get('csrf_token')
            if not token or token != session_token:
                abort(403, 'CSRF token mismatch')
        if 'csrf_token' not in session:
            session['csrf_token'] = token_urlsafe(32)
        return f(*args, **kwargs)
    return wrapper


@app.route('/transfer', methods=['POST'])
@csrf_protect
def transfer():
    amount = request.form['amount']
    # 안전: CSRF 토큰 검증 통과
    return 'OK'


# ✅ 추가 방어: SameSite=Strict 쿠키
@app.after_request
def set_secure_cookies(response):
    response.headers.add(
        'Set-Cookie',
        f'session={session.sid}; HttpOnly; Secure; SameSite=Strict; Path=/',
    )
    return response

SSRF 방어 — URL 화이트리스트

PYTHON📋 코드 (45줄)
import requests
import ipaddress
from urllib.parse import urlparse


PRIVATE_NETWORKS = [
    ipaddress.ip_network('10.0.0.0/8'),
    ipaddress.ip_network('172.16.0.0/12'),
    ipaddress.ip_network('192.168.0.0/16'),
    ipaddress.ip_network('127.0.0.0/8'),
    ipaddress.ip_network('169.254.0.0/16'),  # AWS metadata
]

ALLOWED_DOMAINS = {'api.partner.com', 'cdn.example.com'}


def is_safe_url(url: str) -> bool:
    parsed = urlparse(url)

    # 1. 스키마 검증
    if parsed.scheme not in ('http', 'https'):
        return False

    # 2. 도메인 화이트리스트
    if parsed.hostname not in ALLOWED_DOMAINS:
        return False

    # 3. DNS 해석 후 사설 IP 차단 (DNS rebinding 방어)
    import socket
    try:
        ip_str = socket.gethostbyname(parsed.hostname)
        ip = ipaddress.ip_address(ip_str)
        for net in PRIVATE_NETWORKS:
            if ip in net:
                return False
    except (socket.gaierror, ValueError):
        return False

    return True


def fetch_external(url: str) -> bytes:
    if not is_safe_url(url):
        raise ValueError(f'Unsafe URL: {url}')
    return requests.get(url, timeout=5, allow_redirects=False).content

Path Traversal 방어

PYTHON📋 코드 (16줄)
from pathlib import Path

UPLOAD_DIR = Path('/var/app/uploads').resolve()


def safe_file_read(filename: str) -> bytes:
    # ❌ 위험: '../../../etc/passwd' 우회 가능
    # path = UPLOAD_DIR / filename

    # ✅ 안전: resolve() + 부모 디렉터리 검증
    target = (UPLOAD_DIR / filename).resolve()
    if UPLOAD_DIR not in target.parents and target != UPLOAD_DIR:
        raise ValueError(f'Path traversal attempt: {filename}')
    if not target.is_file():
        raise FileNotFoundError(filename)
    return target.read_bytes()

파일 업로드 방어

PYTHON📋 코드 (33줄)
import magic  # python-magic
from pathlib import Path
from secrets import token_hex

ALLOWED_MIME = {'image/jpeg', 'image/png', 'image/webp'}
MAX_SIZE = 5 * 1024 * 1024  # 5MB
UPLOAD_DIR = Path('/var/app/uploads').resolve()


def safe_upload(file_storage) -> str:
    # 1. 크기 검증
    file_storage.seek(0, 2)
    size = file_storage.tell()
    file_storage.seek(0)
    if size > MAX_SIZE:
        raise ValueError('File too large')

    # 2. MIME 검증 (magic byte — 확장자 위조 방지)
    mime = magic.from_buffer(file_storage.read(2048), mime=True)
    file_storage.seek(0)
    if mime not in ALLOWED_MIME:
        raise ValueError(f'MIME not allowed: {mime}')

    # 3. 랜덤 파일명 (원본 이름 사용 금지)
    ext = {'image/jpeg': '.jpg', 'image/png': '.png', 'image/webp': '.webp'}[mime]
    new_name = f'{token_hex(16)}{ext}'
    target = UPLOAD_DIR / new_name

    # 4. 실행 권한 차단 (파일 시스템 레벨 — chmod 644)
    file_storage.save(target)
    target.chmod(0o644)

    return new_name

AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내 웹앱 코드에서 CSRF/SSRF/Path Traversal/
파일 업로드 4종 취약점 가능 위치를 모두 찾아내고
각각 방어 코드를 적용해줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

OWASP Top 10 2024 vs 2026 변경 사항과
새롭게 추가된 위협을 한국 SaaS 사례로
비교 분석해서 설명해줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 코드베이스 전체에서 위 4종 취약점을
자동 스캔하는 통합 점검 스크립트를 만들고
결과를 우선순위별 리포트로 출력해줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 SSRF 관련 실제 침해 사례 Top 5
(AWS metadata 노출 등)와 방어 트렌드를
솔직히 알려줘.

⭐ 이것만 기억하세요
CSRF와 기타 웹 취약점 이 3가지만 확실히 잡으세요
1.4종 취약점 모두 "사용자 제어 데이터 → 신뢰 영역으로 이동" 패턴 — 경계마다 검증·인코딩 적용
2.CSRF + SameSite=Strict 쿠키 + Origin/Referer 검증 3중 방어로 95% 차단
3.다음 챕터에서 argparse CLI — 위 모든 방어 기법을 통합한 자동 점검 도구 제작


공유하기
진행도 81 / 84