개요
- 정의: 공격자가 사용자가 자신의 의지와 상관없이 웹 사이트에 요청을 보내도록 속이는 공격
- 작동 방식:
- 사용자가 웹 사이트에 로그인한 상태에서 공격자가 만든 악성 웹 사이트를 방문
- 악성 웹 사이트는 사용자의 브라우저를 통해 로그인된 웹 사이트에 위조된 요청을 보냄
- 웹 사이트는 사용자가 보낸 것으로 판단하고 요청을 처리, 예를 들어, 비밀번호 변경, 계정 정보 수정 등이 발생할 수 있음
- 예방 방법:
- CSRF 토큰 사용: 각 요청마다 예측 불가능한 토큰을 포함시켜 서버에서 유효성을 검사
- SameSite 쿠키 설정: 쿠키가 동일한 사이트에서만 전송되도록 설정
- 요청 출처 확인 (Origin Header Check): 요청을 보낸 출처가 신뢰할 수 있는 출처인지 확인
예시
시나리오: 사용자가 웹사이트에 로그인한 상태에서 CSRF 공격을 받는 경우
- 공격:
공격자는 다음과 같은 HTML 코드를 포함한 악성 웹사이트를 만들 수 있음
<form action="http://example.com/change-password" method="POST"> <input type="hidden" name="password" value="new_password"> <input type="submit" value="Submit"> </form> <script> document.forms[0].submit(); // 자동 제출 </script>
- 영향:
- 사용자가 웹사이트에 로그인한 상태로 악성 웹사이트를 방문하면, 폼이 자동으로 제출
- 서버는 사용자가 비밀번호 변경을 요청한 것으로 인식하고 비밀번호를 변경
- 대비 방법:
- CSRF 토큰 사용:
import React, { useState, useEffect } from 'react'; import axios from 'axios'; function ChangePasswordForm() { const [csrfToken, setCsrfToken] = useState(''); const [password, setPassword] = useState(''); useEffect(() => { axios.get('/csrf-token') .then(response => { setCsrfToken(response.data.csrfToken); }); }, []); const handleSubmit = (event) => { event.preventDefault(); axios.post('/change-password', { password: password, _csrf: csrfToken }).then(response => { console.log(response); }); }; return ( <form onSubmit={handleSubmit}> <input type="hidden" name="_csrf" value={csrfToken} /> <input type="password" name="password" value={password} onChange={e => setPassword(e.target.value)} /> <button type="submit">Change Password</button> </form> ); }
각 요청에 예측 불가능한 토큰을 포함시켜 서버에서 유효성을 검사express serverconst express = require('express'); const csrf = require('csurf'); const cookieParser = require('cookie-parser'); const app = express(); app.use(cookieParser()); const csrfProtection = csrf({ cookie: true }); app.use(csrfProtection); app.get('/csrf-token', (req, res) => { res.json({ csrfToken: req.csrfToken() }); }); app.post('/change-password', csrfProtection, (req, res) => { // 비밀번호 변경 로직 res.send('Password changed'); });
- SameSite 쿠키 설정:
app.use((req, res, next) => { res.cookie('sessionid', '123', { sameSite: 'Strict' }); next(); });
쿠키를 설정할 때 SameSite 속성을 사용하여 쿠키가 동일한 사이트에서만 전송되도록 함 - 요청 출처 확인 (Origin Header Check):
const allowedOrigins = ['http://example.com']; app.use((req, res, next) => { const origin = req.get('origin'); if (allowedOrigins.includes(origin)) { res.header('Access-Control-Allow-Origin', origin); } next(); });
서버에서 요청의 Origin 또는 Referer 헤더를 확인하여 요청이 신뢰할 수 있는 출처에서 왔는지 확인
- CSRF 토큰 사용:
참고
- Google Gemini 답변