POST 요청의 네 조각과 requests 옵션 대응표
🛡️ 강사 제공 인가된 실습 환경에서 수행
POST 요청의 전체 구조
POST /dvwa/vulnerabilities/exec/ HTTP/1.1 ← ① 요청 라인
Host: 192.168.10.102 ← ② 헤더 (필수)
Cookie: security=low; PHPSESSID=abc123... ← ② 헤더 (인증)
User-Agent: Mozilla/5.0 ... ← ② 헤더
Referer: http://192.168.10.102/dvwa/... ← ② 헤더
Content-Type: application/x-www-form-urlencoded ← ② 헤더 (본문 형식)
Content-Length: 27 ← ② 헤더 (본문 길이)
(빈 줄 - CRLF) ← ③ 헤더 끝 신호
ip=127.0.0.1;whoami&submit=submit ← ④ 본문(body) = 보낼 데이터
순서는 요청 라인 → 헤더들 → 빈 줄 → 본문이다. POST는 GET과 달리 본문에 데이터를 담는다(GET은 URL 쿼리스트링).
① 요청 라인
메서드 + 경로 + HTTP 버전
| 요소 | 예시 | 의미 |
|---|---|---|
| 메서드 | POST | 서버에 데이터를 보내는 요청 |
| 경로 | /dvwa/vulnerabilities/exec/ | 어느 페이지로 |
| 버전 | HTTP/1.1 | 프로토콜 버전 |
파이썬에선 requests.post(url) 의 url이 호스트+경로를 담당한다.
② 헤더 — 요청의 메타정보
필수 / 중요
| 헤더 | 역할 | 안 넣으면? |
|---|---|---|
| Host | 어느 도메인/서버로 | requests가 URL로 자동 처리 |
| Cookie | 인증/세션 (로그인 증명) | 로그인 필요한 페이지 접근 불가 (302 튕김) |
| Content-Type | 본문 데이터의 형식 | 서버가 본문 해석 못 할 수 있음 |
| Content-Length | 본문 길이 | requests가 자동 계산 |
위장 / 부가 (공격 시 자주 조작)
| 헤더 | 역할 | 공격 관점 |
|---|---|---|
| User-Agent | 클라이언트(브라우저/OS) 정보 | 봇 탐지 회피용 위장, 정보 노출 |
| Referer | 어느 페이지에서 왔는지 | 검증 우회, 위조 가능 |
| Accept | 받고 싶은 응답 형식 | |
| Accept-Language | 선호 언어 | 핑거프린팅 |
| Connection | 연결 유지 방식 (keep-alive) |
헤더는 전부 클라이언트가 보내는 값이다. 위조가 가능하므로 서버가 신뢰하면 안 된다.
③ 빈 줄 (CRLF)
헤더가 끝났다는 신호(\r\n\r\n)이고, 이 빈 줄 다음부터가 본문이다. CRLF Injection 취약점의 원리가 여기서 나온다.
④ 본문 — 실제 보낼 데이터
POST의 핵심. 여기에 데이터를 담아 보낸다.
| Content-Type | 본문 형식 | 용도 |
|---|---|---|
application/x-www-form-urlencoded | key1=val1&key2=val2 | 일반 폼 (DVWA 로그인/공격) |
application/json | {"key": "value"} | API, JSON 전송 |
multipart/form-data | 경계(boundary)로 구분 | 파일 업로드 |
- form 형식:
ip=127.0.0.1;whoami&submit=submit - JSON 형식:
{"items": [{"dir": "/var/www/", "name": "index.php"}]}
파이썬 requests에서 각 요소 대응
requests.post(
url, # ① 요청 라인 (호스트+경로)
headers={ # ② 헤더
"Cookie": "...",
"User-Agent": "...",
"Content-Type": "...",
},
data={ # ④ 본문 (form 형식)
"ip": "127.0.0.1;whoami",
"submit": "submit"
},
# 또는
json={"items": [...]}, # ④ 본문 (JSON 형식) - Content-Type 자동 설정
cookies={"PHPSESSID": "..."}, # ② Cookie (headers 대신 여기로도 가능)
verify=False, # SSL 검증 무시 (프록시 가로채기용)
allow_redirects=False, # 리다이렉트 자동추적 끄기 (302 확인용)
proxies={"http": "http://127.0.0.1:8080"} # Burp 경유 (디버깅)
)
data 와 json 의 차이
data= | json= | |
|---|---|---|
| 본문 형식 | key=val&key=val | {"key": "val"} |
| Content-Type | x-www-form-urlencoded | application/json (자동) |
| 용도 | 일반 폼 (DVWA) | API / 서버 전송 (Flask) |
자주 쓰는 옵션
| 옵션 | 역할 | 언제 씀 |
|---|---|---|
data= | 본문에 폼 데이터 | 일반 POST |
json= | 본문에 JSON | API 전송 |
headers= | 헤더 지정 | 위장 / 인증 |
cookies= | 쿠키 지정 | 세션 유지 |
verify=False | SSL 검증 끄기 | 프록시 가로채기, 자체서명 인증서 |
allow_redirects=False | 리다이렉트 안 따라감 | 302 확인, 중간 응답 분석 |
proxies= | 프록시 경유 | Burp로 요청 확인 / 디버깅 |
세션 유지 — requests.Session()
여러 요청에서 쿠키를 자동으로 이어서 쓰려면 이렇게 한다.
s = requests.Session() # 쿠키를 들고 다니는 객체
s.post(login_url, data=...) # 로그인 → 쿠키 자동 저장
s.post(exec_url, data=...) # 같은 s → 쿠키 자동 첨부
브라우저가 자동으로 하던 쿠키 관리를 Session이 대신 해주는 것이다.
정리
POST 요청 = 요청라인 + 헤더 + 빈줄 + 본문
- URL — 어디로 보낼지
- 헤더 — 메타정보 (Cookie=인증, Content-Type=본문형식, User-Agent/Referer=위장)
- 본문 (data / json) — 실제 보낼 데이터. POST의 핵심
- 옵션 — verify(SSL), allow_redirects(302), proxies(디버깅)
GET과 POST의 핵심 차이는 하나다. GET은 데이터를 URL에, POST는 데이터를 본문에 담는다.