회사에서 일할 때 Charles Proxy 툴을 사용하여 HTTP/S 네트워크 통신을 추적하고 필요에 따라 Request, Response 데이터를 변조하고 있다.
Charles는 매우 훌륭한 Proxy 툴이지만 사내망 접근을 위해 VPN을 켜게 되면 Charles와 충돌이 발생하는지 네트워크가 먹통이 되거나, Charles가 동작하지 않는 등의 문제가 발생하고 있다.
따라서, 오픈소스 Proxy 툴이 있는지 검색해 보았고 매력적인 오픈소스 Proxy 툴이 있어 한번 시험해 보았다.
내가 시험해 본 Proxy 툴은 "mitmproxy"이라는 녀석이다. 이 녀석은 Python으로 만들어진 네트워크 분석 도구이지만 Charles처럼 편리한 GUI를 제공하지는 않는다. 웹 기반의 인터페이스도 제공하지만 매우 허접한 수준이기 때문에 주로 커맨드 라인 또는 콘솔로 활용해야 할듯하다.
그렇다면 왜 이런 GUI도 제공되지 않는 오픈소스를 시험해 보려 하는지 궁금해할 수 있다. 그 이유는 딱 하나가 있는데 Python 스크립트로 Addon 작성이 가능하기 때문이다. 즉, 파이썬 코드를 통해 Request나 Response 변경이 가능하고 자동화 코드와도 결합이 가능하다는 것이다. 대략적인 구조는 아래 샘플 코드를 참고해보면 좋을 것 같다.
class HTTPEvents:
def requestheaders(self, flow: mitmproxy.http.HTTPFlow):
"""
HTTP request headers were successfully read. At this point, the body is empty.
"""
ctx.log(f"requestheaders: {flow=}")
def request(self, flow: mitmproxy.http.HTTPFlow):
"""
The full HTTP request has been read.
Note: If request streaming is active, this event fires after the entire body has been streamed.
HTTP trailers, if present, have not been transmitted to the server yet and can still be modified.
Enabling streaming may cause unexpected event sequences: For example, `response` may now occur
before `request` because the server replied with "413 Payload Too Large" during upload.
"""
ctx.log(f"request: {flow=}")
def responseheaders(self, flow: mitmproxy.http.HTTPFlow):
"""
HTTP response headers were successfully read. At this point, the body is empty.
"""
ctx.log(f"responseheaders: {flow=}")
def response(self, flow: mitmproxy.http.HTTPFlow):
"""
The full HTTP response has been read.
Note: If response streaming is active, this event fires after the entire body has been streamed.
HTTP trailers, if present, have not been transmitted to the client yet and can still be modified.
"""
ctx.log(f"response: {flow=}")