オブジェクトの広場はオージス総研グループのエンジニアによる技術発表サイトです

セキュリティ/組み込み

知りたい組み込みセキュリティ設計

オージス総研 組み込みソリューション部
野田 智史
2021年4月22日

「鎖は最も弱い環から切れる」皆さんはこの言葉を聞いたことがあるでしょうか?この言葉はもともと英語のことわざ「A chain is no stronger than its weakest link」鎖の強さは、もっとも弱い環によって決まる。"たとえ一つでも弱い環があれば、そこからちぎれることになるから、鎖の強さはその弱い環によって決まるわけ。なにごとでも効力は弱い個所によって決定されるという比喩的な意味で引用されます。有名なイギリスの探偵小説作家コナン・ドイルのことばから出たものです"(英語のことわざ、秋本弘介、創元社)。セキュリティでも同じことが言えます。どれだけ高レベルのセキュリティ対策を実施していても、一か所でも弱い部分があれば、攻撃が成功する可能性が高くなります。本記事では、組み込み製品開発者向けに、セキュリティの弱い部分を作らないための注意点とその勘所をご紹介します。

はじめに

誰もが脆弱性があり安全ではない製品を世間に出したくはありません。そのためにセキュリティを考慮した設計を実施する必要があります。 本記事は、よく質問される3つの項目を「知りたい組み込みセキュリティ設計」として紹介していきます。セキュリティ設計をやることになったけど「鍵管理はどうすればいいのか?」「脆弱性がある場合はどうすればいいのか?」「対策はこれでいいのか?」などの悩みをもつ設計者の方の参考になれば幸いです。

目次:知りたい組み込みセキュリティ設計

  • 暗号化はした。鍵はどこに置く?
    • 暗号鍵のハードコーディングは危険
    • 鍵は守るには
    • ハードウェアによる保護機構を持ったストレージに保管する
    • リバースエンジニアリングを防ぐ
  • 更新の仕組みをどうするか?
    • ロールバックの是非
    • パッチ配布のポリシー
  • 開発者機能を残してもいい?
    • 開発者機能の無効化

暗号化はした。鍵はどこに置く?

1つ目のポイントは暗号鍵です。大切なデータを守るために暗号化します。 暗号化とは、第三者に情報の内容が分からないようにするための方法または技術です。この時に暗号鍵というデータを使用します。例えば「いろは」という文字列(暗号化する前の文を平文といいます)を暗号化して「d0re83ze」という暗号文を得ます。第三者が暗号文を見ても平文の内容は分かりません。このように暗号化はデータの機密性を守るための手段として有効と言えます。しかし、暗号文は平文に戻さないと内容が分かりません。暗号文は、暗号化と同様に暗号鍵を使用することで元の平文に戻すことが出来ます。これを復号といいます。

このように暗号化と復号には鍵が必要です、しかしその暗号鍵をどこに置けばいいのでしょうか?例として家には鍵をかけます。その鍵をどこに保管するのがいいのでしょうか?常に持ち歩く、決められた鍵置き場に置く、家の中の金庫に入れるなど色々な方法が考えられます。家の鍵同様に暗号鍵も別途保護する必要があります。

暗号鍵のハードコーディングは危険

暗号鍵の置き場所として、鍵をソースコード内にハードコーディングする方法があります。しかしハードコーディングは危険です。暗号鍵をソースコード内にハードコーディングした場合、攻撃者によって実行ファイルを逆コンパイルされると、ソースコードまでさかのぼって解析することができ簡単に情報を取り出されてしまいます。これは家に鍵をかけて玄関前の植木鉢の下に鍵を隠しているようなものです。

暗号鍵のハードコーデイングの脆弱性はCWE※による脆弱性タイプでもCWE-321: Use of Hard-coded Cryptographic Key (ハードコードされた暗号鍵の使用)と識別されています。

