61. HTTP 요청·응답 구조

changseop lee·5일 전

📚 네트워크 · 패킷 분석 › 02. 포트 · 프로토콜 분석 — 61편
이전 글: 60. TCP Connection과 Port · 다음 글: 62. HTTPS와 TLS Handshake — 암호화된 통신에서 볼 수 있는 것
참고(01. TCP/IP 구조 이해): 50. 웹 요청 하나의 전체 경로 — 01 시리즈 종합 — DNS·TCP·HTTP가 이어지는 전체 흐름

1. 왜 알아야 하는가

보안관제에서 가장 많이 보는 로그 중 하나가 웹 접근 로그와 웹 방화벽(WAF)·IDS의 HTTP 이벤트입니다. SQL Injection, 경로 조작, 웹셸 접근 같은 웹 공격은 대부분 HTTP 요청 안에 흔적을 남깁니다.

그런데 로그는 HTTP 메시지 전체가 아니라 일부 필드만 골라 기록한 결과입니다. HTTP 메시지 구조를 알아야 다음 질문에 답할 수 있습니다.

  • 로그의 "GET /index.html HTTP/1.1"은 메시지의 어느 부분인가
  • POST 요청의 본문(입력값)은 왜 접근 로그에 없는가
  • X-Forwarded-For의 IP를 믿어도 되는가

이번 글은 HTTP/1.1 메시지를 기준으로 구조를 정리하고, 그 구조가 로그의 어느 필드로 남는지 연결합니다. 메서드·상태코드별 이상징후는 다음 글에서 다룹니다.


2. 핵심 개념

HTTP 메시지 구조
그림 1. 요청 줄·상태 줄 → 헤더 → 빈 줄 → 본문 순서

2-1. HTTP 메시지의 3부분

부분요청(Request)응답(Response)
시작줄요청줄: 메서드 경로 버전 (예: GET /login HTTP/1.1)상태줄: 버전 상태코드 설명 (예: HTTP/1.1 200 OK)
헤더이름: 값 형식 여러 줄이름: 값 형식 여러 줄
빈 줄헤더의 끝 (\r\n)헤더의 끝
본문POST·PUT 등의 전송 데이터 (없을 수 있음)HTML, JSON, 파일 등

HTTP/1.1은 사람이 읽을 수 있는 텍스트 형식이며, 줄바꿈은 CR LF(\r\n)입니다.

2-2. 관제에서 중요한 요청 헤더

헤더의미관제 포인트
Host요청 대상 도메인 (HTTP/1.1 필수)한 IP에 여러 사이트가 있을 때 어느 사이트인지 구분. IP로 직접 접근한 요청은 자동화 도구일 가능성
User-Agent클라이언트 프로그램 정보스캐너·스크립트 식별 단서. 클라이언트가 임의로 바꿀 수 있음
Referer이전 페이지 주소정상 사용자 흐름 여부 판단
Cookie세션 정보세션 탈취 조사 시 중요, 로그에는 보통 남지 않음
Content-Type / Content-Length본문 형식·길이파일 업로드(multipart/form-data) 식별
X-Forwarded-For프록시·로드밸런서가 붙이는 원래 클라이언트 IP신뢰할 수 있는 프록시가 붙인 값만 의미 있음. 클라이언트가 직접 넣을 수도 있음

2-3. 관제에서 중요한 응답 헤더

헤더의미관제 포인트
Server서버 소프트웨어 정보버전 노출은 정보 수집에 이용될 수 있음
Set-Cookie세션 발급로그인 성공 여부 판단 단서
Location리다이렉트 대상3xx 응답과 함께 이동 경로 확인
Content-Length응답 크기로그의 응답 바이트와 연결, 비정상적으로 큰 응답 확인

2-4. HTTP 버전 차이 (개념)

버전형식전송 계층관제 시 의미
HTTP/1.1텍스트TCP평문이면 패킷에서 그대로 읽힘
HTTP/2바이너리 프레임TCP (실제로는 대부분 TLS 위)패킷에서 텍스트로 보이지 않음
HTTP/3바이너리 프레임UDP 기반 QUIC443/UDP로 나타남

버전이 달라도 메서드·경로·헤더·상태코드라는 의미 구조는 같습니다. 웹 서버 로그에는 버전과 상관없이 같은 형식으로 기록됩니다.


3. 동작 원리

요청 하나가 로그 한 줄이 되기까지의 흐름입니다.

