블로그 | CTO 사무실

C 너머

수십 년 동안 C 언어와 그 직계 후손 언어는 시스템 프로그래밍을 위한 프로그래밍 언어로 선택되어 왔습니다. 1970년대 초에 C 언어가 개발되었을 때, 그것은 어셈블리 언어에 비해 중요한 진전이었습니다. 50년이 지난 지금, 우리는 더 잘할 수 있습니다.

1970년대와 그 이후 오랜 세월 동안 대부분의 사람들은 컴퓨터 보안에 관심을 두지 않았습니다. 인터넷의 첫 번째 주요 보안 취약점은 그로부터 몇 년 후인 1988년에 발생했습니다. 이는 인터넷 웜 으로 알려졌으며, 메모리 내의 배열이 범위를 벗어나 인덱싱되는 버퍼 오버플로우를 이용했습니다.  

C++를 포함하는 C 언어 계열에서는 배열에 대한 범위 검사를 하지 않습니다. 배열에 올바르게 접근하는 것은 프로그래머의 몫입니다. 따라서 버퍼 오버플로 오류가 흔합니다. 더 나쁜 것은, 의도적으로 버퍼 오버플로를 발생시켜서 자신이 접근해서는 안 될 메모리에 접근할 수 있다는 것입니다.

버퍼 오버플로는 메모리 불안정성 의 한 예일 뿐입니다. 관련된 다른 예로는 포인터 산술과 매달린 포인터(일명 사용 후 해제 버그)가 있습니다. 오늘날 우리는 다양한 기술을 사용하는 많은 프로그래밍 언어를 가지고 있는데, 이 언어들로 작성된 모든 프로그램은 메모리 안전 문제가 없도록 보장합니다. C 계열 언어에서는 그런 보장이 없습니다. 메모리 안전성은 이런 언어의 설계 목표가 아니었습니다.

보안 취약점의 약 70%는 메모리 보안 위반으로 인해 발생합니다. 이 주장은 압도적인 데이터에 의해 뒷받침됩니다. 메모리 안전성은 다음을 고려합니다.

  • Google Project Zero 2021 추정에 따르면, 악용된 취약점의 67%가 실제로 공개되어 감지되었습니다.
  • 안드로이드 취약점의 90%( 구글 기준 ). 최근 Rust를 사용하면서 이 숫자는 줄어들고 있지만 원격으로 악용 가능한 취약점의 89%는 여전히 메모리 안전과 관련되어 있습니다.
  • Microsoft 취약점의 70%(Microsoft 기준).
  • 최근 Apple 취약점이 대부분 수정되었습니다( Catalina , Big Sur , Monterey , Safari , iOS , tvOS , watchOS ).

2022년 7월, Chrome 103.0.5060.134에서 수정된 취약점 중 5/6이 메모리 안전 문제였습니다. 분명히 메모리 안전 버그를 없애는 것은 엄청난 도움이 될 것입니다. 업계에서는 오랫동안 메모리에 안전한 프로그래밍 언어를 사용하는 방법을 알고 있었습니다. 역사적으로 문제는 항상 성능 측면에서 발생하는 비용이었습니다. 메모리 안전성의 중요성으로 인해 사람들은 전통적인 오버헤드 없이 메모리 안전성을 달성하기 위한 새로운 전략을 연구해 왔습니다. 오늘날 우리는 런타임 비용이 거의 없거나 전혀 없이 메모리 안전 오류를 제거하는 방법을 알고 있습니다. Rust와 같은 언어는 유형 시스템 디자인의 혁신을 사용하여 비용이 많이 드는 런타임 지원 없이도 메모리 안전성을 보장합니다.

이는 피할 수 없는 결론으로 이어진다. C/C++(또는 보다 일반적으로, 메모리가 안전하지 않은 언어)로 새로운 시스템 코드를 작성하는 것을 중단하라는 것이다.

이는 기존 코드를 대대적으로 무차별적으로 다시 작성하라는 요구가 아닙니다. 기존 소프트웨어를 교체하는 데는 비용이 많이 들고 위험도 따릅니다. 하지만 업계에서는 기존 코드베이스에 메모리를 안전하게 보호하지 못하는 코드를 더 추가하여 문제를 악화시키는 것을 멈춰야 합니다. 기존 코드의 경우 가장 민감한 구성 요소를 다시 작성하는 것을 우선시합니다. 즉, 신뢰할 수 없는 사용자 입력을 검증하거나 사용하는 구성 요소, 권한이 있는 컨텍스트에서 실행되는 구성 요소, 샌드박스 외부에서 실행되는 구성 요소 등입니다.