※共通脆弱性タイプ一覧CWE(Common Weakness Enumeration)は、ソフトウェアにおけるセキュリティ上の弱点(脆弱性)の種類を識別するための共通の基準を目指しています。 1999年頃から米国政府の支援を受けた非営利団体のMITREが中心となり仕様策定が行われ、2006年3月に最初の原案が公開されました。その後、40を超えるベンダーや研究機関が協力して仕様改善や内容拡充が行われ、2008年9月9日にCWEバージョン1.0が公開されました。

暗号鍵の置き場所が鎖の弱い部分にならないようにするためには、どのような対策があるでしょうか。

鍵を守るには

家の鍵を守るのと同様に、暗号鍵を守るためにも大切なポイントがあります。まずは「鍵を盗まれない」ことです。家の鍵では盗まれないように人の目につかない場所に保管する、金庫に保管する。などが考えられます。

ハードウェアによる保護機構を持ったストレージに保管する

「鍵を盗まれない」ようにするためには、鍵は別の場所に格納するのが有効です。例として一般的な読み出し手段で実行ファイルを盗難されないように、ハードウェアによる保護機構を持ったストレージに保管します。

ハードウェアによる保護機構は、HSM(Hardware Security Module)と呼ばれます。ソフトウェアだけで鍵を守ろうとしても限界があるため、データの暗号化やデジタル署名の生成に使用する鍵をハードウェアで安全に保管および演算します。

ハードウェア及びソフトウェア両方を含む “暗号モジュール” に関する要件はFIPS 140-2(暗号モジュールに関するセキュリティ要件の仕様を規定する米国連邦標準規格)で規定されています。

FIPS 140-2は、 “レベル1” から “レベル4” と命名した四つのレベルを規定します。特定の用途でどのセキュリティレベルが要求されるのかは規定しません。

  • レベル1: 一番低いレベルであり、非常に限定した要件を課する; 大まかに、すべてのコンポーネントが製品品質であり、甚だしくセキュリティの欠如がないこと。
  • レベル2: レベル1に次の要件を加える; 物理的な改竄の痕跡を残すこと、及びオペレータの役割ベースでの認証を行うこと。
  • レベル3: レベル2に次の要件を加える; 物理的な改竄への耐性(モジュール中に含まれる取扱注意情報への攻撃者のアクセスを困難にする)を持つこと、オペレータのIDベースでの認証を行うこと、及び重要なセキュリティパラメータがモジュールに出入力するインタフェースと、その他のインタフェースとを物理的又は論理的に分離すること。
  • レベル4: 物理的なセキュリティ要件がより厳格となり、環境条件を変動させての攻撃に対して頑健であることを要求する。

リバースエンジニアリングを防ぐ

次に暗号鍵を守るための大切なポイントは「リバースエンジニアリング自体を防ぐ」ことです。 リバースエンジニアリングとは、ソフトウェアの動作を解析したり構造を分析すること全般を指しますが、ここではソースコードを解析して暗号鍵を取り出すことを指します。リバースエンジニアリングの対策としては2つあります。1つは、鍵で暗号鍵を暗号化して通常のストレージに保管することです。家でも鍵を金庫(キーボックス)に入れて保管すれば持ち出せません。暗号鍵を暗号化するための鍵はHSMに保存するか、ユーザ入力パスワードから動的に鍵を作成すると安心です。もう1つは、デバッグインタフェースを塞ぐことです。家に入るドアも窓もなければ、家に侵入することはできません。すなわちソースコード内から暗号鍵の取り出し自体を防ぎます。

「鍵を盗まれない」「リバースエンジニアリング自体を防ぐ」の2つのポイントは排他ではありません。両方適用可能なので、出来るなら両方適用したほうが良いです。

更新の仕組みをどうするか?

2つ目のポイントは更新の仕組みです。 脆弱性修正のための更新の機能を準備することはもちろん必要ですが、更新の際にどのように更新プログラム(パッチ)を配布するかを検討する必要があります。パッチ配布経路を準備し、単純に配布すればいいわけではありません。パッチが悪意のある第三者に改ざんされてしまうと、製品が正常に動作しないだけではなく更新できなかった脆弱性を悪用されてしまうかもしれません。

