웹 글꼴의 메모리 안전

Dominik Röttsches
Dominik Röttsches
Rod Sheeter
Rod Sheeter
Chad Brokaw
Chad Brokaw

게시일: 2025년 3월 19일

Skrifa는 Rust로 작성되었으며 모든 사용자를 위해 Chrome에서 글꼴 처리를 안전하게 만들기 위해 FreeType를 대체하기 위해 만들어졌습니다. Skifra는 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개의 fuzzer 문제가 발견됨
잘못된 전송 매크로 사용에 관한 다음 행을 참고하세요.

프로젝트별 문제

패턴/문제
매크로로 인해 명시적인 크기 유형이 누락됨
  • FT_READ_*FT_PEEK_*와 같은 매크로는 사용 중인 정수 유형을 숨겨 명시적 크기 (int16_t 등)가 있는 C99 유형이 사용되지 않는다는 점을 숨깁니다.
새 코드는 방어적으로 작성하더라도 버그가 지속적으로 추가됩니다.
  • COLRv1 및 OT-SVG 지원으로 모두 문제가 발생함
  • 퍼징으로 일부(전체가 아님) #32421, #52404가 발견됨
테스트 부족
  • 테스트 글꼴을 만드는 것은 어렵고 시간이 많이 걸립니다.

종속 항목 문제

퍼징을 통해 FreeType가 종속되는 라이브러리(예: bzip2, libpng, zlib)에서 문제가 반복적으로 발견되었습니다. 예를 들어 freetype_bdf_fuzzer: inflate에서 초기화되지 않은 값 사용을 비교해 보세요.

퍼징만으로는 충분하지 않음

퍼징은 무작위로 잘못된 입력을 포함한 다양한 입력을 사용한 자동 테스트로, Chrome의 안정화 버전에 발생하는 다양한 유형의 문제를 찾는 것을 목표로 합니다. Google은 FreeType를 Google의 oss-fuzz 프로젝트의 일부로 퍼징합니다. 문제를 찾기는 하지만 다음과 같은 이유로 글꼴은 포 fuzzing에 다소 저항력이 있는 것으로 확인되었습니다.

글꼴 파일은 다양한 유형의 정보가 포함되어 있어 동영상 파일과 비슷하게 복잡합니다. 글꼴 파일은 여러 테이블의 컨테이너 형식으로, 각 테이블은 텍스트와 글꼴을 함께 처리하여 화면에 올바르게 배치된 글리프를 생성하는 데 서로 다른 목적을 제공합니다. 글꼴 파일에는 다음이 포함됩니다.

  • 가변 글꼴의 글꼴 이름 및 매개변수와 같은 정적 메타데이터
  • 유니코드 문자와 글리프 간의 매핑입니다.
  • 글리프의 화면 레이아웃을 위한 복잡한 규칙 집합 및 문법입니다.
  • 시각적 정보: 화면에 배치된 글리프의 모양을 설명하는 글리프 도형 및 이미지 정보입니다.
    • 시각적 테이블에는 글리프 모양을 변경하기 위해 실행되는 미니 프로그램인 TrueType 힌팅 프로그램이 포함될 수 있습니다.
    • CFF 또는 CFF2 테이블의 문자열로, CFF 렌더링 엔진에서 실행되는 필수 곡선 그리기 및 힌팅 안내입니다.

글꼴 파일에는 자체 프로그래밍 언어 및 상태 머신 처리와 동일한 복잡성이 있으므로 특정 가상 머신이 이를 실행해야 합니다.

형식이 복잡하여 폰트 파일에서 문제를 찾는 데는 불충분합니다.

다음과 같은 이유로 우수한 코드 적용 범위 또는 fuzzer 진행률을 달성하기가 어렵습니다.

  • 간단한 비트 플립/시프트/삽입/삭제 스타일의 변형자를 사용하여 TrueType 힌팅 프로그램, CFF 문자열, OpenType 레이아웃을 퍼징하면 모든 상태 조합에 도달하기가 어렵습니다.
  • 퍼징은 적어도 부분적으로 유효한 구조를 생성해야 합니다. 무작위 변형은 거의 그러지 않으므로 특히 코드의 더 깊은 수준에서 우수한 적용 범위를 달성하기가 어렵습니다.
  • 현재 ClusterFuzz 및 oss-fuzz의 퍼징 작업은 아직 구조 인식 변형을 사용하지 않습니다. 문법 또는 구조 인식 변형자를 사용하면 초기에 거부되는 변형이 생성되는 것을 방지할 수 있지만, 개발하는 데 더 많은 시간이 걸리고 검색 공간의 일부를 놓칠 수 있습니다.

퍼징을 진행하려면 여러 테이블의 데이터가 동기화되어야 합니다.

  • 퍼저의 일반적인 변형 패턴은 부분적으로 유효한 데이터를 생성하지 않으므로 많은 반복이 거부되고 진행 속도가 느려집니다.
  • 글리프 매핑, OpenType 레이아웃 테이블, 글리프 그리기는 서로 연결되어 있고 서로 종속되므로 푸징으로 도달하기 어려운 조합 공간을 형성합니다.
  • 예를 들어 심각도가 높은 tt_face_get_paintCOLRv1 취약점을 찾는 데 10개월 이상이 걸렸습니다.

