Rust, 안전하다고? 놓치기 쉬운 보안 취약점 44가지

by DD
1개월 전
조회수 6

Canonical社의 Rust 기반 coreutils 재구현체인 uutils에서 44개의 CVE가 발견되었으며, borrow checker, clippy lints, cargo audit 등 기존 안전 장치로는 탐지되지 않음

TOCTOU(Time-of-Check-to-Time-of-Use) 취약점과 같이, 파일 시스템 경로를 두 번 이상 접근하는 경우 발생하는 문제에 대한 해결책 제시

UTF-8 문자열 처리와 관련된 문제, 특히 유닉스 시스템의 바이트 단위 처리가 필요한 경우의 안전한 코딩 방식 강조

오류 처리(Error Handling)의 중요성을 강조하며, `panic!` 대신 `?` 연산자 사용 및 구체적인 오류 반환을 권장

TOCTOU(Time-of-Check-to-Time-of-Use) 취약점 분석

본문에서는 TOCTOU(Time-of-Check-to-Time-of-Use) 취약점을 야기하는 주요 원인으로, `fs::metadata`와 같은 함수가 파일 디스크립터(File Descriptor) 대신 경로(Path)를 사용하기 때문이라고 지적한다. 특히, 경로를 두 번 이상 접근하는 경우, 공격자가 심볼릭 링크(Symbolic Link)를 이용하여 중간에 경로를 변경할 수 있다. 해결책으로, 파일 디스크립터를 사용하거나, `OpenOptions::create_new(true)`를 통해 파일 생성 시 권한을 설정하는 방법을 제시한다. 또한, 경로 비교 시 문자열 비교 대신 `(dev, inode)` 쌍을 사용하여 파일 시스템의 실제 identity를 확인하는 방법을 제안한다.

UTF-8 문자열 처리의 함정

유닉스 시스템에서 파일 경로, 환경 변수, 그리고 `cut`, `comm`, `tr`과 같은 도구들은 바이트 단위로 데이터를 처리한다. 하지만 Rust의 `String`과 `&str`은 UTF-8을 기본으로 사용하므로, 이 둘 사이의 변환 과정에서 데이터 손실(Data Corruption)이 발생할 수 있다. 특히, `from_utf8_lossy`를 사용하면 유효하지 않은 바이트를 U+FFFD로 대체하여 문제를 일으킬 수 있다. 따라서, 바이트 단위의 데이터 처리가 필요한 경우에는 `OsStr` 또는 `&[u8]`을 사용하고, `write_all`을 통해 직접 바이트를 출력하는 것이 권장된다.

오류 처리(Error Handling) 및 예외 상황 관리

Rust 코드에서 `unwrap`, `expect`, 인덱싱, 그리고 unchecked 산술 연산은 잠재적인 서비스 거부 공격(Denial of Service, DoS)의 원인이 될 수 있다. 특히, 신뢰할 수 없는 입력을 처리하는 경우, 이러한 연산들은 치명적인 결과를 초래할 수 있다. 따라서, `?` 연산자, `get`, `checked_*`, `try_from`을 사용하여 오류를 처리하고, 오류 정보를 버리지 않도록 주의해야 한다. 또한, `panic!` 대신 오류를 반환하고, 오류 발생 시에는 구체적인 오류 정보를 제공하여 문제 해결을 용이하게 해야 한다.

GNU coreutils와의 호환성 유지

uutils는 GNU coreutils의 Rust 재구현체로서, 기존 쉘 스크립트와의 호환성을 위해 버그-대-버그(Bug-for-Bug) 호환성을 유지하는 것이 중요하다. 이는 exit code, 오류 메시지, edge case, 그리고 옵션 의미론(Option Semantics)까지 포함한다. uutils는 현재 GNU coreutils의 테스트 스위트를 사용하여 호환성을 검증하고 있다. 이는 기존 쉘 스크립트가 uutils의 예상치 못한 동작으로 인해 오작동하는 것을 방지하기 위한 중요한 조치이다.

신뢰 경계(Trust Boundary) 통과 전 입력 검증

chroot 유틸리티의 경우, 신뢰 경계를 넘기 전에 입력값을 검증하는 것이 중요하다. 특히, `get_user_by_name` 함수가 새로운 루트 파일 시스템에서 공유 라이브러리를 로드하는 경우, 공격자는 chroot 환경 내에서 코드를 실행할 수 있다. 따라서, 신뢰 경계를 넘기 전에 입력을 검증하고, 잠재적인 공격 벡터를 차단해야 한다. 이는 코드 실행 전에 입력값을 확인하는 것의 중요성을 강조한다.

Bugs Rust Won't Catch