
マイクロアーキテクチャチャネルは、現代のサイバーセキュリティにおいて重要でありながら、しばしば誤解されがちな要素です。ネットワーキングにおける従来のサイドチャネルとは異なり、マイクロアーキテクチャチャネルは、現代のCPUの低レベルなハードウェア動作を利用して機密情報を漏洩させます。このブログ記事では、これらのチャネルが何か、どのように機能するのか、なぜ危険なのか、そして検出と防止のためにどのような実践的な手段が取れるのかを、実際の事例とコードサンプルを交えて深く掘り下げていきます。
目次
現代のCPUは、速度、効率、並列性を最適化したエンジニアリングの驚異です。しかし、ソフトウェアの抽象の下には、キャッシュ、メモリコントローラ、実行ユニットなどの物理リソースをプロセスが共有する複雑なシステムが存在します。マイクロアーキテクチャチャネルとは、これらの共有リソースを利用してプロセス間で秘密を漏洩させる通信経路であり、基本的なセキュリティ仮定を破壊する可能性があります。
サイドチャネル は、ソフトウェア脆弱性を直接利用するのではなく、システムの動作(電力使用量、タイミング、キャッシュヒット/ミスなど)の違いを観察して秘密を推測する情報漏洩経路です。例えば、攻撃者が特定の操作にかかる時間を観察して暗号鍵を推測することがあります。
カバートチャネル は、システムのセキュリティポリシーを侵害して情報を伝達するための通信経路です。サイドチャネルとは異なり、カバートチャネルは通常意図的に使用され、2つの共謀している者間で秘密裏に情報を交換します。
主な違い:
マイクロアーキテクチャチャネルは、プロセス間でリソースがどのように共有されているかを利用する一方、ネットワーキングチャネルはデータ転送メディアを使用します。
攻撃者が共有CPUリソースを使用してマイクロアーキテクチャのカバート(およびサイド)チャネルを構築する方法を見てみましょう。
// Prime+Probeの簡略化された手順
1. プライム: 特定のキャッシュセットを埋めるためにキャッシュラインをアクセス
2. 被害者に実行させる
3. プローブ: 同じキャッシュラインをアクセスし、アクセス時間を測定
4. キャッシュミスは、被害者のアクセスによる追い出しを示唆する
これらは、CPUが性能のために将来の命令を予期する推測実行を利用します。誤った推測は巻き戻されますが、キャッシュ状態などのマイクロアーキテクチャによる副作用は残り、秘密を漏らします。
分岐予測テーブルの履歴を使用して、被害者コードの制御フロー決定を推測することができます。
2つのプロセスが物理的な実行ユニットを共有する場合、競合が測定可能なタイミング変化を引き起こします。
プロセスA(攻撃者)とプロセスB(被害者)が同じマシン上で実行され、キャッシュリソースを共有しています。
マイクロアーキテクチャのカバートチャネルは理論上のものではなく、現代のコンピューティングインフラストラクチャに対する最も重要な実際の攻撃を可能にしました。
クラウド環境において、ターゲットと同じハードウェアに共存する攻撃者が、隣接するVMから情報を漏洩させるためにキャッシュ攻撃を使用できます。
JavaScriptコードは、時間間隔を測定し、共有キャッシュ状態を使用して、ユーザーのブラウザコンテキストから機密データのビットを流出させることができます。
これらのチャネルの検出は困難ですが、いくつかの実際的なアプローチが存在します。
現代のCPUは、キャッシュミス、分岐ミス予測、実行スタルなどのイベントのためのカウンタを提供しています。スパイクや異常なパターンは攻撃を示唆することがあります。
関連するイベントの例:
cache-referencescache-missesbranch-instructionsbranch-misses高度なオペレーティングシステム(およびハイパーバイザ)は、プロセスのタイミングの異常を監視、ログ、解析して、疑わしい動作を特定できます。
最近の研究では、機械学習を活用して、ハードウェアリソースの善意と悪意の使用を区別し、通常のパターンをモデル化し、異常をフラグ立てしています。
マイクロアーキテクチャカバートチャネルの防止には、ハードウェアとソフトウェアの両方のアプローチが必要です。
利用可能なタイマーの粒度を下げたり、ジッター/ノイズを追加したりすることで、特にJavaScriptベースの攻撃の効果を下げます。
秘密データに依存しない実行時間を持つコードを記述することで、多くのタイミングチャネルを阻止します。
一部のCPUは現在、「サイドチャネル耐性」のある設計を含んでおり、パーティション化されたキャッシュや投機的実行の緩和策を備えています。
オープンソースツールとコードを使用して、実践者がマイクロアーキテクチャの異常をスキャンする方法を見てみましょう。
perfユーティリティを使用してパフォーマンスデータを収集perf list | grep cache
<pid>を実際のPIDに置き換える)sudo perf stat -e cache-references,cache-misses -p <pid>
104,212 cache-references
12,342 cache-misses
sudo perf record -e cache-misses -p <pid> -- sleep 10
sudo perf report
sudo perf stat -a --per-socket -e cache-misses sleep 5 | grep "cache-misses"
プロセスごとには次のようにします:
ps -eo pid,comm | while read pid comm; do
sudo perf stat -p $pid -e cache-misses -I 1000 -- sleep 1 2>&1 | grep cache-misses
done
複数のプロセスを自動的にスキャンしたり、perfの結果を解析して異常を検出したい場合を想定します。
perf stat出力を解析して不審なキャッシュミスを検出import subprocess
import re
def get_cache_misses(pid):
cmd = ["perf", "stat", "-p", str(pid), "-e", "cache-misses", "--", "sleep", "2"]
result = subprocess.run(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
match = re.search(r"([\d,]+)\s+cache-misses", result.stderr)
if match:
count = int(match.group(1).replace(",", ""))
return count
else:
return None
# すべてのプロセスをスキャン
import psutil
for proc in psutil.process_iter(attrs=['pid', 'name']):
pid = proc.info['pid']
name = proc.info['name']
try:
misses = get_cache_misses(pid)
if misses and misses > 100000:
print(f"疑わしい: PID {pid} ({name}) が {misses} キャッシュミス")
except Exception:
continue
#!/bin/bash
THRESHOLD=100000
for pid in $(ps -e -o pid=); do
MISS=$(sudo perf stat -p $pid -e cache-misses -- sleep 1 2>&1 | grep cache-misses | awk '{print $1}' | tr -d ',')
if [ ! -z "$MISS" ] && [ "$MISS" -gt "$THRESHOLD" ]; then
echo "警告: PID $pid は高いキャッシュミス ($MISS)"
fi
done
倫理的および法的な理由から、安全で隔離された許可されたテスト環境でのみ実験してください。
// 警告: デモンストレーションのためだけ。生産システムで使用しないでください。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <x86intrin.h>
#include <unistd.h>
#define CACHE_LINE_SIZE 64
#define PROBE_COUNT 100000
volatile char *array;
void prime_cache() {
for (int i = 0; i < 4096; i += CACHE_LINE_SIZE)
_mm_clflush(&array[i]);
}
int probe_cache() {
int sum = 0;
uint64_t start, end;
for (int i = 0; i < 4096; i += CACHE_LINE_SIZE) {
start = __rdtscp(&i);
volatile char x = array[i];
end = __rdtscp(&i);
sum += (end - start);
}
return sum;
}
int main() {
array = malloc(4096);
for (int i = 0; i < PROBE_COUNT; i++) {
prime_cache();
usleep(1); // 「被害者」に実行させる
int timing = probe_cache();
printf("%d\n", timing);
}
free((void *)array);
return 0;
}
説明:
このコードは、被害者が実行する前後にキャッシュアクセス時間を測定し、他のプロセスがキャッシュを使用したかどうかを推測します。これは多くのマイクロアーキテクチャ攻撃の基本的な考え方です。
マイクロアーキテクチャチャネルは、マルチコアおよびクラウドコンピューティングの時代において、情報セキュリティへの独特でかつますます重要な脅威を表しています。従来のネットワークベースのサイドチャネルとは異なり、これらの攻撃は物理的なハードウェアリソースの複雑な共有を利用しています。この分野は急速に進化しており、すべての実務家は基本的および高度な影響の両方を理解する必要があります。
重要ポイント:
perf)、OS強化、コーディングプラクティスを使用して検出と防御を行う。セキュリティが重要な環境(クラウド、ブラウザ、暗号化アプリ)においては、マイクロアーキテクチャのセキュリティに注意を払うことはもはや選択肢ではありません。
本記事は教育目的です。セキュリティ研究を行う際は、常に倫理基準および地域の規定に従ってください。
このコンテンツが価値あるものだと感じたなら、私たちの包括的な47週間のエリートトレーニングプログラムで何が達成できるか想像してみてください。ユニット8200の技術でキャリアを transformed した1,200人以上の学生に参加しましょう。