HYOYOUL / ENGINEERING NOTESFIELD REPORT № 01 · 2026.09.07

RustServer · Security review

진단 시점의 기록 · 패치 미적용

메모리 안전성 다음에
확인한 것들.

작은 요청들이 서버를 멈추게 한 이유, 제한 없는 큐가 남긴 비용,
그리고 HTTP 메시지 경계에서 발견한 문제들. RustServer의 보안 진단과 성능을 고려한 수정 방향을 기록합니다.

rustserver v1.0.6macOS · Apple Silicon기준 b22031f로컬 재현 / 코드 검토
10
문제 동작 재현
독립적인 CVE 10건이라는 뜻은 아닙니다.
65
기존 테스트 통과
실패 0개. 감사 검사 11개는 기본 실행에서 제외.
20,000
결정적 입력 변이
이 입력 집합에서 파서 패닉 관찰 없음.
0
RustSec 취약점 매칭
의존성 경고 2건. 자체 코드 안전 판정이 아닙니다.

01 / Context & scope

“내부 메모리 변조나 취약점이 있을까?”

조사의 출발점

CppServer를 Rust로 옮긴 공개 네트워킹 라이브러리의 보안 상태를 확인하는 작업에서 시작했습니다. TCP·TLS·UDP·HTTP·WebSocket·FBE의 파서와 큐, 비동기 작업의 수명, 캐시와 TLS 설정을 살폈습니다.

프로젝트의 forbid(unsafe_code)는 직접 작성한 unsafe 코드를 막습니다. 그러나 큐의 무제한 적재, 콜백 교착, 메시지 경계 혼동은 별도로 검증해야 합니다.

기준 커밋
b22031faff65bf078fe8c1e560e94409a049fdfa
환경
macOS Apple Silicon / rustc 1.97.1
검사 방식
소스 검토, loopback 통신, 임시 파일 fixture
조사 범위 밖
실제 침해 포렌식, 운영 부하 실험, 커널·프로세스 메모리 덤프
01 · 완료소스 진단
02 · 완료제한된 재현 검사
03 · 설계 방향 정리패치 구현 미완료
04 · 미실시패치 후 성능 비교

02 / Findings

어디서, 왜 문제가 생겼는가

항목을 펼치면 관찰값과 원인, 적용 조건을 확인할 수 있습니다. 9개 항목에 10개 문제 재현이 담겨 있으며, “HTTP 프레이밍”에 두 검사가 포함됩니다.

F01

HTTP 파이프라인의 순환 대기

큐의 빈자리와 callback mutex를 서로 기다림
관찰 · 요청 1,100개 / 29,700바이트 → 핸들러 1,026개, 응답 38바이트에서 정체. 250ms 안에 stop 완료 안 됨.

수신 콜백은 mutex를 보유한 채 bounded 송신 큐의 빈자리를 기다립니다. writer는 on_sent를 위해 같은 mutex를 기다리느라 다음 항목을 소비하지 못합니다.

클라이언트가 응답을 읽는 상태에서도 재현했습니다. 단순한 느린 수신자 문제와 구분해야 합니다. 정확한 정체 개수는 실행 조건에 따라 달라질 수 있습니다.

소스: TCP read/write loop ↗
F02

프로토콜 이벤트의 무제한 적재

프레임 상한과 큐의 총 보유량은 다른 제한
관찰 · 콜백 처리 0개인 동안 2,048 프레임 / 8,437,760바이트 수신.

프로토콜 계층의 unbounded_channel이 느린 핸들러 뒤에 계속 이벤트를 쌓을 수 있습니다. 프레임 개수와 총 바이트를 함께 제한할 필요가 있습니다.

연결 콜백을 의도적으로 막은 조건입니다. 수치는 네트워크 수신 통계이며 RSS 측정값이나 실제 OOM의 증거가 아닙니다.

소스: protocol callback queue ↗
F03

TCP 쓰기 중 종료 지연

시작된 write_all이 취소 신호를 확인하지 않음
관찰 · 읽지 않는 상대에 8 MiB 전송. stop이 250ms 넘게 대기하다 상대 소켓 종료 후 풀림.

큐 항목 대기에는 취소 경로가 있지만 진행 중인 쓰기에는 없습니다. 종료는 쓰기, 콜백, 남은 큐의 정리까지 포함해 제한해야 합니다.

작은 송신 버퍼를 설정한 TCP 실험입니다. TLS writer에는 이미 쓰기 취소와 shutdown 제한이 있어 같은 누락으로 분류하지 않았습니다. WS는 별도의 코드 검토 대상입니다.

소스: TCP writer ↗
F04

HTTP timeout에서 빠진 업로드 시간

