KNOWLEDGE - COLUMN ナレッジ - コラム

高度なプライバシーを実現するTEE (Trusted Execution Environment)の研究

col202_main

関連するソリューション

サイバーセキュリティ

ID-Ashura/セキュリティサービス

慶應義塾大学理工学部の河野と申します。共同研究をさせていただいているご縁で、コラムを書かせていただくことになりました。どのような話題で書こうか、かなり迷った挙句、私の研究室でやっているあまり肩の凝らない話をさせていただこうと思います。

TEE ってなんだろう

最近、よく話題にあがる CPU の機能に TEE (Trusted Execution Environment) というものがあります。Intel、 AMD、 ARM などの CPU はいずれも TEE をサポートしています。RISC-V でも KeyStone という TEE が提案されています。

TEE とは高度なプライバシーを実現するためのベースとして利用する機能です。TEEからアクセスするコードやデータは暗号化されてメモリに保持されており、実際にアクセスする段階で CPU 内部で復号化されるというものです。
 
CPU の内部で暗号化・復号化を行うことで、オペレーティングシステムやハイパーバイザなどの特権ソフトウェアに悪意があっても、データのプライバシーを守れることが強みです。
河野教授_426x756

 データを守るって?

さて、少し専門的な話をします。暗号が解読されない限り、暗号化されたデータを覗き見ることはできません。これを秘匿性 (confidentiality) といいます。
 
データを守りたいとき、データの秘匿性だけを保証してあればいいわけではありません。もう一つ、完全性(integrity)という性質を保証してあげなければいけません。平たくいえば、完全性が保証されているというのは、データが改竄されていないということです。
 
データを暗号化しただけでは完全性は保証されませんので注意が必要です。

完全性に対する攻撃

データの完全性を脅かすには、いくつかの基本的な攻撃の仕方があります。実際のコンピュータシステムに対してどのように攻撃するのか、という話ではなく、原理的にどんなことができるのかという話です。

弱い完全性の保証

最初に思いつく攻撃は、メモリの内容をランダムに書き換えてしまうというものです。このような攻撃を防ぐには、たとえばメモリ・ブロックに対してハッシュ値を計算しておいて、復号化したあとにハッシュ値を再計算し、ハッシュ値が変わっていないことを確認すれば十分です。よほどのことがない限り、データ改竄後のハッシュ値が改竄前のハッシュ値と一致することはありません。
 
このような攻撃だけを防げるようにしたものを、このコラムの中では「弱い完全性」と呼んでおきましょう。

強い完全性の保証

実は、ハッシュ値を用いた防御策だけでは不十分です。では、どのような攻撃をすればハッシュ値を用いた防御策を突破できるのでしょうか。そのような攻撃にリプレイ(replay)攻撃というものがあります。
 
リプレイ攻撃では、メモリの内容をランダムに書き換えるのではなく、1) 以前のメモリ・ブロックの値を取っておき、2) 後からその値を入れ込むという攻撃です。以前に書き込まれた値を再利用するため、ハッシュ値のチェックだけでは改竄を検出できません。
 
リプレイ攻撃を防ぐにどうすればいいのでしょうか? Intel SGX で用いられている手法は、Merkle-木 というものを使った手法です。専門的になりすぎるので、ここでは説明を省きますが、興味のある方は "Merkle tree" で検索してみるといいでしょう。たくさんの解説を見つけることができます。
 
リプレイ攻撃まで防げるようにしたものを、このコラムの中では「強い完全性」と呼んでおきましょう。

TEE の比較

さて、各社が提供している TEE ですが、その機能にはいろいろと違いがあります。
商業的に利用できるできる TEE の中では、Intel SGX が一番強力です。ご存知の通り、Intel SGX はサイド・チャネル攻撃に脆弱であったり、さまざまな不具合が指摘されています。とはいえ、同等な機能をもった研究ベースの TEE も発表されており、いずれはリニューアルして登場してくるでしょう。

なぜ強い完全性を保証しないのか?

TEE を使って保護したくなるようなデータはとても大切なデータであるはずです。たとえば、医療データであれば、秘匿性はもちろんのこと、そのデータが改竄されていないということも大切です。改竄されたデータを元に診察されたのでは、たまったものではありません。
 
だとしたら、どうして商用システムの多くが「強い」完全性を保証しないのでしょうか。実は、強い完全性を保証するのは技術的にとても難しいのです。先ほど、少し触れた Merkle-木 の扱いが簡単ではありません。強い完全性が保証できるデータの総量を増やそうとすると、この Merkle-木の大きさが大きくなります。
 
Merkle-木の大きさが大きくなると、 Merkle-木を保存しておくために必要なメモリが大きくなります。詳細は省きますが、かなりの容量になります。それだけではありません。Merkle-木は大きいため、CPU の内部に保存しておけるような代物ではありません。CPU 内部には木のルートだけを保存しておくことになります。そして、メモリ・アクセスのたびにツリーをたどってデータが改竄されていないことを確認しなければなりません。メモリ・アクセスがとても遅くなります。
 
ここまでしないといけないのなら、「強い」完全性はあきらめて、「弱い」完全性だけ保証しようというものがあっても存在価値が出てきます。実際、クラウド環境で使うことを考えると、リプレイ攻撃まで脅威として考えるかどうか、難しいところがあります。そのため、TEE の多くはリプレイ攻撃の防御までは対象としていないようです。

