Chapter 13
秘密情報の捜索 — 鍵・証明書・資格情報が残っていないか
この章のゴール.
自分の製品のダンプから、平文で残ってはいけない秘密(鍵・証明書・パスワード・トークン)を体系的に探せるようになること。 「見つかった=脆弱性」であることを理解し、何を保護すべきだったかを判断できること。
この章で使う既出の用語(定義は各リンク先). エントロピー(01 章 3 節)、binwalk(02 章 3 節)、SWD(04 章 2 節)、文字列(07 章 6 節)、署名(08 章 4 節)、NVM3(10 章 1 節)、ATE(11 章 3 節)、NVS(11 章 1 節)、バイナリ(12 章 3 節)
1. これは防御のための作業である
この章は「他人の製品から鍵を盗む」ためのものではない。 自分の製品を出荷前に自己監査し、「攻撃者が同じことをしたら何が漏れるか」を先に知るための作業である。 攻撃者がフラッシュを吸い出せる(04 章)以上、そこに平文で秘密があれば、それは漏れる。だから探して、保護する。
PSA 編・IoT セキュリティ編が「どう守るか」を扱うのに対し、本章は「守れているかをバイナリで確かめる」。
2. 探すべき秘密の一覧
| 種類 | 何に使う | 漏れると |
|---|---|---|
| 秘密鍵(ECC / RSA / 対称鍵) | 署名・暗号・認証 | なりすまし、通信の復号、偽ファーム |
| 証明書と秘密鍵の対 | TLS クライアント認証 | 機器のなりすまし |
| Wi-Fi / ネットワーク資格情報 | 接続 | ネットワーク侵入 |
| クラウドのトークン / API キー | サーバ接続 | アカウント乗っ取り、課金 |
| パスワード / PIN | ローカル認証 | 不正操作 |
| 共通のマスター鍵 | 全機器共通の鍵(最悪) | 1 台の解析で全機器が破れる |
| デバッグ用の裏口 | 開発時のバックドア | 認証回避 |
3. 高エントロピー領域を探す — 鍵の在りか
鍵はランダムなバイト列なので、周囲よりエントロピーが高い(14 章)。 コード(.text)は中程度、文字列(.rodata)は低め、鍵や圧縮/暗号データは高い。
binwalk -E dump.bin # エントロピーのグラフ。急に高くなる区間が候補
ent dump.bin # 全体の統計エントロピーが高い区間のうち、16 / 24 / 32 バイト(対称鍵)や 32 バイト(P-256 の秘密鍵)、04 で始まる 65 バイト(非圧縮の公開鍵)にきれいに区切れるものは鍵の候補である。 ただし、圧縮・暗号化されたデータも高エントロピーなので、区別は文脈による(14 章)。
4. 既知の形式で探す — 証明書と鍵
証明書や鍵には決まった形式があり、目印を検索できる。
PEM(テキスト形式)
strings -n 16 dump.bin | grep -E "BEGIN (CERTIFICATE|.*PRIVATE KEY|PUBLIC KEY)"
# -----BEGIN CERTIFICATE-----
# -----BEGIN EC PRIVATE KEY----- ← これが出たら秘密鍵が平文-----BEGIN ... PRIVATE KEY----- が見つかれば、秘密鍵が平文で埋まっている。切り出して openssl で確認する。
# 見つけた範囲を切り出して
openssl ec -in found.pem -text -noout # EC 秘密鍵の内容
openssl x509 -in found.pem -text -noout # 証明書の内容(発行先、有効期限)DER(バイナリ形式)
DER の証明書・鍵は 30 82(SEQUENCE、2 バイト長)で始まることが多い。
# DER 証明書らしき先頭を探す(0x30 0x82 の後に妥当な長さ)
python3 -c "
d=open('dump.bin','rb').read(); i=-1
while (i:=d.find(b'\x30\x82',i+1))>=0:
ln=int.from_bytes(d[i+2:i+4],'big')
if 200<ln<4000: print(hex(i),'len',ln)"
# 候補を切り出して
# openssl x509 -inform DER -in cand.der -text -noout
# openssl asn1parse -inform DER -in cand.der ← 構造を見るopenssl asn1parse は ASN.1(DER の中身の文法)を木構造で表示し、証明書か鍵か、中に何があるかを教えてくれる。
5. パターンで探す — トークンと資格情報
多くの資格情報は、見た目に特徴がある。
strings -n 12 dump.bin | grep -iE "password|passwd|secret|token|api[_-]?key|bearer|ssid|psk"
# JWT(eyJ で始まる Base64)
strings dump.bin | grep -oE "eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}"
# AWS アクセスキー
strings dump.bin | grep -oE "AKIA[0-9A-Z]{16}"
# 一般的な 32/64 桁 hex(鍵やハッシュ)
strings dump.bin | grep -oE "\b[0-9a-fA-F]{32,64}\b"gitleaks / trufflehog のような資格情報スキャナは、多数のパターン(クラウド各社の鍵形式)を内蔵している。バイナリにも --no-git 系のモードで当てられる。
6. 設定ストレージの中を見る
10 章〜11 章のストレージには、しばしば秘密が入る。そして追記型なので、消したはずの古い秘密が残る。
- NVM3 / NVS を 10 章・11 章の手順で全部(履歴も)取り出し、値を上のパターンで検査する
- 「削除マーカが付いた古い Wi-Fi パスワード」が平文で読めたら、それは削除が消去になっていない証拠
- Zigbee / Thread のネットワーク鍵は決まったキー番号に入る
7. 見つけたらどう判断するか
秘密が平文で見つかったとき、それが問題かどうかは保護の前提による。
| 状況 | 判断 |
|---|---|
| フラッシュ読み出し保護(04 章)が有効で、正規手段では読めない | 平文でも一定の防御。ただし保護回避(フォールト注入など)には無力。多層防御が望ましい |
| 読み出し保護が無効 / 未設定で、SWD や SPI 直読みで読めた | 脆弱性。秘密は暗号化するか、セキュアエレメント / TF-M 側に置くべき(PSA 編 12 章・13 章) |
| 全機器共通のマスター鍵が入っている | 重大。1 台の解析で全機器が破れる。機器ごとの鍵に(PSA 編 17 章のパターン F) |
| 公開鍵・証明書だけ(秘密鍵はない) | 問題なし。公開鍵は秘密ではない |
「見つかったこと」より、「読み出せる状態だったこと」が問題である。対策は本サイトの PSA 編・IoT セキュリティ編にある。
8. 監査を仕組みにする
出荷ごとに手で探すのは漏れる。自動化(19 章)する。
# CI に組み込む「秘密が平文で残っていないか」チェックの例
strings -n 12 firmware.bin | grep -qiE "BEGIN .*PRIVATE KEY" && echo "NG: 平文の秘密鍵" && exit 1
binwalk -E firmware.bin > entropy.txt # エントロピーの回帰比較9. 手を動かす
ダンプから秘密を捜索する
この章のポイント
- これは自己監査。攻撃者が読めるなら、平文の秘密は漏れる。先に見つけて保護する
- 鍵は高エントロピー(
binwalk -E)。16/24/32 バイト(対称鍵・P-256 秘密鍵)や 65 バイト(非圧縮公開鍵)に区切れる区間が候補 - 証明書・鍵は形式で検索: PEM の
BEGIN ... PRIVATE KEY、DER の30 82。opensslで確認 - トークンはパターン(JWT の
eyJ、AKIA…、長い hex)。gitleaks などで網羅 - 設定ストレージの履歴にも消し忘れた秘密が残る(10 章・11 章)
- 問題は「見つかったこと」より「読み出せる状態だったこと」。対策は PSA / IoT セキュリティ編