パッチ配布のポリシー

パッチ配布の際のポリシーの例は以下になります。

  • 配布するプログラムには署名やMAC等の改ざん防止コードを添付し、検証に成功してから適用する。
  • 盗聴の可能性がある経路(インターネット回線、USBなど)を使用する場合は、データを暗号化する。加えて、脆弱性のある過去のバージョンに戻されるのを防ぐため、古いバージョンへのロールバックを防止する仕組みを持つこと。ただし、更新失敗時に直前にバージョンに戻すことは許容する。

また、更新管理(機器間のバージョン不整合が起きないように適用漏れを防ぐ。運用中のサービスに支障が出ないように適用タイミングを制御する。など)の仕組みも用意すると安心です。

ロールバックの是非

攻撃手法のひとつに、バージョンロールバック攻撃があります。この攻撃は脆弱性が発見されている古いバージョンを使用するよう誘導し、その脆弱性を利用して攻撃を行うもので、ダウングレード攻撃の一種です。ロールバック防止と更新失敗時の振る舞いに関しては、利便性とセキュリティのトレードオフになっていて、あるべき仕様を一概に決められない問題です。

ロールバック制限機能の判断の観点として下記を検討します。

  • 開発効率が悪くならないか?
  • 障害発生時の解析の効率に影響がないか?
  • 更新失敗時の復旧に影響がないか?

また、コストや実装の制約にもかなり影響を受けるので、以下の選択肢の例を検討します。

  • ロールバックを許容する/しない
  • 最新のバージョン値に対して一定範囲までのロールバックを許容する/しない(※1)
  • 直前に使っていたバージョンまでのロールバックは許容する/しない(※2)
  • 更新失敗以外でのロールバックを許容する/しない
  • 同じバージョンでの上書きを許容する/しない

※1:最新のバージョン値をサーバ等に取りに行くための安全な通信経路が必要
※2:直前のバージョンを覚えておくために2面持ちする容量と複雑な更新の仕組みが必要

※2より※1の方が利便性が高い(セキュリティが低い)オプション

開発者機能を残してもいい?

3つ目のポイントは開発者向け機能です。 不具合が発見された場合はもちろん修正しなければいけませんが、不具合の箇所を特定するにはデバッグをする必要があります。デバッグとは不具合を発見し修正する作業です。サーバーシステムではSSHがありますが組み込みシステムでは開発用のポート(JTAGなど)があります。この開発用ポートを使用してプログラムの読み出し、デバッグ(ステップ実行、メモリの読み出しや書き換えなど)したりファームウェアに書き込んだりすることができます。開発者にとっては必要な機能ですが、これは攻撃者にとっても便利な機能になります。開発者は、不具合を発見した際にデバッグ作業を楽にしたいからといって出荷後開発用ポートを有効な状態にしてはいけません。その場合、開発者と同様に攻撃者もデバッグ機能を利用して、脆弱性を探したりファームウェアを書き込むことが可能になります。

開発者機能の無効化

開発用のポートの対策としては、有効にする手段を完全になくしてしまえば悪用されようがないため、開発者機能の入出力ポートを完全無効化するのが望ましいです。しかし、出荷後にデバッグ機能が必要になるケースもあります。その場合は、認証(パスワード、証明書など)の仕組みを設けて開発者ポートの利用を制限することもあります。また開発者向け機能は開発用のポートには限りません。管理者権限でのコンソールへのログイン機能、GDBなどのデバッグツールの他、OSやネットワークの設定ツールや、単純なところではテキストエディタも「製品の動作には不要な(=開発者向け)機能」と考えられます。

開発者向け機能の対策としては以下を検討します。

  • 削除できるものは削除する(アンインストール/またはそもそもビルド対象外)
  • 削除が難しいものは実行できないようにする(rootしか実行できないようにした上でrootログインを止めるなど)

おわりに

セキュリティ設計をする際には、考慮することがたくさんあります。
どの方法を選択するかは、期間/コスト/技術のトレードオフとなりますがその際の指針に活用頂ければ幸いです。

参考文献