オンラインゲームにおけるチート防止

では、リプレイ攻撃までを考えた TEE には使い道があまりないのでしょうか? 実はそんなことはないだろう、というのが私の研究者としての見解です。「強い完全性」を保証する TEE の使い道として、オンラインゲームのチート行為防止に使えるのではないかということを考えています。

ゲームにおけるチート行為

オンラインゲームにおけるチート行為とは、ズルをすることです。ゲーム・アプリケーションの内部データをいじってしまえば、いろいろなズルができます。シューティング・ゲームであれば、弾丸の量を増やしたり、受けたダメージを軽減したりといったことが簡単にできます。ゲームといえども、ただのソフトウェアですから、そうした値はメモリ上にデータとして保存されています。そうしたデータを何らかの方法で改竄してしまえば、やりたい放題です。
 
世の中には、このようなチート行為をサポートするためのツールが多く出回っています。そうしたツールを使えば、比較的簡単にチート行為を行うことができます。実際、YouTube などで調べていると、チート行為を行なっているゲームの動画がたくさんあります。具体的なゲーム名などは伏せておきますが、ユーザの多い著名なゲームでのチート行為がたくさんアップロードされているのは驚きです。

オンライン・ゲームの仕組み

現在のオンライン・ゲームでは 1 秒間に 60 回程度、画面の書き換えを行います。プレイヤからの入力を受け取り、次の状態を計算し、それから画面を描画する、というサイクルを 1 秒間に 60 回ほど実行します。そのため、次の状態の計算はクライアント側(つまり、プレイヤ側のマシン)で行う必要があります。コントローラからの入力をいちいちサーバに送信していたのでは、次の描画までに間に合いません。結果として、ゲームの状態はクライアント側で保持することになります。
 
 [オンライン・ゲームのアーキテクチャその1 ] 
omamori-ccs-StatelessClient.drawio 

上の図は、オンライン・ゲームのアーキテクチャを概念的に示したものです。このアーキテクチャを採用すれば、クライアント側で状態を持たないため、チート行為は難しくなります。しかし、描画のために毎回、サーバと通信する必要が出てきてしまいます。

 
 [オンライン・ゲームのアーキテクチャその2]
images/omamori-ccs-OffladingWithValidation.drawio 

そこで、実際にはこちらのアーキテクチャのように、クライアント側で状態を保持することになります。クライアント側で保持している状態を改竄することでチートを行うわけです。

TEE によるチート防止

そこで、ゲームの状態を TEE を使って守ってやったらどうか、という話になります。チート行為の場合、クライアント側のマシンは完全に攻撃者(チート行為を行うプレイヤ)が所有しているため、リプレイ攻撃も原理的に可能になります。リプレイ攻撃ができるといろいろなことができます。シューティング・ゲームであれば、弾丸を打った後に、メモリの内容をリプレイし、弾丸の量を元に戻すことができます。
 
では、ゲーム状態を丸ごと TEE に入れてしまえばいいか、となるとそうもいきません。二つほど問題があります。
 
・TEE 内部からのメモリ参照は遅い。

すでに説明した通り、TEE の内部からのメモリ参照は、通常のメモリ参照より数倍程度遅くなります。そのため、TEE 内部からのメモリ参照の頻度はなるべく少なくする必要があります。
 
・TEE のメモリ容量が限られている

TEE が素直に使えるメモリ容量には制限があります。この制限を超えると、メモリ参照が爆発的に遅くなります。われわれの実験では 30 倍以上、遅くなるケースも確認しています。そのため、TEE に入れるゲーム状態をこの制限以下に抑える必要があります。
 
そこで、われわれがとった手法というのは、次の二つになります。
 
・ゲーム状態の変更を行わないコードは TEE の外に置く。

まあ、これは当たり前といえば当たり前です。守りたいデータと関係のないコードを TEE の中で実行する必要はありません。でも、このようにすることで TEE の内部で動作するコード量が減り、結果としてメモリ参照の頻度が減ります。
 
・改竄されるとまずい状態も TEE の外に置く。

それでも改竄できないようにする。TEE の外においたデータは TEE の保護対象から外れてしまいますので、改竄から守ることはできないはずです。そこでひと工夫します。改竄されてはまずい状態については、そのハッシュ値を計算しておき、そのハッシュ値だけ TEE で守るようにしておきます。こうすることで、データの改竄を検知できるようになります。
 
[提案方式]
images/omamori-ccs-approach-omamori.drawio.png 

このように書くと簡単そうに見えますが、実際にはいろいろな技術を組み合わせて実現しています。結果的には、この仕組みでうまくいきそうだという感触を得ています。

 
[適用例]
images/omamori-ccs-implementation-omamori-2.drawio.png



当サイトの内容、テキスト、画像等の転載・転記・使用する場合は問い合わせよりご連絡下さい。

エバンジェリストによるコラムやIDグループからのお知らせなどを
メルマガでお届けしています。

メルマガ登録ボタン

関連するソリューション

サイバーセキュリティ

ID-Ashura/セキュリティサービス

関連するナレッジ・コラム

Society 5.0を支える認証基盤-トラストサービス

ドメイン管理のリスクと対策について

フィッシング攻撃への対策 ~パスワードが諸悪の根源!?~