Chapter 10
フォールトインジェクション攻撃 — if 文が飛ぶ
この章がなぜ必要なのか——「保護機能そのもの」が狙われるから.
セキュアブート、読み出し保護、デバッグ無効化、鍵の権限チェック—— これらはすべて、最終的にはプログラム上の分岐に落ちる。
if (verify() != OK) halt();この 1 行を飛ばせたら、全部が無効になる。
そして、それは数万円の機材でできる。
この章で使う既出の用語(定義は各リンク先). 数百万〜数千万円(01 章 2 節)、セキュアブート(02 章 3 節)、不可能(04 章 3 節)、署名検証(04 章 4 節)、完全性(06 章 6 節)、サイドチャネル(07 章 8 節)、フォールト(07 章 8 節)、AES(08 章 8 節)、マスキング(08 章 6 節)、乱数(08 章 5 節)、処理時間(09 章 1 節)、必要(09 章 6 節)
1. 原理
if であるCPU は、電源電圧・クロック・温度が仕様の範囲内にあることを前提に動く。 その前提を一瞬だけ壊すと、次のようなことが起きる。
| 現象 | 内容 |
|---|---|
| 命令スキップ | フェッチした命令が実行されない、または NOP になる |
| データ破損 | レジスタやメモリの読み書きでビットが化ける |
| 分岐の反転 | 条件分岐の判定が逆になる |
| ループカウンタの改変 | ループが早く終わる/終わらない |
| アドレスの化け | ジャンプ先が変わる |
攻撃手法
| 手法 | やること | 費用の目安 | 精度 |
|---|---|---|---|
| 電圧グリッチ | 電源電圧を数十〜数百 ns だけ落とす/上げる | 数万円 | 中 |
| クロックグリッチ | クロックに短いパルスを挿入する | 数万円 | 中〜高(外部クロックが必要) |
| 電磁パルス (EMFI) | 近接コイルで強い磁界パルスを与える | 数十万〜数百万円 | 高(空間分解能あり) |
| レーザー (LFI) | チップを開封し、レーザーを照射する | 数百万〜数千万円 | 極めて高い(1 ビット単位) |
| 温度・電源の極端な操作 | 動作範囲外にする | 数千円 | 低 |
| ボディバイアス注入 | 基板側から電位を注入する | 数百万円 | 高 |
電圧グリッチのハードルの低さが問題である.
ChipWhisperer などのオープンソースツールと数万円のハードウェアで、 誰でも実験できる。手法は公開され、チュートリアルも豊富にある。
つまり 02 章のクラス 2(愛好家)でも実行可能な攻撃になっている。 「専門ラボが必要」という時代ではない。
グリッチによる分岐スキップを見る
グリッチのタイミングと幅を変えて、成功する条件と対策の効果を確かめられる。
2. 何が狙われるか
標的 1: セキュアブートの検証
if (verify_signature(image) != OK) {
halt(); /* ← ここに来なければ通る */
}
jump_to(image);攻撃者は署名検証の直後を狙ってグリッチを打つ。 halt() への分岐が飛べば、不正なファームウェアが起動する。
標的 2: 読み出し保護の判定
/* ブート ROM の中(イメージ) */
if (read_protection_level() > 0) {
disable_debug_port(); /* ← ここを飛ばせたら? */
}飛ばせたら、デバッグポートが開いたまま起動する。 SWD を繋いでフラッシュを吸い出せる。
これは理論上の話ではない.
複数のベンダの MCU で、電圧グリッチによる読み出し保護のバイパスが 公開実証されている。
代表的なものとして、あるベンダの MCU では APPROTECT(アクセスポート保護)が単一のレジスタ判定に依存しており、 グリッチで解除できることが 2020 年に公表された。 同社はその後、保護の判定をハードウェアで冗長化した改良版を出している。
「保護機能がある」だけでは足りない。「フォールト耐性があるか」を確認すること。
標的 3: PIN / パスワードの比較
if (memcmp(input_pin, stored_pin, 4) == 0) {
unlock();
}比較の結果を保持するレジスタをグリッチで書き換えれば、通る。
標的 4: ループカウンタ
for (i = 0; i < 32; i++) {
key[i] = read_key_byte(i);
}ループ回数を減らせば、鍵の一部が初期値(0 など)のままになる。 探索空間が劇的に小さくなる。
標的 5: 暗号演算そのもの(DFA)
差分故障解析 (Differential Fault Analysis) は、 暗号演算の途中に故障を注入し、正常な暗号文と故障暗号文の差分から鍵を求める。
| 暗号 | 必要な故障暗号文 |
|---|---|
| AES-128 | 理論上、1〜2 個(最終ラウンド近傍への 1 バイト故障) |
| RSA-CRT | 1 個(有名な Bellcore 攻撃。CRT の片側だけ壊すと素因数が求まる) |
| ECDSA | 数個 |
RSA-CRT の Bellcore 攻撃は特に有名である.
RSA の署名生成を中国剰余定理(CRT)で高速化する実装で、 \(p\) 側か \(q\) 側の片方だけを故障させると、 正しい署名 \(S\) と誤った署名 \(S'\) から
\[ \gcd(S - S', N) = p \]として素因数が 1 回で求まる。鍵が完全に破られる。
対策は単純: 署名を作ったら、その場で検証してから出力する。 (\(S^e \bmod N\) が元のメッセージに戻るか確認する。) これは多くのライブラリで既に実装されている。
3. 対策 — ソフトウェア側
対策 1: 冗長な検証
/* 悪い: 単一の分岐に全部を賭けている */
if (verify(img) != OK) halt();
jump_to(img);
/* 良い: 二重チェック + 冗長な値 + デフォルト失敗 */
#define OK_MAGIC 0xA5C36E9Bu /* ハミング距離の大きい値 */
#define FAIL_MAGIC 0x5A3C91B6u
volatile uint32_t r1 = FAIL_MAGIC, r2 = FAIL_MAGIC;
r1 = verify(img);
random_delay();
r2 = verify(img); /* 2 回検証する */
if (r1 != OK_MAGIC) halt();
if (r2 != OK_MAGIC) halt();
if (r1 != r2) halt();
if ((r1 ^ r2) != 0) halt(); /* 冗長な確認 */
if (r1 + r2 != 2 * OK_MAGIC) halt(); /* さらに冗長 */
jump_to(img);| 技法 | 効果 |
|---|---|
| デフォルトを失敗にする | 初期化を飛ばされても安全側に落ちる |
| 成功値に特殊なビットパターン | 単一ビット反転では成功値にならない |
| 同じ検証を 2 回行う | 2 回とも同じタイミングで飛ばすのは難しい |
| ランダム遅延を挟む | タイミングを狙いにくくする |
| 冗長な条件を並べる | すべてを飛ばす必要がある |
対策 2: 制御フロー完全性(CFI)
「本来通るはずの経路を通ったか」を検証する。
/* 各関門でカウンタを進める */
volatile uint32_t flow_counter = 0;
void secure_boot(void)
{
flow_counter = 0;
if (check_header(img) != OK) halt();
flow_counter += 0x11;
if (check_key_hash(img) != OK) halt();
flow_counter += 0x22;
if (check_signature(img) != OK) halt();
flow_counter += 0x44;
if (check_svn(img) != OK) halt();
flow_counter += 0x88;
/* ★ すべての関門を通ったことを確認する */
if (flow_counter != (0x11 + 0x22 + 0x44 + 0x88)) halt();
jump_to(img);
}関門を 1 つ飛ばすと、カウンタの値が合わなくなる。
対策 3: 秘密の二重化
/* 鍵を 2 通りの形で保持し、使う前に照合する */
uint8_t key[32];
uint8_t key_complement[32]; /* ビット反転版 */
load_key(key, key_complement);
for (int i = 0; i < 32; i++) {
if ((key[i] ^ key_complement[i]) != 0xFF) halt(); /* 破損を検出 */
}対策 4: 暗号演算の結果検証
/* RSA 署名を作ったら、その場で検証する(Bellcore 対策) */
rsa_sign(msg, sig);
if (rsa_verify(msg, sig) != OK) {
/* 故障が起きた。署名を出力してはいけない */
zeroize(sig);
halt();
}
output(sig);/* AES も、暗号化 → 復号 → 一致確認(コストは 2 倍になる) */
aes_encrypt(pt, ct);
aes_decrypt(ct, check);
if (memcmp_ct(pt, check, 16) != 0) halt();どこまでやるかはコストとのトレードオフである.
全部の暗号演算を二重化すると性能が半分になる。
原則: 「一度きりで、取り返しがつかない処理」を重点的に守る。
- セキュアブートの検証 → 必ず冗長化する
- OTA イメージの検証 → 必ず冗長化する
- ライフサイクル状態の遷移 → 必ず冗長化する
- デバッグポートの閉鎖 → 必ず冗長化する
- 通信の 1 パケットの暗号化 → 冗長化しなくてよい(失敗しても再送される)
4. 対策 — ハードウェア側
シリコンレベルの対策は、ベンダが実装するものである。選定時に確認する。
| 対策 | 内容 |
|---|---|
| 電圧モニタ | 電源電圧が範囲外になったら検出してリセット |
| クロックモニタ | クロック周波数の異常を検出(グリッチ検出) |
| 温度センサ | 動作範囲外の温度を検出 |
| 光センサ | 開封+レーザー照射を検出(パッケージが開いたら光が入る) |
| アクティブシールド | チップ表面に配線を張り、断線したら検出(プロービング対策) |
| メモリの ECC / パリティ | ビット反転を検出・訂正 |
| 冗長な CPU(ロックステップ) | 2 つの CPU が同じ命令を実行し、結果を照合 |
| タンパー検出 | 検出時に鍵を即座に消去する |
| ベンダ・製品 | 特徴 |
|---|---|
| Arm Cortex-M35P | 物理セキュリティ機能(パリティ、アンチタンパリング)を持つ M33 派生 |
| Silicon Labs Secure Vault High | アンチタンパ、DPA/DFA 対策 |
| セキュアエレメント全般 | センサ群 + シールド + 冗長設計(22 章) |
| スマートカード IC | 最も対策が厚い。CC AVA_VAN.5 |
MCU とセキュアエレメントの差が最も出るのが、この分野である.
汎用 MCU は「電圧が下がったらリセットする」程度のブラウンアウト検出しか持たないことが多い。 これは電源異常への対策であって、攻撃への対策ではない。
数十ナノ秒のグリッチは、ブラウンアウト検出の応答より速い。
セキュアエレメントは、グリッチを検出するための専用回路を持ち、 検出したら即座に鍵を消去する。設計思想が違う。
5. 攻撃の実際のフロー
攻撃者の作業を知っておくと、対策の効き所が分かる。
| 段階 | やること | ここを潰す対策 |
|---|---|---|
| 1. 標的の特定 | どの命令を飛ばしたいかを決める(ファームウェア解析) | ファームウェアを読ませない(06 章・11 章) |
| 2. トリガーの確立 | グリッチを打つタイミングの基準を見つける(GPIO、通信、電源波形) | ランダム遅延でタイミングを揺らす |
| 3. パラメータ探索 | 電圧・幅・遅延を総当たりで試す(数千〜数万回) | 試行回数の制限、異常検出でロック |
| 4. 成功の検出 | 「保護が外れた」ことをどう知るか | 失敗時の応答を一様にする |
| 5. 再現性の確保 | 成功率を上げる(数 % でも実用になる) | 冗長チェックで成功確率を下げる |
段階 1 が実は最大の防御点である.
ファームウェアを読めなければ、どこを狙えばよいか分からない。 「署名検証の後の分岐」がどのアドレスにあるかを知るには、 まずファームウェアを入手して解析する必要がある。
だから 11 章(ファームウェア抽出の防止)が、 フォールト攻撃への間接的だが強力な対策になる。
ただし——ブート ROM は多くのベンダで解析されている(同じチップを買えば誰でも読める)。 ROM を狙う攻撃には、この防御は効かない。
6. 「グリッチ耐性」を評価する
SoC を選定するとき、次を確認する。
| 確認項目 | どこを見るか |
|---|---|
| 物理攻撃耐性の第三者評価 | PSA Certified Level 3、SESIP 4 以上、CC AVA_VAN.4 以上 |
| 保護機能の冗長性 | セキュリティアプリケーションノート。「フォールト耐性」の記述 |
| 公知の攻撃事例 | 学会論文、CVE、セキュリティ研究者の公開資料 |
| シリコンリビジョン | 過去に破られた品種は、改版で対策されていることがある |
| ハードウェア検出機構 | 電圧/クロック/温度/光センサの有無 |
公知の攻撃事例を調べることを強く推奨する.
MCU のグリッチ攻撃は、セキュリティカンファレンス (CHES、Black Hat、DEF CON、hardwear.io など)で継続的に発表されている。
「候補チップ名 + glitch」「候補チップ名 + fault injection」で検索して、 公開された攻撃事例があるかを確認するのは、選定時の重要な作業である。
事例があること自体は必ずしも失格ではない(対策版が出ていることも多い)。 重要なのは、ベンダが対応したかどうかである。
7. サイドチャネルとの関係
| サイドチャネル(09 章) | フォールト(本章) | |
|---|---|---|
| 性格 | 受動的(観測するだけ) | 能動的(介入する) |
| 検出 | ほぼ不可能 | 検出できる(センサで) |
| 標的 | 主に暗号演算 | 保護の判定ロジック全般 |
| 必要な試行 | 数百〜数百万回の正常な実行 | 数千〜数万回の故障を起こす試行 |
| 主な対策 | マスキング、定時間実装 | 冗長化、CFI、センサ |
組み合わせると強力になる.
DPA 対策のマスキングを、フォールトで無効化するという複合攻撃がある。 マスク生成の乱数を 0 に固定できれば、マスキングは効かなくなる。
だから高セキュリティ実装では、 「乱数が本当にランダムか」を検証してから使うといった対策も入れる。
対策どうしが干渉することもある。 たとえば「エラー時に即座に停止する」フォールト対策が、 「エラーの有無で処理時間が変わる」タイミング攻撃の窓口になる。 両方を考慮した設計が必要で、これが専門性を要する理由である。
8. この章のまとめ
| ポイント | 内容 |
|---|---|
| 原理 | 電圧・クロック・温度を一瞬崩す → 命令スキップ、データ化け、分岐反転 |
| ハードル | 電圧グリッチは数万円。クラス 2(愛好家)でも実行可能 |
| 主な標的 | セキュアブートの検証、読み出し保護の判定、PIN 比較、ループ、暗号演算 |
| 実例 | 複数ベンダの MCU で読み出し保護のグリッチ突破が公開実証されている |
| DFA | AES は1〜2 個の故障暗号文で、RSA-CRT は1 個で破れる |
| RSA-CRT の対策 | 署名を作ったら、その場で検証してから出力する |
| ソフト対策 | デフォルト失敗・二重検証・ハミング距離の大きい成功値・CFI カウンタ |
| どこを守るか | 一度きりで取り返しがつかない処理(ブート検証、LCS 遷移、デバッグ閉鎖) |
| ハード対策 | 電圧/クロック/温度/光センサ、アクティブシールド、ロックステップ |
| 最大の防御点 | ファームウェアを読ませない。狙う場所が分からなければ攻撃できない |
| 選定時 | PSA L3 / SESIP 4 / AVA_VAN.4 以上と、公知の攻撃事例を確認する |
次章では、その「ファームウェアを読ませない」を正面から扱う。