게시일: 2025년 3월 19일
Skrifa는 Rust로 작성되었으며 Chrome에서 모든 사용자를 위해 글꼴 처리를 안전하게 할 수 있도록 FreeType을 대체하기 위해 만들어졌습니다. Skrifa는 Rust의 메모리 안전성을 활용하여 Chrome에서 글꼴 기술 개선사항을 더 빠르게 반복할 수 있도록 지원합니다. FreeType에서 Skrifa로 이동하면 글꼴 코드를 변경할 때 민첩하고 대담하게 대처할 수 있습니다. 이제 보안 버그를 수정하는 데 훨씬 적은 시간을 소비하므로 업데이트가 더 빨라지고 코드 품질이 향상됩니다.
이 게시물에서는 Chrome이 FreeType에서 벗어난 이유와 이 이동으로 가능해진 개선사항의 흥미로운 기술적 세부정보를 공유합니다.
FreeType을 대체하는 이유는 무엇인가요?
웹은 사용자가 다양한 신뢰할 수 없는 소스에서 신뢰할 수 없는 리소스를 가져와도 작동하고 안전할 것이라는 기대가 있다는 점에서 고유합니다. 이 가정은 일반적으로 올바르지만 사용자에게 이 약속을 지키려면 비용이 발생합니다. 예를 들어 웹 글꼴 (네트워크를 통해 제공되는 글꼴)을 안전하게 사용하기 위해 Chrome은 다음과 같은 여러 보안 완화 조치를 사용합니다.
- 글꼴 처리는 2의 규칙에 따라 샌드박스 처리됩니다. 글꼴이 신뢰할 수 없고 소비 코드가 안전하지 않기 때문입니다.
- 글꼴은 처리 전에 OpenType Sanitizer를 통과합니다.
- 글꼴 압축 해제 및 처리에 관련된 모든 라이브러리는 퍼즈 테스트를 거칩니다.
Chrome은 FreeType과 함께 제공되며 Android, ChromeOS, Linux에서 기본 글꼴 처리 라이브러리로 사용합니다. 즉, FreeType에 취약점이 있으면 많은 사용자가 노출됩니다.
FreeType 라이브러리는 Chrome에서 측정항목을 계산하고 글꼴에서 힌트가 지정된 윤곽선을 로드하는 데 사용됩니다. 전반적으로 FreeType 사용은 Google에 큰 도움이 되었습니다. 복잡한 작업을 잘 수행하며, Google은 이를 광범위하게 사용하고 다시 기여합니다. 하지만 안전하지 않은 코드로 작성되었으며 악성 입력이 발생할 가능성이 적었던 시기에 시작되었습니다. 퍼징으로 발견된 문제의 흐름을 따라가는 데만 Google에서 최소 0.25명의 전임 소프트웨어 엔지니어가 필요합니다. 더 심각한 문제는 모든 것을 찾지 못하거나 코드가 사용자에게 제공된 후에만 찾을 수 있다는 것입니다.
이러한 문제 패턴은 FreeType에만 국한된 것이 아닙니다. 최고의 소프트웨어 엔지니어를 사용하고, 모든 변경사항을 코드 검토하고, 테스트를 요구하는 경우에도 안전하지 않은 다른 라이브러리에서 문제가 발생하는 것을 확인할 수 있습니다.
문제가 계속 발생하는 이유
FreeType의 보안을 평가할 때 다음과 같은 세 가지 주요 문제 클래스가 발생하는 것으로 확인되었습니다 (전체 목록은 아님).
안전하지 않은 언어 사용
| 패턴/문제 | 예 |
|---|---|
| 수동 메모리 관리 |
|
| 확인되지 않은 배열 액세스 | CVE-2022-27404 |
| 정수 오버플로 | CFF 드로잉 및 힌팅의 TrueType 힌팅을 위해 삽입된 가상 머신 실행 중 https://issues.oss-fuzz.com/issues?q=FreeType%20Integer-overflow |
| 0으로 설정된 할당과 0이 아닌 할당의 잘못된 사용 | https://gitlab.freedesktop.org/freetype/freetype/-/merge_requests/94에서 논의, 이후 퍼저 문제 8개 발견 |
| 잘못된 캐스트 | 매크로 사용에 관한 다음 행을 참고하세요. |
프로젝트 관련 문제
| 패턴/문제 | 예 |
|---|---|
| 매크로로 인해 명시적 크기 입력이 누락됨 |
|
| 새 코드는 방어적으로 작성하더라도 일관되게 버그를 추가합니다. |
|
| 테스트 부족 |
|
종속 항목 문제
퍼징을 통해 FreeType이 종속된 라이브러리(예: bzip2, libpng, zlib)의 문제가 반복적으로 확인되었습니다. 예를 들어 freetype_bdf_fuzzer: inflate에서 초기화되지 않은 값 사용을 비교해 보세요.
퍼징이 충분하지 않음
퍼징(무작위로 생성된 잘못된 입력을 포함한 광범위한 입력을 사용한 자동 테스트)은 Chrome의 안정화 버전에 포함되는 여러 유형의 문제를 찾기 위한 것입니다. Google의 oss-fuzz 프로젝트의 일환으로 FreeType을 퍼징합니다. 문제를 발견하지만 다음과 같은 이유로 글꼴은 퍼징에 다소 강한 것으로 입증되었습니다.
폰트 파일은 여러 유형의 정보를 포함하므로 동영상 파일과 비교할 수 있을 정도로 복잡합니다. 글꼴 파일은 여러 테이블의 컨테이너 형식으로, 각 테이블은 텍스트와 글꼴을 함께 처리하여 화면에 올바르게 배치된 글리프를 생성하는 데 서로 다른 용도로 사용됩니다. 글꼴 파일에는 다음이 포함됩니다.
- 글꼴 이름, 가변 글꼴의 매개변수와 같은 정적 메타데이터
- 유니코드 문자에서 글리프로의 매핑입니다.
- 글리프의 화면 레이아웃을 위한 복잡한 규칙 집합과 문법입니다.
- 시각적 정보: 화면에 배치된 글리프의 모양을 설명하는 글리프 모양 및 이미지 정보입니다.
- 시각적 테이블에는 글리프 모양을 변경하기 위해 실행되는 미니 프로그램인 TrueType 힌팅 프로그램이 포함될 수 있습니다.
- CFF 렌더링 엔진에서 실행되는 필수 곡선 그리기 및 힌트 지정 명령인 CFF 또는 CFF2 테이블의 문자열입니다.
글꼴 파일에는 자체 프로그래밍 언어와 상태 머신 처리가 있는 것과 같은 복잡성이 있어 이를 실행하려면 특정 가상 머신이 필요합니다.
형식의 복잡성으로 인해 퍼징에는 글꼴 파일의 문제를 찾는 데 단점이 있습니다.
다음과 같은 이유로 우수한 코드 적용 범위나 퍼저 진행률을 달성하기 어렵습니다.
- 간단한 비트 반전/이동/삽입/삭제 스타일 뮤테이터를 사용하여 TrueType 힌트 프로그램, CFF 문자열, OpenType 레이아웃을 퍼징하면 모든 상태 조합에 도달하기가 어렵습니다.
- 퍼징은 최소한 부분적으로 유효한 구조를 생성해야 합니다. 무작위 변이는 거의 발생하지 않으므로 특히 더 깊은 수준의 코드의 경우 적절한 커버리지를 달성하기가 어렵습니다.
- ClusterFuzz 및 oss-fuzz의 현재 퍼징 노력은 아직 구조 인식 변형을 사용하지 않습니다. 문법 또는 구조를 인식하는 변이 생성기를 사용하면 개발하는 데 시간이 더 오래 걸리고 검색 공간의 일부를 놓칠 수 있는 기회가 도입되는 대신 초기에 거부되는 변형을 생성하지 않는 데 도움이 될 수 있습니다.
퍼징이 진행되려면 여러 테이블의 데이터가 동기화되어야 합니다.
- 퍼저의 일반적인 변이 패턴은 부분적으로 유효한 데이터를 생성하지 않으므로 많은 반복이 거부되고 진행이 느려집니다.
- 글리프 매핑, OpenType 레이아웃 테이블, 글리프 그리기는 서로 연결되어 있고 서로 종속되어 있어 퍼징으로 도달하기 어려운 조합 공간을 형성합니다.
- 예를 들어 심각도가 높은 tt_face_get_paint COLRv1 취약점을 찾는 데 10개월 이상이 걸렸습니다.
Google은 최선을 다했지만 글꼴 보안 문제가 반복적으로 최종 사용자에게 도달했습니다. FreeType을 Rust 대안으로 대체하면 여러 전체 취약점 클래스가 방지됩니다.
Chrome에서 Skrifa 사용하기
Skia는 Chrome에서 사용하는 그래픽 라이브러리입니다. Skia는 FreeType을 사용하여 글꼴에서 메타데이터와 글자 모양을 로드합니다. Skrifa는 Rust 라이브러리로, Fontations 라이브러리 패밀리의 일부이며 Skia에서 사용하는 FreeType 부분을 안전하게 대체합니다.
FreeType을 Skia로 전환하기 위해 Chrome팀은 Skrifa를 기반으로 새로운 Skia 글꼴 백엔드를 개발하고 사용자에게 점진적으로 변경사항을 출시했습니다.
- Chrome 128(2024년 8월)에서는 안전한 체험판으로 덜 흔히 사용되는 글꼴 형식(예: 컬러 글꼴 및 CFF2)에서 사용할 수 있도록 Fontations를 사용 설정했습니다.
- Chrome 133(2025년 2월)에서는 Linux, Android, ChromeOS의 모든 웹 글꼴 사용과 Windows 및 Mac의 대체 글꼴로 웹 글꼴을 사용하는 경우(시스템에서 글꼴 형식을 지원하지 않지만 Chrome에서 표시해야 하는 경우)에 Fontations를 사용 설정했습니다.
Chrome과의 통합을 위해 Chrome 보안팀에서 도입한 코드베이스에 Rust를 원활하게 통합합니다.
향후에는 Linux와 ChromeOS를 시작으로 Android까지 운영체제 글꼴에도 Fontations를 사용할 예정입니다.
안전이 최우선
기본 목표는 메모리에 대한 범위를 벗어난 액세스로 인해 발생하는 보안 취약점을 줄이는 것입니다 (이상적으로는 제거). unsafe 코드 블록을 피하면 Rust에서 기본적으로 이를 제공합니다.
Google의 성능 목표를 달성하려면 현재 안전하지 않은 작업(임의 바이트를 강력한 유형의 데이터 구조로 재해석)을 실행해야 합니다. 이를 통해 불필요한 복사본을 실행하지 않고도 글꼴 파일에서 데이터를 읽을 수 있으며 빠른 글꼴 파서를 생성하는 데 필수적입니다.
안전하지 않은 코드를 방지하기 위해 이 책임을 bytemuck에 아웃소싱했습니다. bytemuck은 이 목적으로 특별히 설계된 Rust 라이브러리이며 생태계 전반에서 널리 테스트되고 사용됩니다. 바이트먹에서 원시 데이터 재해석을 집중하면 이 기능이 한곳에 있고 감사되므로 이 목적으로 안전하지 않은 코드를 반복하지 않아도 됩니다. 안전한 트랜스뮤트 프로젝트는 이 기능을 Rust 컴파일러에 직접 통합하는 것을 목표로 하며, 이 기능이 제공되는 즉시 전환할 예정입니다.
정확성이 중요함
Skrifa는 대부분의 데이터 구조가 변경 불가능하도록 설계된 독립적인 구성요소로 빌드됩니다. 이렇게 하면 가독성, 유지 관리성, 멀티스레딩이 개선됩니다. 또한 코드를 단위 테스트에 더 적합하게 만듭니다. Google에서는 이 기회를 활용하여 하위 수준 파싱 루틴부터 상위 수준 힌팅 가상 머신까지 전체 스택을 다루는 약 700개의 단위 테스트 모음을 제작했습니다.
정확성은 충실도도 의미하며 FreeType은 고품질 윤곽선 생성으로 높은 평가를 받고 있습니다. 이 품질과 일치해야 적합한 대체품이 될 수 있습니다. 이를 위해 다양한 구성에서 글꼴 파일 배치의 Skrifa와 FreeType 출력을 비교하는 맞춤 도구인 fauntlet를 빌드했습니다. 이를 통해 품질 회귀를 방지할 수 있습니다.
또한 Chromium에 통합되기 전에 Skia에서 다양한 픽셀 비교를 실행하여 FreeType 렌더링을 Skrifa 및 Skia 렌더링과 비교하여 모든 필수 렌더링 모드(다양한 앤티앨리어싱 및 힌팅 모드)에서 픽셀 차이가 절대적으로 최소화되도록 했습니다.
퍼즈 테스트는 소프트웨어가 잘못된 입력과 악의적인 입력에 어떻게 반응하는지 확인하는 데 중요한 도구입니다. Google은 2024년 6월부터 새로운 코드를 지속적으로 퍼징해 왔습니다. 여기에는 Rust 라이브러리 자체와 통합 코드가 포함됩니다. 퍼저가 현재까지 39개의 버그를 발견했지만 이 중 보안에 심각한 버그는 없습니다. 원치 않는 시각적 결과나 제어된 비정상 종료가 발생할 수 있지만 악용 가능한 취약점으로 이어지지는 않습니다.
힘차게 전진하세요.
텍스트에 Rust를 사용하기 위해 노력한 결과에 매우 만족합니다. 사용자에게 더 안전한 코드를 제공하고 개발자 생산성을 높이는 것은 Google에 큰 이점입니다. Google은 텍스트 스택에서 Rust를 사용할 기회를 계속 모색할 계획입니다. 자세한 내용은 Oxidize에서 Google Fonts의 향후 계획을 확인하세요.