[브라우저]
  GET /board/list?page=2 HTTP/1.1      ← 요청줄
  Host: www.example.com                 ← 헤더
  User-Agent: Mozilla/5.0 ...
  Referer: http://www.example.com/
  (빈 줄)
        ↓ TCP 연결(80) 위로 전송
[웹 서버: nginx]
  요청 처리 → 응답 생성
  HTTP/1.1 200 OK                      ← 상태줄
  Content-Type: text/html
  Content-Length: 5120
  (빈 줄)
  <html>...                            ← 본문
        ↓ 응답 전송 후 로그 기록
[access.log 한 줄]
  클라이언트IP - - [시각] "요청줄" 상태코드 응답바이트 "Referer" "User-Agent"

nginx의 기본 combined 로그 형식은 다음 변수로 구성됩니다.

$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"

여기서 $request가 요청줄 전체이고, 요청 본문은 포함되지 않습니다. Apache의 combined 형식(%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i")도 같은 구조입니다. 단, Rocky Linux 패키지의 nginx.conf는 combined 뒤에 "$http_x_forwarded_for"를 덧붙인 main 형식을 기본으로 쓰므로, 로그 끝에 필드가 하나 더 있을 수 있습니다(/etc/nginx/nginx.conf의 log_format에서 확인).


4. 실제 명령어 / 실습

실습 환경: 본인 소유 Linux VM (Rocky Linux 또는 Ubuntu). 인터페이스 이름은 ens33을 예시로 사용합니다.

4-1. nginx 설치

# Rocky Linux
sudo dnf install -y nginx curl tcpdump
sudo systemctl enable --now nginx
sudo firewall-cmd --add-service=http --permanent && sudo firewall-cmd --reload

# Ubuntu (설치 시 자동 시작)
sudo apt install -y nginx curl tcpdump
항목nginx (Rocky·Ubuntu 공통)Apache 참고 (Rocky: httpd / Ubuntu: apache2)
접근 로그/var/log/nginx/access.log/var/log/httpd/access_log / /var/log/apache2/access.log
에러 로그/var/log/nginx/error.log/var/log/httpd/error_log / /var/log/apache2/error.log

4-2. 요청·응답 메시지 전체 보기

curl -v http://127.0.0.1/ -o /dev/null

-v 출력에서 >로 시작하는 줄이 요청, <로 시작하는 줄이 응답 헤더입니다.

4-3. 헤더를 바꿔 로그에 어떻게 남는지 확인

curl -s -o /dev/null -A "lab-test-agent" -e "http://ref.example/" "http://127.0.0.1/?q=hello"
curl -s -o /dev/null -X POST -d "user=test&memo=secret" http://127.0.0.1/
sudo tail -n 2 /var/log/nginx/access.log

📷 [실습 화면 삽입] curl -v의 요청(>)·응답(<) 헤더 출력 화면

4-4. 평문 HTTP가 네트워크에서 보이는 모습

다른 PC(또는 호스트)에서 VM의 IP로 접속하면서 VM에서 캡처합니다.

sudo tcpdump -ni ens33 -A 'tcp port 80'

패킷 필드 단위 해석은 03. Wireshark 패킷 분석 시리즈에서 다룹니다.


5. 결과 확인

형식 예시(값은 환경마다 다름):

$ curl -v http://127.0.0.1/ -o /dev/null
> GET / HTTP/1.1
> Host: 127.0.0.1
> User-Agent: curl/8.x.x
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.x.x
< Content-Type: text/html
< Content-Length: 615

nginx 접근 로그 형식 예시(값은 환경마다 다름):

127.0.0.1 - - [26/Sep/2026:14:02:11 +0900] "GET /?q=hello HTTP/1.1" 200 615 "http://ref.example/" "lab-test-agent"
127.0.0.1 - - [26/Sep/2026:14:02:15 +0900] "POST / HTTP/1.1" 405 157 "-" "curl/8.x.x"
로그 필드HTTP 메시지에서의 출처
127.0.0.1HTTP가 아니라 TCP 연결의 상대 IP
"GET /?q=hello HTTP/1.1"요청줄 (쿼리 스트링 포함)
200, 405응답 상태줄의 상태코드 (정적 페이지에 POST 시 nginx는 405 응답)
615응답 본문 바이트 수
"http://ref.example/", "lab-test-agent"요청 헤더 Referer, User-Agent
(없음)POST 본문 user=test&memo=secret → 접근 로그에 기록되지 않음
  • 요청줄·헤더·빈 줄·본문을 구분할 수 있다
  • -A, -e로 바꾼 헤더가 로그에 그대로 남는 것을 확인했다
  • POST 본문이 접근 로그에 없는 것을 확인했다
  • 평문 HTTP가 tcpdump -A로 읽히는 것을 확인했다

📷 [실습 화면 삽입] access.log에 조작한 User-Agent·Referer가 기록된 화면


6. 패킷 / 로그 분석

HTTP 구조 관점에서 로그를 볼 때의 판단 기준입니다.

관찰정상일 수 있는 경우의심해야 하는 경우
Host가 IP 주소이거나 비어 있음모니터링 시스템의 헬스체크외부에서 IP 직접 접근 + 다양한 경로 요청 (자동화 탐색)
User-Agent가 curl, python-requests 등사내 스크립트, API 연동로그인·관리자 경로에 반복 요청
User-Agent에 SQL 구문·스크립트 문자열—헤더를 통한 주입 시도
요청줄에 ../, %2e%2e, ', <script>드물게 정상 검색어경로 조작·SQLi·XSS 시도 (응답 코드와 함께 판단)
X-Forwarded-For 값이 로그마다 다른 사설 IP사내 프록시 경유프록시가 없는 구간인데 존재 → 클라이언트가 임의로 넣은 값
요청줄이 HTTP 형식이 아님 (로그에 400과 깨진 문자열)다른 프로토콜 클라이언트의 오접속80 포트에 TLS·기타 프로토콜을 보내는 탐색

로그에서 빠르게 확인하는 명령 예시입니다.

# User-Agent 상위 10개 (combined 형식에서 따옴표 기준 6번째 필드)
sudo awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
# 요청줄에 상위 경로 이동 문자열이 포함된 요청
sudo grep -E '\.\./|%2e%2e' /var/log/nginx/access.log

7. 보안관제 관점

로그·장비볼 수 있는 HTTP 정보한계
웹 서버 접근 로그요청줄, 상태코드, 크기, 일부 헤더본문·쿠키는 기본 미기록
WAF요청 본문까지 검사, 차단 룰설정된 룰 범위만 탐지 (06. 방화벽 · IDS/IPS 분석 시리즈에서 다룸)
네트워크 IDS평문 HTTP 전체HTTPS는 복호화 없이는 내용 확인 불가 (06편에서 다룸)
프록시 로그내부 사용자의 외부 접속 URL프록시를 거치지 않는 통신은 누락

한계와 오탐 주의

  • User-Agent, Referer, X-Forwarded-For는 클라이언트가 조작할 수 있는 값입니다. 판단 근거로 쓰되 단독 근거로 쓰지 않습니다.
  • 접근 로그만으로는 POST 본문에 담긴 공격 문자열을 볼 수 없습니다. 본문 확인이 필요하면 WAF 로그나 애플리케이션 로그를 함께 봅니다.
  • 로드밸런서·리버스 프록시 뒤의 웹 서버는 $remote_addr가 프록시 IP로 남습니다. 원래 클라이언트 IP가 어느 필드에 남는지 구조를 먼저 확인합니다.
  • 공격 문자열이 요청에 있다는 것과 공격이 성공했다는 것은 다릅니다. 응답 코드·크기를 함께 봐야 합니다(다음 글).

8. 핵심 정리

  • HTTP 메시지는 시작줄(요청줄/상태줄), 헤더, 빈 줄, 본문으로 구성됩니다.
  • 웹 접근 로그의 "GET /... HTTP/1.1"은 요청줄이고, 상태코드·크기는 응답에서, Referer·User-Agent는 요청 헤더에서 옵니다.
  • 기본 접근 로그에는 요청 본문(POST 데이터)과 쿠키가 기록되지 않습니다.
  • User-Agent, X-Forwarded-For 등 헤더 값은 조작 가능하므로 단독 판단 근거로 쓰지 않습니다.
  • HTTP/2·HTTP/3는 형식이 바이너리지만 메서드·경로·상태코드라는 의미 구조는 같습니다.

다음 글: 05. HTTP 메서드와 상태코드로 보는 이상징후

profile
코드에 숨겨진 위협을 읽고 AI로 보안의 미래를 설계합니다. 프론트엔드 개발 경험을 자산 삼아 더 견고하고 지능적인 보안 운영 시스템을 구축해 나가는 과정을 기록합니다

0개의 댓글