mitm proxy

손호준·2023년 7월 24일

rust axum_server 크레이트 테스트

https://docs.rs/axum-server/latest/axum_server/

0. 사용 예제

use axum::{routing::get, Router};
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(|| async { "Hello, world!" }));

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("listening on {}", addr);
    axum_server::bind(addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}


프록시 설정까지 해주고 나서 http로 통신하는 아무 사이트 접속해보면 (예: http://f1.fasoo.com) 화면에 Hello, world!가 출력된다.하지만 우리가 접속해야하는건 https이다. 마침 axum_server 크레이트에서 tls-rustls 기능을 제공하기에 테스트 해봤다.

1. tls_rustls 사용

use axum::{routing::get, Router};
use axum_server::tls_rustls::RustlsConfig;
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(|| async { "Hi!" }));

    let config = RustlsConfig::from_pem_file(
        "self_cert/cert.pem",
        "self_cert/key.pem",
    )
    .await
    .unwrap();

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("listening on {}", addr);
    axum_server::bind_rustls(addr, config)
        .serve(app.into_make_service())
        .await
        .unwrap();
}


-> 브라우저로 접속시 ERR_RESPONSE_HEADERS_TRUNCATED에러발생
pem file 문제라고 생각하여 openssl로 인증서랑 키 재발급 후 다시 테스트해도 같은 에러가 발생했다.
브라우저에 인증서 추가해줘도 마찬가지.. ssl 및 인증서 관련해서 공부가 필요하다고 판단하여 조사해봤다.

ssl 통신 과정

  1. 클라이언트가 서버에 접속 (Client Hello)
  • 클라이언트가 생성한 랜덤 데이터 + 클라이언트가 지원하는 암호화 방식 + 세션ID 를 서버에 전송
  1. 서버 응답 (Server Hello)
  • 서버가 생성한 랜덤 데이터 + 클라이언트가 전송한 암호화 방식중 서버도 사용가능한 암호화 방식 + 인증서를 클라이언트에게 전송
  1. 인증서 확인
  • 클라이언트는 서버가 보낸 인증서가 CA에 의해 발급됐는지 확인하기 위해 브라우저가 갖고 있는 CA리스트를 확인해서, 인증서를 발급한 CA가 없으면 경고를 보내고 있다면 인증서를 그 공개키로 복호화(브라우저는 리스트에 있는 CA 기관들에 대한 공개키를 갖고 있기 때문에 복호화 가능). 복호화된 인증서 안에는 서버의 공개키가 들어 있음
  • 둘이 주고 받은 랜덤 데이터를 조합해서 pre master secret을 생성하고 이걸 서버의 공개키로 암호화 해서 서버에 전송
  1. 서버 응답2
  • 클라이언트로부터 받은 pre master key 값을 자신의 비공개키로 복호화.
  • 이후 클라이언트와 서버는 일련의 과정을 거쳐 이 pre master secret로 master secret 생성하는데, 이걸로 세션 키 생성
  • 실제 데이터는 세션키로 암호화 한 후(대칭키 암호화 방식) 주고 받는다.

그냥 공개키 방식으로만 통신하는게 충분히 이상적이지만, 컴퓨팅 파워가 많이 필요하기 때문에 공개키+대칭키 방식을 조합해서 통신한다.

tls_rustls사용 예시로 돌아가서, config안에 있는 인증서(cert.pem)를 확인해보면 도메인주소가 localhost로 되어있다. 즉, 인증서에 포함된 공개키는 localhost서버에 대한 것이고, 따라서 해당 도메인에 접속했을때만(https://localhost:3000) ssl 통신이 이루어진다. 즉 다른 도메인(예: chat.openai.com, www.google.com)에 접속하려하면 에러가 발생하는게 맞다.(근데 저 에러가 나오는게 정상인지는 확인해봐야겠다..)


https://localhost:3000에 접속해보면 hi가 출력되는 것을 확인할 수 있다.


위 예제는 그냥 ssl통신이 되는 localhost 서버를 띄우는 거 뿐이었다.

그렇다면 이걸 프록시 서버로 쓰려면 어떻게해야할까? 마침 axum으로 프록시를 만든 예시가 있어서 테스트해봤다.(https://blog.devgenius.io/building-a-proxy-server-in-rust-with-axum-4d1e0215a6b0) 일단 이 예시에서는 인증서를 등록하고 config를 만드는 부분이 없다.

아래는 결과 로그의 일부이다.

여기서 알 수 있는 문제점은 https 트래픽은 암호화 되어 전송되므로 URL의 CONNECT 및 기본 도메인 부분만 표시된다는 것이다.

클라이언트와 서버 간의 암호화된 HTTPS 트래픽을 가로채고 로깅하려면 mitmproxy 및 SSL 트래픽을 해독하는 단계가 필요하다고 한다..

mitmproxy란?

https://docs.mitmproxy.org/stable/
보통 MITM 이라고하면 권한이 없는 제3자가 서로 직접 통신하고 있다고 믿는 두 당사자 간의 통신을 가로채서 변경할 수 있는 사이버 보안 공격인 중간자공격(Man-In-The-Middle-Attack)을 말한다. mitmproxy 이와 유사하게 두 당사자 간의 데이터 흐름을 캡처하여 사용자가 디버깅, 보안 테스트 및 리버스 엔지니어링과 같은 다양한 목적을 위해 통신을 분석하고 조작할 수 있도록 구성된 프록시다.

mitmproxy

mitmproxy를 설치하고 mitmproxy에서 제공하는 인증서도 등록한 후 실행시켜봤다. chat.openai.com에 질문을 보내고 mitmproxy 로그를 확인해보니 conversation이 정상적으로 캡쳐되었다.




conversation 헤더를 잘 가져온다.

러스트기반의 mitm proxy

  1. thirdwheel (https://github.com/campbellC/third-wheel)
  2. man-in-the-middle-proxy (https://github.com/emanuele-em/man-in-the-middle-proxy)
  3. privaxy (https://github.com/Barre/privaxy)
profile
Rustacean🦀

0개의 댓글