CSRF 란?

CSRF란, Cross Site Request Forgery의 약자로 한글 뜻으로는 사이트 간 요청 위조를 뜻합니다.
CSRF는 웹 어플리케이션 간 보안 취약점 중 하나로, 인증된 사용자의 권한으로 악성 요청을 전송하여 공격하는 방법입니다.
공격자는 희생자의 브라우저에서 유효한 희생자인 척하여 데이터 수정, 등록, 삭제 등의 행위를 특정 웹사이트에 요청할 수 있습니다.
예를 들어, 희생자의 이메일 주소 및 암호를 변경하거나 계좌이체 등의 요청을 수행하게 할 수 있습니다.

CSRF 동작원리

CSRF 가 성공하기 위해서는 다음 조건을 만족해야 합니다.
1. 사용자는 보안이 취약한 서버로부터 이미 로그인되어 있는 상태여야 합니다.
2. 쿠키 기반의 서버 세션 정보를 획득할 수 있어야 합니다.
3. 공격자는 서버를 공격하기 위한 요청 방법에 대해 미리 파악하고 있어야 합니다. 예상하지 못한 요청 매개변수가 없어야 합니다.

위 조건이 만족되면 다음과 같은 과정을 통해 CSRF 공격을 할 수 있습니다.
1. 사용자가 보안이 취약한 서버에 로그인합니다.
2. 서버에 저장된 세션 정보를 사용할 수 있는 session ID가 사용자의 브라우저 쿠키에 저장됩니다.
3. 공격자는 사용자가 악성 스크립트 페이지를 누르도록 유도합니다.

악성 스크립트 페이지를 누르도록 유도하는 방식은 아래와 같은 방식들이 있습니다.
1. 게시판이 있는 웹사이트에 악성 스크립트를 게시글로 작성하여 사용자들이 게시글을 클릭하도록 유도
2. 메일 등으로 악성 스크립트를 직접 전달하거나, 악성 스크립트가 적힌 페이지 링크 전달

  1. 사용자가 악성 스크립트가 작성된 페이지 접근 시 웹 브라우저에 의해 쿠키에 저장된 session ID와 함께 서버로 요청됩니다.
  2. 서버는 쿠키에 담긴 session ID를 통해 해당 요청이 인증된 사용자로부터 온 것으로 판단하고 처리합니다.

CSRF 방어 방법

1. Referer 체크

HTTP 요청 헤더 정보에서 Referer 정보를 확인할 수 있습니다.
보통이라면 호스트와 Referer 값이 일치하므로 둘을 비교합니다.
CSRF 공격의 대부분은 Referer 값에 대한 검증만으로도 많은 수의 공격을 방어할 수 있다고 합니다.

Python Django 웹 프레임워크를 사용하신다면 아래와 같이 커스텀 CSRF 미들웨어를 만들어서 모든 요청에 대해 Referer 체크를 적용해 방어가 CSRF 방어가 가능합니다.

from django.middleware.csrf import CsrfViewMiddleware

class CustomCsrfMiddleware(CsrfViewMiddleware):
    def process_view(self, request, callback, callback_args, callback_kwargs):
        referer = request.META.get('HTTP_REFERER')
        allowed_hosts = ['www.example.com', 'example.com'] # 신뢰할 수 있는 도메인
        if referer and not any(host in referer for host in allowed_hosts):
            return self._reject(request) # 요청 거부
        return super().process_view(request, callback, callback_args, callback_kwargs)

2. CSRF 토큰 사용

사용자 세션에 임의의 값을 저장해 모든 POST 요청마다 해당 값을 포함하여 전송하도록 합니다.
서버에서 요청을 받을 때마다, 세션에 저장된 값과 요청으로 전송된 값의 일치 여부를 검증하여 방어하는 방법입니다.

Python Flask 웹 프레임워크를 사용한다면 아래와 같이 CSRF 토큰 검증이 가능합니다.

from flask import Flask, render_template, request, session 
import os

app = Flask(__name__)
app.secret_key = os.urandom(24)

@app.route('/')
def home():
    session['csrf_token'] = os.urandom(24).hex() # CSRF 토큰 생성 및 세션에 저장
    return render_template('index.html')

@app.route('/changePassword', methods=['POST'])
def changePassword():
    if session.get('csrf_token') != request.form.get('csrf_token'): # 토큰 검증
        return 'Invalid CSRF Token'
    else:
        # 요청 처리 로직
        return 'Success'

if __name__ == '__main__':
    app.run()

3. SameSite 쿠키 속성 사용

SameSite 속성은 브라우저가 쿠키를 어떤 상황에서 전송하는지를 제어하는 속성입니다.
SameSite 속성을 'Strict'나 'Lax'로 설정하면, 외부 도메인에서의 요청 시 쿠키를 전송하지 않도록 할 수 있습니다.

Python Django 웹 프레임워크를 사용하여 아래와 같이 SameSite 쿠키 속성을 적용할 수 있습니다.

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def csrf_view(request):
    response = HttpResponse('Hello, World!')
    response.set_cookie('sessionid', '1234567890', samesite = 'Strict') # SameSite 속성 적용
    return response

4. CAPCHA 도입

요청 시에 CAPCHA 를 이용하여, CAPCHA 인증코드가 없거나 틀리면 요청을 거부하도록 할 수 있습니다.

profile
풀 스택 개발자를 지향하는 개발자 새내기입니다.

0개의 댓글