昨今、組込みセキュリティに関しては、誰もが少し不安を感じています。組込みシステムは急速に変化する分野であり、常に新たな脅威が出現しているのが現状です。何よりも悪いことに、組込みソフトウェア開発に携わる私たちは通常、リソースに制約のあるハードウェアの世界に生きており、ほとんどのセキュリティ防御メカニズムとは相容れません。
さらに事態を複雑にしているのは、組込みセキュリティ要件は、保護する必要があるものの重要性、潜在的な攻撃対象領域、技術的および経済的に何が実用的であるかによって大きく異なります。言い換えると、組込みセキュリティは、画一的なアプローチではなく、より広範囲にわたるものです。たとえば、銀行のセキュリティ要件は、レストランのセキュリティ要件とは大きく異なります。組込みシステムでも同じことが言えます。
組込みシステムのハードウェアセキュリティ対策
組込みプロセッサは、さまざまなハードウェアセキュリティ機能を提供します。最も一般的なものを見てみましょう。
改ざん防止
この機能は、ファームウェアを不正アクセスから保護します。これは、ファームウェア IP を保護し、攻撃者がオブジェクトコードの脆弱性を調査するのを防ぐために使用されます。
ロックステップ実行
この機能では、複数のプロセッサが同じデータで同じコードを実行するため、正確なコード実行が保証されます。このようなハードウェアは、主に安全性が重要なアプリケーションで使用されますが、より安全なコードは一般的により安全です。
アンチグリッチ
この機能は、攻撃者が電源やその他のシステム信号を操作して異常なプログラムを実行することを防ぐ回路を採用しています。そうしないと、攻撃者が MMU または MPU のセットアップを有効にする命令を「スキップ」できる可能性があります。
フラッシュからのみ実行
ほとんどのマイクロコントローラ (MCU) はフラッシュから命令を実行します。これらの MCU の一部では、RAM からの実行を禁止することもできます。これは、リモート実行攻撃における悪意のあるコードの動的挿入を防ぐために推奨されます。
ハードウェアのスタック制限
一部のプロセッサには、スタックオーバーフローによるメモリ破損を防ぐスタック制限レジスタがあります。
ハードウェアウォッチドッグタイマー
多くのプロセッサには、マスク不可能なハードウェアウォッチドッグタイマーが搭載されています。この機能はフェイルセーフとして機能します。通常の動作では、アプリケーションコードは定期的に、常に有効期限が切れる前にウォッチドッグをリセットします。異常な実行状態になった場合、ウォッチドッグはリセットされないため、異常な実行を停止するマスク不可能な割り込みが発生します。通常、アプリケーションはウォッチドッグの有効期限が切れた後に単にリセットされます。
真の乱数ジェネレーター (TRNG)
ハードウェアにTRNG またはさらに基本的な乱数ジェネレーター (RNG) があると、非常に有益です。これはネットワーク接続されたデバイスにとって最も重要です。
メモリ管理ユニット (MMU)
通常、このハードウェア機能は、より大型で強力なプロセッサでのみ利用可能で、メモリのさまざまな領域へのアクセスを制限できます。ほとんどの場合、これはリセット後にアプリケーションファームウェアによって一度設定され、さまざまなメモリ領域へのアクセスをマッピングして保護します。
メモリ保護ユニット (MPU)
このハードウェア機能は MMU に似ており、より小型でリソースに制約のあるデバイスに搭載されています。繰り返しになりますが、これも通常、リセット後にさまざまなメモリ領域を保護するためにアプリケーションファームウェアによって設定されます。スタック制限レジスタがない場合、MPU は各スレッドのスタックの先頭に保護されたブロックを設定し、スタックオーバーフローを防止できます。
Secure Element (SE) または Trusted Platform Module (TPM)
ネットワーク化された組込みデバイスの場合、SE または TMP は、資格情報やその他の機密情報をメインアプリケーションから安全に分離できます。これにより、デバイスのネットワークセキュリティが大幅に向上するため、強く推奨されます。
もちろん、ここで述べたハードウェアセキュリティ機能には、回路、消費電力、サイズなどの関連コストが伴います。これらのコストは、前述のリスクと利益の分析を行う必要があります。
組込みシステムのソフトウェアセキュリティ対策
利用できるハードウェアセキュリティ機能に関係なく、セキュリティを向上させるためにソフトウェアで実行できることもあります。ここでは、デバイスのセキュリティを強化できるソフトウェアのベストプラクティスをいくつか紹介します。
デバイスのファームウェアを強化します
より安全なコードはより安全であるため、100% のステートメントおよび 100% の分岐/決定カバレッジテストを目指す必要があります。
静的解析と関連ツールを活用します
前述の強化に加えて、静的解析ツール、ペネトレーションテスト、ファジングツールを活用することをお勧めします。これらのテストツールは、開発中の微妙な問題を事前に発見するのに役立ち、現場のデバイスをデバッグするよりもはるかに簡単です。
適切な (またはそれ以上の) スタックサイズを使用します
スタックオーバーフローは、組込みシステムにおけるメモリ破損の最大の原因です。各スレッドスタックには、最悪の場合の関数呼び出しネスト(各関数のすべてのローカル変数を含む)に対応できる十分なメモリが必要です。十分なメモリがない場合、スタックはスタックの直前のメモリにオーバーフローします。この問題は、ハードウェアのスタック制限機能を使用するか、あるいは MPU/MMU を使用してスタックの直上の領域を保護することで防止できます。
バッファサイズを明示的に指定して確認します
バッファが提供されるすべての関数では、オーバーランを回避するために、呼び出し元がバッファのサイズを明示的に提供し、呼び出された側がサイズを明示的にチェックすることが重要です。
メモリ破損の可能性に注意してください
スレッドスタックがオーバーフローすると、通常、スレッドスタックメモリの直前のメモリが破損します (ほとんどのアーキテクチャでは、スレッドスタックは下位アドレスに向かって増加します)。対照的に、バッファオーバーフローでは、バッファの直後にあるメモリが破損する可能性が高くなります。したがって、重要なデータはスタックの後、バッファメモリの前に配置する方が一般的に安全です。
実行時にメモリ破損を検出するために使用できる、スタックとバッファの間に実行時および場所固有のデータパターンを挿入します。
関数ポインタには細心の注意を払ってください
関数ポインタは、意図的でない場合でも意図的な場合でも、望ましくないプログラムの実行への簡単なパスを提供します。たとえば、関数ポインタをバッファ内に配置することは、バッファオーバーフローによって関数ポインタが上書きされる可能性があるため、推奨されません。関数ポインタを使用する前に、小さなハッシュやチェックサムを使用して関数ポインタを検証することも良い方法です。関数ポインタの破損は、攻撃者が望ましくないリモート実行を開始し、組込みセキュリティを危険にさらす最も簡単な方法です。
ハードウェアセキュリティリソースを活用し、組込みソフトウェアのベストプラクティスに従うことで、組込みデバイスのセキュリティを向上できます。また、デバイス側およびデバイスが存在するネットワークレベルでのすべてのセキュリティ対策は、全体的な多層防御戦略の一部であることも忘れないでください。唯一の目標は、攻撃者が望ましくないアクセスを取得することをより困難にすることです。