이런 입장은 널리 받아들여지고 있지만 여전히 일부에서는 논란의 여지가 있습니다. 현 상태를 지지하는 일반적인 주장과 이에 대한 우리의 대응은 다음과 같습니다.

  • 버그는 프로그래밍 언어에만 국한되지 않습니다.  
    • 대부분의 취약점은 메모리 안전 버그로 인해 발생합니다. Rust와 같은 메모리 안전 언어에서는 메모리 안전 버그가 발생할 수 없습니다. 따라서 메모리 안전 언어를 사용하면 대부분의 취약점이 처음부터 생성되는 것을 방지할 수 있습니다.
  • 코드 검토를 통해 버그를 발견할 수 있습니다.
    • 연구에 따르면 코드 검토를 통해 개선이 이루어질 수는 있지만, 종종 많은 문제점을 놓치는 것으로 나타났습니다. 모든 것을 찾아내지는 못하는 것이 확실합니다.
  • 안전하지 않은 모듈은 전체적인 요점을 무의미하게 만듭니다.
    • 안전하지 않은 코드는 매우 작은 코드 섹션에 명확하게 구분되어 있으므로 리소스를 해당 코드의 유효성 검사에 집중할 수 있습니다.
  • 메모리 안전 언어 구현에는 버그가 있을 수 있습니다.
    • 물론이죠. 하지만 확률은 훨씬 낮습니다. 컴파일러는 비교적 작고 엄격하게 테스트됩니다. 컴파일러를 수정하면 단일 프로그램의 버그를 수정하는 것과 달리 해당 컴파일러로 컴파일된 모든 프로그램이 자동으로 수정됩니다.
  • 이런 실수는 초보자나 무능한 프로그래머만이 저지른다.
    • 반면, 위의 데이터는 Chrome 브라우저와 Windows, MacOS 운영 체제 등의 주요 시스템 프로젝트에서 가져온 것입니다. 이러한 프로젝트에는 업계에서 가장 유능하고 경험이 풍부한 개발자들이 투입되었지만, 여전히 메모리 안전성과 관련된 문제가 나타납니다.
  • 이러한 문제는 모범 사례를 따르면 피할 수 있습니다.
    • 위를 참조하세요. 언급된 팀은 매우 엄격한 관행을 따르고 사용 가능한 최고의 툴을 사용합니다.
  • 모든 코드를 다시 작성하는 것은 비현실적이다.
    • 모든 코드를 완전히 다시 작성해야 한다고 주장하는 사람은 없습니다. 오히려 핵심 요소(높은 권한, 넓은 공격 표면, 보안 보장의 핵심)에 초점을 맞추고 메모리 안전 언어로 새 코드를 작성하는 접근 방식이 권장됩니다.
  • 성능이 부족합니다.
    • Rust의 성능은 C/C++와 거의 비슷합니다. 사소한 차이점 때문에 보안 위험이 정당화되는 것은 아닙니다. Rust 프로그램이 더 빨라질 수 있는 상황도 있습니다.
  • Rust와 같은 메모리 안전 시스템 프로그래밍 언어는 아직 너무 새롭습니다.
  • 정적 분석, 퍼징, 샌드박싱과 같은 다른 솔루션도 있습니다.

메모리 불안정성과 이에 대한 반응 정량화 참조 위의 사항들에 대한 자세한 논의는 다음을 참조하세요.

이러한 반대에도 불구하고, 필요한 변화에 대한 추진력이 커지고 있습니다. 예를 들어 , 오픈소스 소프트웨어 보안 재단 (Linux 재단이 지원)에 대규모 투자가 이루어지고 있습니다. 메모리 안전은 미국에서 논의되었습니다. 상원 보고서와 NSA 에 따르면. 소비자 보고서 또한 기업과 국가 기관에 일련의 권장 사항을 제공하여 이러한 움직임을 가속화할 수 있는 다양한 인센티브를 파악하기 위해 노력합니다.

요약하자면:

지난 50년 동안 사회에 대한 컴퓨팅의 중요성이 엄청나게 커졌습니다. 최근 수십 년 동안 컴퓨팅 인프라에 대한 위협 환경도 근본적으로 바뀌었습니다. 하지만 우리가 컴퓨팅 시스템을 구축하는 데 사용하는 프로그래밍 언어는 그에 따라 변하지 않았습니다. 메모리 안전성의 부족은 소프트웨어 보안 취약성의 가장 큰 단일 원인입니다. 이는 특정 유형의 소프트웨어에만 나타나는 현상이 아니며, 메모리를 안전하게 보호하지 못하는 언어가 사용되는 모든 곳에서 메모리 안전 문제가 발생합니다. 숫자로 따져 보면 다른 취약성 유형은 이에 근접하지 못합니다.

이제 우리는 이 점차 커지는 문제를 해결할 수 있는 수단을 갖게 되었고, 업계 전체가 이를 실행하는 것이 중요합니다. 다행히도 상황에 대한 인식이 확산되고 있지만, 문제는 시급하며 허비할 시간이 없습니다.