요청 송신을 완료한 다음에 타이머를 시작
관찰 · 25ms timeout 설정, 8 MiB 요청이 쓰기 단계에서 250ms 이후에도 대기.

timeout이 응답 receiver에만 적용됩니다. 송신 잠금과 큐 대기, 쓰기, 응답을 하나의 deadline 안에 넣고 timeout 후 FIFO의 응답 대응도 정리해야 합니다.

재현은 HTTP에서 수행했습니다. HTTPS에는 같은 구조가 소스에 있습니다. 업로드가 느린 정상 사용자도 변경된 timeout의 영향을 받을 수 있습니다.

소스: HTTP request_with_timeout ↗
F05

출력 직렬화의 CRLF 삽입

입력 파서와 출력 생성자의 검증 수준이 다름
관찰 · 요청 객체 1개가 요청 2개로 직렬화. header 값에서 추가 Set-Cookie 생성.

to_bytes()가 문자열을 그대로 이어 붙입니다. 오류를 반환하는 출력 API와 모든 전송 경로의 검증이 필요하며, 기존 API에 우회 경로가 남지 않아야 합니다.

외부 입력이 target이나 header에 도달할 수 있는 애플리케이션이 전제입니다. 모든 사용자 서비스에서 악용 가능하다는 뜻은 아닙니다.

소스: HTTP serialization ↗
F06

HTTP 프레이밍의 불일치

304 응답 경계와 TE+CL 조합 · 재현 검사 2개
관찰 · 304가 다음 200 응답 전체를 본문으로 소비. TE: identity와 CL을 함께 가진 요청 수용.

상태와 요청 method의 의미를 고려해 본문 길이를 정해야 합니다. HEAD는 요청 문맥이 필요합니다. 지원하지 않는 전송 코딩과 모호한 조합도 일관되게 거부해야 합니다.

304와 TE+CL은 재현했습니다. HEAD·1xx·204는 추가 검사 대상입니다. 프록시를 포함한 request smuggling 공격 사슬 전체를 검증한 결과는 아닙니다.

기준: RFC 9112 §6.3 ↗ · 파서 소스 ↗
F07

정적 캐시 갱신의 경로 이탈

하위 디렉터리 교체 후 mount 밖 파일을 읽음
관찰 · 조상 디렉터리를 외부 경로의 symlink로 교체한 뒤 watchdog이 외부 fixture를 캐시.

최종 파일의 symlink 여부만으로는 조상 디렉터리의 교체를 막지 못합니다. 검증과 실제 열기 사이의 경쟁까지 포함해 마운트 경계를 유지해야 합니다.

마운트 안 디렉터리를 교체할 로컬 파일시스템 권한이 필요합니다. 원격 URL traversal만으로 재현한 것이 아닙니다.

소스: static cache watchdog ↗
F08

UDP 유니캐스트의 송신자 경계

설정한 remote와 다른 소켓의 데이터도 콜백으로 전달
관찰 · 지정 서버와 무관한 로컬 소켓이 보낸 데이터그램 수신.

recv_from()의 송신자를 remote와 대조하지 않습니다. 유니캐스트의 peer 제한과 멀티캐스트·다중 송신자 허용 정책을 분리할 필요가 있습니다.

여러 송신자를 받으려는 사용법에는 호환성 영향이 있습니다. 주소 검사는 암호학적 인증을 대체하지 않습니다.

소스: UDP receive loop ↗
F09

미완료 TLS 핸드셰이크의 누적

초기 연결 단계의 deadline과 개수 상한 부재
관찰 · TLS 데이터를 보내지 않은 연결 128개가 세션 등록 후 250ms 뒤에도 남음.

소스에서도 서버 핸드셰이크의 별도 timeout과 개수 상한을 확인하지 못했습니다. 전체 연결과 동시 핸드셰이크를 분리해 제한하고 취소 경로를 보장하는 방향입니다.

250ms만으로 영구 잔류나 OOM을 입증한 것은 아닙니다. 관찰과 소스 경로를 함께 근거로 삼았습니다. WS/WSS 초기화는 코드 검토 대상입니다.

소스: TLS connection setup ↗

03 / Root cause, visualized

제한된 큐도 설계에 따라 멈출 수 있습니다

HTTP 교착에서 중요한 것은 버퍼 크기보다 잠금과 대기의 순서였습니다.

04 / Patch design

성능을 지키면서 바꿔야 할 것들

설계 예상입니다. 정상 트래픽의 처리량이나 지연이 얼마나 달라지는지는 아직 측정하지 않았습니다.

