はじめに
システムプログラミング――あなたがこれから足を踏み入れようとしている世界――において、私たちは現状に甘んじるようになってしまいました。 もしかすると、加担してさえいるのかもしれません。
私たちは顧客に対して信頼性が高く安全なソフトウェアを約束してきましたが、そこには暗黙の但し書きがありました。性能を犠牲にしてはならない、というものです。 そして、最速の言語は伝統的にメモリ安全ではありませんでした。 それらを使用することで、私たちは一般的な機能要件と、最も基本的な防御姿勢の両方を積極的に危険にさらしています。
-
信頼性: メモリ安全性エラーは、予測不能なクラッシュ(例: 「セグメンテーションフォルト」)、再現不能なエラー(例: 「データ競合」)、そして最終的なサービス停止(例: 「メモリリーク」)を引き起こします。
-
セキュリティ: 同じメモリエラーによって、攻撃者が機密データを読み取れるようになったり(例: 「秘密情報を盗む」)、被害を受けたプログラムの権限でほぼ任意のコマンドを実行できるようになったりする可能性があります(例: 「完全制御」)。
プログラムがクラッシュしたり、不正な出力を生成したりすることは一つの問題です。 あなたのマシンがボットネットに参加することは、また別の問題です1。 メモリ安全でないプログラムは、これら両方の結果を同時にもたらす危険があります。
こうした途方もないリスクは、ガベージコレクションや重量級ランタイムを使用する、ほとんどの現代的な「アプリケーション言語」(Python、Java、Go などを想像してください)では、大部分が軽減されています2。 しかし、安全性が重要なシステム、高性能な分散インフラストラクチャ、さまざまな種類のファームウェアなど、C と C++ に依存し続けている重要な分野があります3。
時代とともに自らを再発明し、世界で最も重要なソフトウェアの多くを支えてきたにもかかわらず、これら 2 つの伝統的な「システム言語」は、依然として最悪級のセキュリティ脆弱性(例: Heartbleed4、Stagefright5、EternalBlue6、Dirty Pipe7 など)の主要な供給源であり続けています。 メモリ破損の脅威は衰えていません。
これが、数十年にわたって後方互換性にコミットしてきた代償です。 私たちは、自分たちがその肩の上に立つ巨人たちをけなそうとしているわけではありません。
しかし、その肩はひどく重い荷を背負っています。 2012 年の重要な研究論文8は、「Systematization of Knowledge: Eternal War in Memory」という適切な題名のもと、30 年にわたる C と C++ のメモリ保護スキームの失敗を記録しました。 新しい防御策はことごとく、新しい回避技術によって打ち破られてきました。
時を進めて 2019 年。 Microsoft の調査9は、同社製品における 2004 年から 2018 年までのすべてのセキュリティ問題について、次のように述べています。
毎年セキュリティ更新プログラムによって対処される脆弱性の約 70% は、依然としてメモリ安全性の問題である。
この割合には、市場をリードする巨大企業である Microsoft におけるエンジニアの経験やソフトウェア品質プロセスが反映されています。 もしあなたがすでに C または C++ プログラマーであるなら、これは受け入れがたい真実です。 それは理解できます。
-
C は革命でした。プロセッサ間で移植可能な「高水準」プログラムを世界にもたらしました10。そして今日、それはほぼすべてのデバイスのかなり深い層で動いています。
-
C++ の強力な抽象化は、Web ブラウザー、グラフィックスエンジン、データベースなど、驚くべき規模の高性能なシステムを可能にしています。私たちがなくてはならないソフトウェアです。
しかし、進化は遅れすぎているのかもしれません。 Google による 2022 年の分析11では、「実際に悪用されているものとして検出および開示された」これまで未知だった(例: 「ゼロデイ」)エクスプロイトについて、次のことがわかりました。
その年[2021 年]に実際に悪用された 58 件の 0-day のうち、39 件、つまり 67% はメモリ破損脆弱性だった。 メモリ破損脆弱性は、過去数十年にわたりソフトウェアを攻撃する標準的な手段であり続けており、攻撃者が今なお成功を収めている方法でもある。
調査対象となったエクスプロイトの一部は、ジャーナリスト、政治家、活動家、マイノリティ集団を標的にするために実際に使用されていました11。 メモリ破損は理論上の問題ではありません。 それは差し迫った深刻な問題であり、最悪の場合には具体的な人的被害を伴います。
もし私が、常に本当に慎重で、いつでも気を抜かないとしたらどうでしょうか?
現代的な C 系言語のベストプラクティスは、メモリ脆弱性の可能性を低減できます。 しかし、それらを排除できるとは思えません。 そして Rust のコンパイル時エラーやランタイムチェックとは異なり、ベストプラクティスをスケールさせることは困難です。
数十年分のデータは、開発者の勤勉さと広範なテストではメモリ安全性の問題を確実に解決できないことを示しています。 根本原因への対処を真剣に検討すべき時です。
もう現状に甘んじる必要はありません
Rust は比較的新しいシステム言語として、伝統的に C/C++ の領域であったものをサポートします。 それは、コンパイラによって強制される「所有権」12という、別のパラダイム群を提供します。
もし Rust を一度も試したことがないなら、ほぼ全知だが狭い範囲に集中する完璧主義者とペアプログラミングしているところを想像してみてください。 所有権の概念を実装するコンパイラコンポーネントである借用チェッカーは、ときにそのように感じられます。
それに伴う学習曲線と引き換えに、私たちはメモリ安全性の保証を得ます。 つまり、多くの場合、常にではないにせよ、すべてのメモリエラーが存在しないことを確実に証明できるということです。
Rust は 2010 年に Mozilla によって発表され13、2021 年時点では独立した非営利団体によって主導されており14、過去 40 年のシステムプログラミングにおける最も差し迫った単一のセキュリティ問題、すなわちメモリ安全性を大部分解決します。
商業的に実用可能なプログラミング言語として、メモリ安全性とベアメタルの速度を同時に提供する、おそらく初めての言語です。
それは単なる意見でしょうか?
それは大きな勢いを得つつある意見です。 社会的受容と市場圧力のあるしきい値を超えれば、退屈なほど当たり前の立場にさえなるかもしれません。 Rust を本番環境で使用しているユーザーの一部から、同様の見解を紹介します:15
Amazon:16
…AWS では、Firecracker VMM のような重要インフラストラクチャを Rust で構築することがますます増えています。なぜなら、Rust の標準機能により、Amazon の高いセキュリティ基準に到達するために必要な時間と労力を削減できる一方で、C や C++ と同様のランタイム性能を引き続き提供できるからです。
Google:17
私たちは、Rust が[Linux]カーネルを実装するための実用的な言語として C に加わる準備が整ったと感じています。Rust は、コアカーネルとうまく共存し、その性能特性を維持しながら、特権コードにおける潜在的なバグやセキュリティ脆弱性の数を減らす助けになります。
Microsoft:18
[Rust は]この[メモリ安全性]問題に正面から取り組むための、業界にとって最良の機会です。 さらに、Rust は米国政府のセキュリティガイダンスの最前線にも登場し始めています。 このセクションが最初に公開された時点(2022年3月)以降、さまざまな政府機関が、メモリ安全性という深刻な社会的問題に対する解決策として Rust を明示的に挙げるようになりました。
国家安全保障局(NSA):19
ソフトウェア解析ツールはメモリ管理の問題の多くの事例を検出でき、動作環境のオプションも一定の保護を提供できますが、メモリ安全なソフトウェア言語が提供する本質的な保護は、ほとんどのメモリ管理問題を防止または軽減できます。NSA は、可能な場合はメモリ安全な言語を使用することを推奨します。
…メモリ安全な言語の例には、C#、Go、Java、Ruby、Rust、Swift があります。
サイバーセキュリティ・インフラストラクチャセキュリティ庁(CISA):20
ほかのどの産業であれば、市場が、製品のユーザーにとってこれほどよく理解されており深刻な危険を何十年も容認するでしょうか?
…数年前まで欠けていたのは、C/C++ の速度と組み込みのメモリ安全性保証を備えた言語です。2006年、Mozilla のソフトウェアエンジニアが Rust という新しいプログラミング言語の開発を始めました。Rust バージョン 1.0 は 2015年に正式に発表されました。
米国国立標準技術研究所(NIST):21
安全性や品質をプログラムに「テストで後付け」することはできません。最初から設計に組み込まれていなければなりません。より安全またはよりセキュアな言語や言語サブセットで実装することを選択すれば、弱点のクラス全体を完全に回避できます。
…Rust には所有権モデルがあり、ガベージコレクターを必要とせずに、コンパイル時にメモリ安全性とスレッド安全性の両方を保証します。これにより、ユーザーは多くのバグクラスを排除しながら、高性能なコードを書くことができます。Rust には unsafe モードがありますが、その使用は明示的であり、許可されるアクションの範囲も狭く限定されています。
それは Rust プログラムが侵害されないという意味ですか?
まったく違います。 メモリ破損は、バグクラスの 1 つにすぎません。 それは特に厄介で、高価値のエクスプロイトチェーンの一部であることが多いものですが22、ほかのバグクラスも存在します。
多くの、もしそうでなくてもほとんどのセキュリティ問題は、言語に依存しません(例: 設定ミス23、コマンドインジェクション24、ハードコードされたシークレット25 など)。 また、メモリ安全な言語が独自の問題を持ち込むこともまれにあります(例: 信頼できない入力のインタープリター評価、別名「eval インジェクション」)。 どのプログラミング言語も、あらゆる攻撃に対してあなたのコードを完全にセキュアにしてくれるわけではありません。
さらに、Rust にはあまり知られていない厄介な秘密があります。
先に触れておきましょう。Rust の unsafe キーワードを使う必要がある場合があります。これは、特定のコードブロック内で潜在的にメモリ安全でない振る舞いを許可します。
その意図は、コンパイラーが自動的に検証できないアクションの安全性を、人間が慎重にレビューすることです。
そして、それはコードベース全体ではなく、いくつかの重要な箇所に限られます。
この本のコアプロジェクトでは、unsafe をまったく使う必要はありません!
Rust の保証を最大限に活用するために、問題を慣用的な方法で捉え直す方法を学びます。
これは、この言語から具体的なセキュリティ上の価値を引き出すための重要なスキルです。
しかし、状況認識と将来の取り組みのために、以下を扱います。
unsafeの使用法と影響を詳しく取り上げます。- Rust アプリケーションの安全性を監査するためのツールを学びます。
- ケーススタディとして、Rust ソフトウェアにおける実世界の脆弱性をいくつかレビューします。
- 安全でない言語から Rust コードを呼び出すための Foreign Function Interface(FFI)バインディングを構築します。
Rust はシステムセキュリティにおける途方もない飛躍ですが、万能薬ではありません。
Rust は C や C++ を置き換えることになりますか?
C と C++ は、現代世界で最も広く使われているソフトウェアの一部を支えています。 膨大な数の C ファミリーのコードベースが何十年も前から存在しており、今後さらに何十年も進み続けるでしょう。 重要な問題に対して、長年の実績に裏付けられた解決策を提供しているからです。
プロのシステムプログラマーは、3 つすべての言語の経験から恩恵を受けられます。 C ファミリーがすぐになくなることはありません。 そして Rust は既存の C/C++ コードと統合できます。 ランタイムオーバーヘッドなしでです。
これは Rust の本ですが、C の小さなスニペットをいくつか目にすることになります。 通常それらは、すべての C プログラマーが認識しておくべき微妙な「未定義動作」の問題を示すものです。
また、Foreign Function Interface(CFFI)を介して、あなたが構築するライブラリを C から呼び出すためのバインディングも書きます。 これは、別の言語で書かれた既存のコードベースに新しい Rust コンポーネントを統合するための前提条件です。
curlの興味深い事例
curl26 は、URL を使ってデータを転送するためのユーティリティで、1998年以降広く普及しており、C で書かれています。 このツールは非常に広く依存されているため、そのセキュリティは私たちが「インターネット」と見なすものの多くに影響します27。2020年時点で、
curlはセキュリティを強化するために、HTTP と TLS の Rust バックエンド(CFFI 経由で呼び出される)を統合しています27。 メモリ安全な Rust コードは、既存の C コードとシームレスに統合されます。 すべてが 1 つのコンパイル済みバイナリであり、言語間相互運用性によるパフォーマンスペナルティはありません。平均的なエンドユーザーには違いが分からないとしても(そしてそれは良いことです!)、
curlは今や、より安全で信頼性の高いプログラムになっています28。
新しい言語を採用することは本当に労力に見合いますか?
あなたはおそらく、自分が選んだ言語/ツールチェーン/エコシステムに相当な経験時間を投資してきたでしょう。 知識の多くが移転可能だとしても、Rust はその労力に見合うのでしょうか?
すべてのプロジェクトに当てはまるわけではありません。 Rust は、性能、信頼性、セキュリティのすべてがミッションクリティカルな要件である場合に魅力的な選択肢です。 このように相反しがちな基準が交わるところでは、正当化可能な確信に到達するための障壁は、従来ばかばかしいほど高いものでした。 ここで言っているのは「形式手法」です。機械支援による証明、モデル検査、シンボリック実行などです。 これらのアプローチは価値がある一方で、産業界での大規模な採用には障害があります。
現在、Rust の保証は、これらの検証アプローチが総体として提供するものの小さなサブセットです。 しかし Rust は、それに伴う実用性上の落とし穴の多くも回避しています。
Rust のコンパイラーは、重要な性質の特定の集合をほぼ自動的に証明し29、私たちが迅速に出荷できるようにします。 表面的には、低レイテンシ性能を犠牲にすることなくメモリ安全性を得られます。 さらに深く見ると、私たちの優位性は、実際には商用開発のペースで行える原理に基づいた検証にあります。 実世界のコードの速度で、[一部の] 証明を得られるのです。
検証はおもちゃのプログラムだけのものではないのですか?
形式的な裏付けのある保証のほとんどは研究用プロトタイプに限定されています。大規模でマルチスレッドのコードベースには単にスケールしません。 そのため、形式手法は実務のソフトウェアエンジニアの間で評判がよくありません。難しすぎるうえに、価値が十分ではないのです。
対照的に、Rust コンパイラーはもともと、Firefox のブラウザーエンジンのコンポーネントを堅牢化するために設計されました13。これは数百万行規模で高度に並列な商用コードベースです。
さて、どれほど有益になり得るツールであっても、採用と影響を得るには使いやすくなければなりません。 当初の学習曲線はあるものの、Rustは過去7年連続で「最も愛されている」プログラミング言語に選ばれました。 StackOverflowの年次開発者調査30では、2022年に73,000件を超える回答が寄せられました31。
わかりました。では、Rustを学べますか?
あなたが別の国へ移住し、新しい言語を話せるようになることを選んだと想像してみてください。 簡単ではないでしょうが、それだけの価値があるかもしれません。 Rustにおける高保証プログラミングへの私たちの第一歩も同様に、困難ではあるものの、やりがいのあるものになるでしょう。
新しいことを学ぶことの素晴らしさは、誰にでもできるという点にあります。 少しの時間と適切なリソースが必要ですが、十分に長く続ければ、物事が腑に落ち始めます。 本書は、Rustの高い学習曲線をすばやく手なずける手助けをします。そうすることで、私たち全員が共同で信頼できるシステムソフトウェアを構築できるようになります。
学習成果
- 本書が何を扱い、なぜ扱うのかを理解する
- 本書が技能習得のドレイファスモデルのどこに位置づけられるかを理解する
- Rustを書き始められるように開発環境をセットアップする
-
You Can’t Spell Trust Without Rust。Aria Desires(2015)。Rust標準ライブラリのBTree作者の1人によるこの修士論文は、このボットネットの類推の出典であり、本書全体への影響源でもあります。 ↩
-
ここで「大部分は」としているのは注意書きです。Pythonにもデータ競合はあり得ますし、Goにもセグメンテーションフォールトはあり得ます、などです。しかし、ガベージコレクションを備えた言語は、CやC++と同じ強力な悪用プリミティブを攻撃者に与えるわけではありません(詳細は第4章で述べます)。そのため、同じリスクを伴うわけではありません。 ↩
-
CとC++は、それぞれのかつての姿との共通点と同じくらい、互いの共通点も少ない、と主張するのは正しいでしょう。C++1132はC++2033とはほとんど似ていません。ざっと読む代わりに言うと、公式のC++言語標準は、合計で1,308ページ32から1,823ページ33へと、515ページ、ほぼ40%増加しました。 ↩
-
Embedded System Security with Rust: Case Study of Heartbleed。Jens Getreu(2016)。 ↩
-
Stagefright: Scary Code in the Heart of Android。Joshua Drake(2015) ↩
-
EternalBlue Exploit: What It Is And How It Works。SentinelOne(2019)。 ↩
-
The Dirty Pipe Vulnerability。Max Kellerman(2022)。 ↩
-
SoK: Eternal War in Memory。Laszlo Szekeres、Mathias Payer、Tao Wei、Dawn Song(2012)。 ↩
-
Trends, challenges, and strategic shifts in the software vulnerability mitigation landscape。Mat Miller(2019) ↩
-
Episode 53 - C Level, Part I。Sean Haas(2021)。このポッドキャストで説明されているように、Cはアセンブリへコンパイルされる最初の言語ではありませんでした。広く採用されるようになった、そのアイデアの実用的な変種です。 ↩
-
The More You Know, The More You Know You Don’t Know。Maddie Stone、Google Project Zero(2022)。 ↩ ↩2
-
所有権はまったく新しいものではなく、類似の概念は研究用言語によって先駆的に取り組まれていました。密接に関連する概念であるライフタイムは、C++コミュニティに以前から存在していました34。しかし、Rustの新しい所有権システムは、コンパイル時にライフタイム規則を強制します。C++では、注意しないとライフタイムに関する仮定が実行時に破られる可能性があります。その結果、バグや脆弱性が生じる可能性があります。Rustは、特定のC++のベストプラクティスをコンパイラそのものの中で結晶化したものだと主張する人もいます。 ↩
-
Project Servo, Technology from the past come to save the future from itself。Graydon Hoare(2010)。 ↩ ↩2
-
Hello World!。Ashley Williams(2021)。 ↩
-
Production Users。The Rust Team(アクセス日: 2022年)。 ↩
-
Why AWS loves Rust, and how we’d like to help。Matt Asay、Official AWS Open Source Blog(2020)。 ↩
-
Rust in the Linux kernel。Wedson Almeida Filho、Official Google Security Blog(2021)。 ↩
-
Microsoft: Rust Is the Industry’s ‘Best Chance’ at Safe Systems Programming。Joab Jackson(2020)。 ↩
-
Software Memory Safety。NSA(2022)。 ↩
-
The Urgent Need for Memory Safety in Software Products。Bob Lord、CISA(2023)。 ↩
-
Safer Languages。NIST(2023)。 ↩
-
Zoom RCE from Pwn2Own 2021。Thijs Alkemade、Daan Keupe(2021)。 ↩
-
A05:2021 – Security Misconfiguration。OWASP(2021)。 ↩
-
Apache Log4j Vulnerability Guidance。CISA(2021)。 ↩
-
CVE-2022-1162。National Vulnerability Database(2022)。 ↩
-
Memory Safe ‘curl’ for a More Secure Internet。Internet Security Research Group(2020)。 ↩ ↩2
-
本稿執筆時点では、Rust対応ビルドの
curlはデフォルト構成ではありません。curlをビルドして配布する人々(たとえばOSディストリビューションのメンテナー)が、サポートするプラットフォームやユーザーに適したビルド構成を選ぶことになります。注目に値する妥当な取り決めです! ↩ -
Computer Scientist proves safety claims of the programming language Rust。Saarland University(2021)。Rustの形式検証は、現在の成果と継続中の作業の両方を含む研究課題であることに注意してください。 ↩
-
Technology: Most loved, dreaded, and wanted。StackOverflow(2022)。 ↩
-
Methodology: Participants。StackOverflow(2022)。 ↩
-
[最終] ワーキングドラフト、プログラミング言語 C++ 標準。文書番号:N3337 (2012)。文書番号 3337 - 1337 にとても近い!惜しい機会を逃しました。 ↩ ↩2
-
[最終] ワーキングドラフト、プログラミング言語 C++ 標準。文書番号:N4861 (2020)。 ↩ ↩2