Google은 최선을 다했지만 글꼴 보안 문제가 최종 사용자에게 반복적으로 전달되었습니다. FreeType를 Rust 대안으로 대체하면 여러 가지 취약점 클래스가 방지됩니다.

Chrome에서 Skrifa 사용하기

Skia는 Chrome에서 사용하는 그래픽 라이브러리입니다. Skia는 FreeType를 사용하여 글꼴에서 메타데이터와 문자 모양을 로드합니다. SkrifaFontations 라이브러리 제품군의 일부인 Rust 라이브러리로, Skia에서 사용하는 FreeType의 일부를 안전하게 대체합니다.

FreeType를 Skia로 전환하기 위해 Chrome팀은 Skrifa를 기반으로 새로운 Skia 글꼴 백엔드를 개발하고 변경사항을 사용자에게 점진적으로 출시했습니다.

Chrome에 통합하기 위해 Chrome 보안팀에서 도입한 코드베이스에 Rust를 원활하게 통합합니다.

향후 Linux 및 ChromeOS를 시작으로 Android에서 운영체제 글꼴도 Fontations으로 전환할 예정입니다.

무엇보다도 안전

기본 목표는 메모리 범위를 벗어난 액세스로 인해 발생하는 보안 취약점을 줄이거나 (이상적으로는 제거) 하는 것입니다. 안전하지 않은 코드 블록을 피하는 한 Rust는 이를 기본적으로 제공합니다.

성능 목표를 달성하려면 현재 안전하지 않은 한 가지 작업을 실행해야 합니다. 임의의 바이트를 강력한 유형의 데이터 구조로 재해석하는 작업입니다. 이를 통해 불필요한 복사를 실행하지 않고도 글꼴 파일에서 데이터를 읽을 수 있으며, 빠른 글꼴 파서를 생성하는 데 필수적입니다.

자체적으로 안전하지 않은 코드를 방지하기 위해 이 책임을 bytemuck에 아웃소싱했습니다. 이 라이브러리는 이 목적을 위해 특별히 설계되었으며 생태계 전반에서 광범위하게 테스트되고 사용되는 Rust 라이브러리입니다. 원시 데이터 재해석을 bytemuck에 집중하면 이 기능을 한곳에서 감사하고 목적으로 인해 안전하지 않은 코드를 반복하지 않을 수 있습니다. 안전 변환 프로젝트는 이 기능을 Rust 컴파일러에 직접 통합하는 것을 목표로 하며, 사용 가능해지는 대로 전환할 예정입니다.

정확성의 중요성

Skrifa는 대부분의 데이터 구조가 변경 불가능한 방식으로 설계된 독립적인 구성요소로 빌드됩니다. 이렇게 하면 가독성, 유지관리성, 멀티스레딩이 개선됩니다. 또한 코드를 단위 테스트에 더 적합하게 만듭니다. Google은 이 기회를 활용하여 하위 수준 파싱 루틴에서 상위 수준 힌트 가상 머신에 이르기까지 전체 스택을 다루는 약 700개의 단위 테스트 모음을 제작했습니다.

정확성은 또한 충실도를 의미하며 FreeType는 고품질 윤곽선을 생성하는 것으로 높이 평가됩니다. 적절한 대체품이 되려면 이 품질을 충족해야 합니다. 이를 위해 Google은 다양한 구성에서 글꼴 파일 일괄 처리에 대한 Skrifa 및 FreeType의 출력을 비교하는 맞춤 도구인 fauntlet을 빌드했습니다. 이를 통해 품질의 회귀를 방지할 수 있습니다.

또한 Chromium에 통합하기 전에 Skia에서 광범위한 픽셀 비교를 실행하여 FreeType 렌더링을 Skrifa 및 Skia 렌더링과 비교하여 모든 필수 렌더링 모드(다양한 안티앨리어싱 및 힌팅 모드 전반)에서 픽셀 차이가 최소화되도록 했습니다.

퍼징 테스트는 소프트웨어가 잘못된 형식의 악의적인 입력에 어떻게 반응하는지 확인하는 데 중요한 도구입니다. YouTube는 2024년 6월부터 새로운 코드에 대한 퍼징을 지속적으로 진행해 왔습니다. 여기에서는 Rust 라이브러리 자체와 통합 코드를 다룹니다. 이 글을 작성하는 시점에서 fuzzer는 39개의 버그를 발견했지만 이 중 보안상 중요한 버그는 없었습니다. 원치 않는 시각적 결과나 제어된 비정상 종료가 발생할 수 있지만 악용 가능한 취약점으로 이어지지는 않습니다.

힘차게 전진하세요.

텍스트에 Rust를 사용하기 위한 노력의 결과에 매우 만족합니다. 사용자에게 더 안전한 코드를 제공하고 개발자 생산성을 높이는 것은 Google에 큰 도움이 됩니다. Google은 텍스트 스택에서 Rust를 사용할 기회를 계속 모색할 계획입니다. 자세한 내용은 Oxidize에서 Google Fonts의 향후 계획을 확인하세요.