영역비용을 줄이는 방향달라질 수 있는 동작
프로토콜 큐개수 + 총 바이트 예산을 진입 시 관리과부하 시 대기·거부·연결 종료
TCP/WS 종료진행 중 쓰기도 취소, 정리 수명 제한종료 중 미전송 데이터 폐기 가능
HTTP 교착writer 진행과 콜백 전달의 순환 대기 제거콜백 시점·합산·순서 계약 검토
TLS/WS 초기화허용 시점에 개수 상한과 deadline 적용초과 연결 거부, 느린 핸드셰이크 timeout
HTTP/HTTPS timeout요청 전체를 하나의 deadline으로 관리대용량·저속 업로드 실패 시점 변화
TE+CL파싱 중 모호한 프레이밍 거부기존 identity 사용과의 호환성 변화
HEAD·1xx·204·304요청 method와 상태로 본문 경계 결정파서 문맥 API 추가 또는 변경
CRLF출력 경계의 선형 검증과 오류 반환잘못된 header/target의 호출 실패
정적 파일마운트·갱신 경로에서 접근 경계 유지디렉터리 교체 시 갱신 거부 가능
UDP유니캐스트 peer 제한, 멀티캐스트 분리다중 송신자 사용의 명시적 설정 필요
메모리 캐시저장 시 총량·엔트리 예산 관리한도 초과 쓰기 실패 또는 축출
예제 기본값loopback 바인딩, 외부 공개는 설정다른 기기의 기본 예제 접속 제한
PEM 파싱기반 라이브러리 API로 의존성 교체인증서 로딩 내부 변경
정상 부하의 효율과 과부하의 예측 가능성을 함께 검증해야 합니다.처리량과 p95·p99 지연만 보지 않고 RSS, 큐 최대 바이트, 거부율, timeout 비율, 종료 시간도 비교할 계획입니다. 현재 표에는 실측 성능 개선율을 넣지 않았습니다.

05 / Evidence & limits

테스트 통과가 의미하는 범위를 지킵니다

두 종류의 “통과”

기존 테스트 65개는 기존 동작을 확인합니다. 별도 감사 테스트는 문제 동작 검사 10개 + 입력 변이 검사 1개입니다. 현재 취약 동작을 확인하도록 작성해 일반 실행에서는 제외했습니다.

패치 단계에서는 열 개의 재현 기대값을 바꾸고 일반 회귀 검사에 포함해야 합니다. Criterion 테스트 모드 6개 성공은 성능 측정값이 아닙니다.

알려진 의존성 취약점 0건의 의미

잠금 파일의 의존성 136개, advisory 1,239개를 대조했습니다. DB 갱신일은 2026-09-02입니다. 자체 코드의 결함이나 미공개 취약점까지 없다는 뜻은 아닙니다.

rustls-pemfile 2.2.0 유지보수 중단과 chacha20 0.10.1 yanked 경고는 별도로 기록했습니다. RustSec 안내 ↗

cargo test --locked --test security_audit -- --ignored --nocapture --test-threads=1
cargo test --locked --all-targets --all-features
cargo audit --json

Audit probes   11 passed / 0 failed  = 10 problem reproductions + 1 mutation check
Existing tests 65 passed / 0 failed / 11 audit probes ignored
Parser inputs  20,000 deterministic mutations / no panic observed
Dependencies   0 known vulnerability matches / 2 warning entries

감사 명령은 별도 추가한 tests/security_audit.rs가 있는 조사 체크아웃에서 실행했습니다. 이 파일은 기준 커밋에 포함되지 않습니다. 변이 검사는 coverage-guided fuzzing이 아니며 Miri 검증은 완료하지 않았습니다. 정식 CVSS 점수나 CVE도 부여하지 않았습니다.

06 / Next validation

해결됐다고 말하기 전에

  1. 재현 검사를 차단 기대값으로 바꾸고 HEAD·1xx·204, TLS/WS 수명과 취소 후 자원 회수 검사를 추가합니다.
  2. 같은 머신과 트래픽으로 패치 전후 TCP·HTTP·TLS·WS의 처리량, 지연, CPU와 RSS를 비교합니다.
  3. 정상 부하와 과부하를 구분하고 성공·거부·timeout을 함께 보고합니다.
  4. 느린 상대, 멈춘 콜백, 반복 취소·재연결에서 종료 시간과 남은 task·session을 확인합니다.
  5. 기존 테스트, fmt, Clippy와 의존성 감사 결과, API·설정의 마이그레이션 내용을 남깁니다.

기록일 2026-09-07 · 이후 패치의 상태는 이 진단 스냅샷과 다를 수 있습니다. 게시일의 원고는 원인과 재현 결과를 기록하며 보안 패치·성능 개선 완료를 주장하지 않습니다.