Add mitmproxy to HAR conversion tools
- scripts/mitmproxy-to-har.py: Конвертер mitm → HAR - MITMPROXY.md: Инструкция по записи и конвертации - Два способа: mitmweb (GUI) и mitmdump + скрипт
This commit is contained in:
parent
90f465eedb
commit
7b01544802
156
MITMPROXY.md
Normal file
156
MITMPROXY.md
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
# Mitmproxy → HAR
|
||||||
|
|
||||||
|
## Быстрый старт
|
||||||
|
|
||||||
|
### Вариант 1: mitmweb (проще всего)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Запустить mitmweb
|
||||||
|
mitmweb --mode regular
|
||||||
|
|
||||||
|
# 2. Открыть в браузере
|
||||||
|
http://127.0.0.1:8081
|
||||||
|
|
||||||
|
# 3. Настроить прокси в браузере
|
||||||
|
# localhost:8081
|
||||||
|
|
||||||
|
# 4. Выполнить действия на Kwork
|
||||||
|
|
||||||
|
# 5. Экспорт: File → Export → HAR
|
||||||
|
```
|
||||||
|
|
||||||
|
### Вариант 2: mitmdump + конвертация
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Записать трафик
|
||||||
|
mitmdump -w kwork-dump.mitm
|
||||||
|
|
||||||
|
# 2. Конвертировать в HAR
|
||||||
|
python3 scripts/mitmproxy-to-har.py kwork-dump.mitm kwork-dump.har
|
||||||
|
|
||||||
|
# 3. Анализировать
|
||||||
|
jq '.log.entries | length' kwork-dump.har
|
||||||
|
```
|
||||||
|
|
||||||
|
## Настройка прокси
|
||||||
|
|
||||||
|
### Системный прокси (Linux)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export http_proxy=http://127.0.0.1:8081
|
||||||
|
export https_proxy=http://127.0.0.1:8081
|
||||||
|
```
|
||||||
|
|
||||||
|
### Firefox
|
||||||
|
|
||||||
|
1. Настройки → Прокси
|
||||||
|
2. Manual proxy configuration
|
||||||
|
3. HTTP Proxy: 127.0.0.1:8081
|
||||||
|
4. ☑ Also use for HTTPS
|
||||||
|
|
||||||
|
### Chrome
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Запустить с прокси
|
||||||
|
google-chrome --proxy-server="127.0.0.1:8081"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Установка сертификата
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Открыть в браузере
|
||||||
|
http://mitm.it
|
||||||
|
|
||||||
|
# 2. Скачать сертификат для вашей ОС
|
||||||
|
|
||||||
|
# 3. Установить в доверенные
|
||||||
|
# Linux:
|
||||||
|
sudo cp mitmproxy-ca-cert.pem /usr/local/share/ca-certificates/
|
||||||
|
sudo update-ca-certificates
|
||||||
|
|
||||||
|
# 4. Перезапустить браузер
|
||||||
|
```
|
||||||
|
|
||||||
|
## Фильтрация трафика
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Только Kwork
|
||||||
|
mitmdump -w kwork.mitm --set 'filter=~u kwork\.ru'
|
||||||
|
|
||||||
|
# Только API
|
||||||
|
mitmdump -w kwork-api.mitm --set 'filter=~u /api/'
|
||||||
|
|
||||||
|
# Исключить статику
|
||||||
|
mitmdump -w kwork.mitm --set 'filter=!~u \.(css|js|png|jpg|gif|svg)'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Скрипт для записи Kwork
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# record-kwork.sh
|
||||||
|
|
||||||
|
echo "Starting mitmproxy recording for Kwork..."
|
||||||
|
echo "Configure your browser to use proxy: localhost:8081"
|
||||||
|
echo "Press Ctrl+C when done"
|
||||||
|
|
||||||
|
mitmdump \
|
||||||
|
-w kwork-session.mitm \
|
||||||
|
--set 'filter=~u kwork\.ru' \
|
||||||
|
--set 'ignore_hosts=~u (cdn|counter|yandex|google)' \
|
||||||
|
--set 'keep_host_headers=true'
|
||||||
|
|
||||||
|
echo "Converting to HAR..."
|
||||||
|
python3 scripts/mitmproxy-to-har.py kwork-session.mitm kwork-session.har
|
||||||
|
|
||||||
|
echo "Done! File: kwork-session.har"
|
||||||
|
echo "Analyze with: jq '.log.entries | length' kwork-session.har"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Анализ записанного HAR
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Количество запросов
|
||||||
|
jq '.log.entries | length' kwork-session.har
|
||||||
|
|
||||||
|
# JSON эндпоинты
|
||||||
|
jq '[.log.entries[] | select(.response.content.mimeType | test("json"; "i")) | .request.url | split("?")[0]] | unique' kwork-session.har
|
||||||
|
|
||||||
|
# Самые большие ответы
|
||||||
|
jq '[.log.entries[] | {url: .request.url, size: .response.bodySize}] | sort_by(.size) | reverse | .[0:10]' kwork-session.har
|
||||||
|
|
||||||
|
# Авторизация
|
||||||
|
jq '[.log.entries[].request.headers[] | select(.name | ascii_downcase | test("authorization|cookie"; "i"))] | unique' kwork-session.har
|
||||||
|
```
|
||||||
|
|
||||||
|
## Типичные проблемы
|
||||||
|
|
||||||
|
### SSL ошибки
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Игнорировать SSL ошибки
|
||||||
|
mitmdump --set 'ignore_hosts=.*'
|
||||||
|
|
||||||
|
# Или отключить проверку
|
||||||
|
mitmdump --set 'ssl_insecure=true'
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebSocket
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# WebSocket не экспортируется в HAR
|
||||||
|
# Используйте --show-websocket для отладки
|
||||||
|
mitmdump --show-websocket
|
||||||
|
```
|
||||||
|
|
||||||
|
### Сжатый трафик
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# mitmproxy автоматически распаковывает
|
||||||
|
# Если нужно сырое:
|
||||||
|
mitmdump --set 'rawtcp=true'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Updated: 2026-03-22_
|
||||||
97
scripts/mitmproxy-to-har.py
Normal file
97
scripts/mitmproxy-to-har.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Convert mitmproxy flow dump to HAR format
|
||||||
|
Usage: mitmproxy-to-har.py <input.mitm> <output.har>
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
from mitmproxy import io
|
||||||
|
from mitmproxy.http import HTTPFlow
|
||||||
|
|
||||||
|
|
||||||
|
def flow_to_har_entry(flow: HTTPFlow) -> dict:
|
||||||
|
"""Convert single flow to HAR entry."""
|
||||||
|
if not flow.request or not flow.response:
|
||||||
|
return None
|
||||||
|
|
||||||
|
entry = {
|
||||||
|
"startedDateTime": datetime.fromtimestamp(flow.request.timestamp_start).isoformat() + "Z",
|
||||||
|
"time": int((flow.response.timestamp_end - flow.request.timestamp_start) * 1000) if flow.response.timestamp_end else 0,
|
||||||
|
"request": {
|
||||||
|
"method": flow.request.method,
|
||||||
|
"url": flow.request.url,
|
||||||
|
"httpVersion": flow.request.http_version,
|
||||||
|
"headers": [{"name": k, "value": v} for k, v in flow.request.headers.items()],
|
||||||
|
"queryString": [{"name": k, "value": v} for k, v in flow.request.query.items()],
|
||||||
|
"cookies": [{"name": k, "value": v} for k, v in flow.request.cookies.items()],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": len(flow.request.content) if flow.request.content else 0,
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": flow.response.status_code,
|
||||||
|
"statusText": flow.response.reason,
|
||||||
|
"httpVersion": flow.response.http_version,
|
||||||
|
"headers": [{"name": k, "value": v} for k, v in flow.response.headers.items()],
|
||||||
|
"cookies": [{"name": k, "value": v} for k, v in flow.response.cookies.items()],
|
||||||
|
"content": {
|
||||||
|
"size": len(flow.response.content) if flow.response.content else 0,
|
||||||
|
"mimeType": flow.response.headers.get("Content-Type", "unknown").split(";")[0],
|
||||||
|
"text": flow.response.get_text() if flow.response.content else ""
|
||||||
|
},
|
||||||
|
"redirectURL": flow.response.headers.get("Location", ""),
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": len(flow.response.content) if flow.response.content else 0,
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": 0,
|
||||||
|
"wait": int((flow.response.timestamp_start - flow.request.timestamp_end) * 1000) if flow.response.timestamp_start and flow.request.timestamp_end else 0,
|
||||||
|
"receive": int((flow.response.timestamp_end - flow.response.timestamp_start) * 1000) if flow.response.timestamp_end and flow.response.timestamp_start else 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add POST data if present
|
||||||
|
if flow.request.method == "POST" and flow.request.content:
|
||||||
|
entry["request"]["postData"] = {
|
||||||
|
"mimeType": flow.request.headers.get("Content-Type", "application/octet-stream"),
|
||||||
|
"text": flow.request.get_text() if flow.request.content else ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry
|
||||||
|
|
||||||
|
|
||||||
|
def convert_mitm_to_har(input_file: str, output_file: str):
|
||||||
|
"""Convert mitmproxy dump to HAR."""
|
||||||
|
har = {
|
||||||
|
"log": {
|
||||||
|
"version": "1.2",
|
||||||
|
"creator": {
|
||||||
|
"name": "mitmproxy-to-har",
|
||||||
|
"version": "1.0"
|
||||||
|
},
|
||||||
|
"entries": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(input_file, 'rb') as f:
|
||||||
|
reader = io.FlowReader(f)
|
||||||
|
for flow in reader.read():
|
||||||
|
if isinstance(flow, HTTPFlow):
|
||||||
|
entry = flow_to_har_entry(flow)
|
||||||
|
if entry:
|
||||||
|
har["log"]["entries"].append(entry)
|
||||||
|
|
||||||
|
with open(output_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(har, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
print(f"Converted {len(har['log']['entries'])} flows to {output_file}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print("Usage: mitmproxy-to-har.py <input.mitm> <output.har>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
convert_mitm_to_har(sys.argv[1], sys.argv[2])
|
||||||
Loading…
Reference in New Issue
Block a user