本開示に係る実施の形態について、図面を参照しながら詳細に説明する。なお、図中の同一または相当部分については、同一符号を付してその説明は繰り返さない。
<A.概要>
まず、本実施の形態に従うソフトウェア開発システム1におけるソフトウェア検証方法の一例を説明する。本実施の形態においては、ソースコード10から生成される中間表現20自体を検証する。このように、中間表現20を検証することで、ソースコード10の言語およびターゲットアーキテクチャなどに依存することなく、汎用的な検証が可能となる。また、ソースコード10をコンパイルして生成される中間表現20を検証するので、ソースコード10に含まれるノウハウなどを秘匿化できる。
中間表現20の検証は、アプリケーションプログラムに含まれる脆弱性の有無を検証することに加えて、存在する脆弱性の種類およびソースコード10の位置を特定する処理を含む。
さらに、中間表現20に脆弱性が存在しない(すなわち、検証結果に問題が無い)ことを条件に、対応する実行コード30の生成、あるいは、生成された実行コード30の実行を許容するようにしてもよい。
本明細書において、「中間表現」は、任意のプログラミング言語(通常は、高級言語)で記載されたソースコードを、任意のアーキテクチャをターゲットとしてコンパイルすることで生成されるコードを意味する。中間表現20は、任意の仮想マシンのマシン語を包含し得る。なお、中間表現20は、必ずしもバイナリ形式である必要はなく、アセンブラ言語のような自然言語に類似した形式で表現されてもよい。
中間表現20のコード形式としては、LLVM IR形式やLLVM BC形式などが挙げられる。
通常、中間表現20は、実行コード30が実行されるプラットフォームおよびアーキテクチャとは独立して生成される。ソースコード10から中間表現20の生成には、静的コンパイラが用いられてもよい。中間表現20は、特定のプラットフォームおよびアーキテクチャに向けられた実行コード30としてコンパイルされてもよい。中間表現20は、元になったソースコードに比較してデータ量が削減されるとともに、記述される処理自体も効率化できる。
図1は、本実施の形態に従うソフトウェア開発システム1におけるソフトウェア検証方法の一例を説明するための図である。図1(A)および(B)に示すように、ソフトウェア開発システム1においては、ソースコード10から生成された中間表現20が任意の主体により検証される。図1(A)および(B)に示す例では、検証サーバ200が中間表現20を検証する例を示すが、これに限られず、任意の主体によって検証されてもよい。
典型的には、検証結果400は、ソースコード10の作成主体であるユーザにフィードバックされ、必要に応じて、ソースコード10に対する修正が行われる。後述するように、検証結果400は、検出された脆弱性の種類を示す情報およびソースコード10の位置を特定する情報を含むので、ユーザは、ソースコード10を適切に修正できる。
なお、検証結果400に問題が無いことを条件として、実行コード30の生成、あるいは、実行コード30の実行を許可するようにしてもよい。
例えば、図1(A)には、検証結果400に問題が無いことを、実行コード30の生成条件とする例を示す。この例において、検証結果400に問題が無い場合には、検証サーバ200は、中間表現20に対して認証を与える。中間表現20に対する認証を生成条件として、中間表現20から実行コード30が生成される。このような生成条件を採用することで、生成される実行コード30についての安全性を高めることができる。
また、図1(B)には、検証結果400に問題が無いことを、中間表現20から生成された実行コード30の実行条件とする例を示す。この例において、検証結果400に問題が無い場合には、検証サーバ200は、中間表現20に対する認証を含む証明書40を発行する。並行して、中間表現20から実行コード30が生成される。生成された実行コード30の実行には、対応する証明書40の存在を条件としてもよい。このように、中間表現20に対する証明書40の存在を実行コード30の実行条件に含めるようにしてもよい。このような実行条件を採用することで、安全性が損なわれた状態で実行コード30が実行される可能性を低減できる。
このように、ソースコード10ではなく中間表現20を検証することで、ソースコード10に含まれるノウハウなどを秘匿化した状態で、アプリケーションプログラムに含まれる脆弱性をより効率的に発見できる。また、中間表現20を検証することで、ソースコード10の言語およびターゲットアーキテクチャなどに応じた複数の検証ロジックを用意する必要がなく、効率的な検証を実現できる。
なお、中間表現20を生成する際に、ソースコード10をコンパイルすることで生成されるデバッグ情報50を中間表現20と合わせて検証サーバ200へ送信するようにしてもよい。中間表現20を参照することで、検出された脆弱性に対応するソースコード10の位置をより詳細に特定できる。デバッグ情報50を利用する検証結果400の生成処理については、後述する。
次に、本実施の形態に従うソフトウェア開発システム1を用いた実行コード30の生成手順の一例について説明する。
図2および図3は、本実施の形態に従うソフトウェア開発システム1におけるソースコード10から実行コード30を生成する手順の概要を示す図である。
図2を参照して、ソフトウェア開発システム1は、1または複数のソフトウェア開発装置100と、ソフトウェア開発装置100からインターネット2などを介してアクセス可能な検証サーバ200とを含む。
ソフトウェア開発装置100は、エッジデバイス4に含まれるコントローラ300で実行されるアプリケーションプログラムの作成を支援する。但し、本発明の技術的範囲は、エッジデバイス4で実行されるアプリケーションプログラムの作成に限定されるものではなく、任意のコンピューティングデバイスで実行されるアプリケーションプログラムの作成に適用可能である。
ソフトウェア開発装置100には、統合開発環境(IDE:Integrated Development Environment)が提供されており、ユーザは統合開発環境上で任意のアプリケーションプログラムを作成できる。
エッジデバイス4に含まれるコントローラ300は、コンピューティングデバイスの一例であり、プロセッサを含む。エッジデバイス4としては、どのようなデバイスであってもよいが、典型的には、工場設備、家庭内の各種装置、社会インフラ設備、車両などの移動体、任意の携帯デバイスなどが想定される。後述するように、コントローラ300は、プロセッサを有しており、ソフトウェア開発装置100からのアプリケーションプログラムを実行可能になっている。
まず、作成主体であるユーザは、ソフトウェア開発装置100を用いてソースコードを作成する((1)ソースコード作成)。そして、作成されたソースコードは、ソフトウェア開発装置100においてコンパイルされて中間表現20が生成される((2)中間表現生成)。
中間表現20から実行コード30が生成される前に中間表現20が検証される。図2に示す例においては、生成された中間表現20が検証サーバ200へ送信される((3)中間表現検証依頼)。
検証サーバ200は、ソフトウェア開発装置100からの中間表現20を検証する((4)中間表現の検証)。そして、検証サーバ200は、中間表現20の検証によって得られた検証結果400をソフトウェア開発装置100へ送信する。ソフトウェア開発装置100は、検証サーバ200から検証結果400を受信する((5)検証結果受信)。
検証サーバ200からの検証結果400に問題があれば、ユーザは、検証結果400を参照して、ソースコード10を修正する((6)ソースコード修正)。そして、(2)から(5)の処理が繰り返される。
一方、検証サーバ200からの検証結果400に問題が無ければ、ユーザは、ソフトウェア開発装置100を用いて、中間表現20から実行コード30を生成する((7)実行コード生成)。生成された実行コード30は、ソフトウェア開発装置100からエッジデバイス4のコントローラ300へ転送される((8)実行コード転送)。そして、エッジデバイス4のコントローラ300は、必要に応じて、転送された実行コード30を実行する((9)実行コードの実行)。
図2および図3に示すような一連の処理によって、エッジデバイス4のコントローラ300において、安全性の高いアプリケーションプログラムの実行を保証できる。
<B.ハードウェア構成例>
次に、本実施の形態に従うソフトウェア開発システム1を構成する主要装置のハードウェア構成例について説明する。
(b1:ソフトウェア開発装置100)
ソフトウェア開発装置100は、典型的には汎用コンピュータで実現される。
図4は、本実施の形態に従うソフトウェア開発装置100のハードウェア構成例を示す模式図である。図4を参照して、ソフトウェア開発装置100は、主たるコンポーネントとして、プロセッサ102と、メインメモリ104と、入力部106と、ディスプレイ108と、ハードディスク110と、通信インターフェイス122とを含む。これらのコンポーネントは、内部バス120を介して接続されている。
プロセッサ102は、例えば、CPU(Central Processing Unit)やGPU(Graphics Processing Unit)などで構成される。複数のプロセッサ102が配置されてもよいし、複数のコアを有するプロセッサ102を採用してもよい。
メインメモリ104は、DRAM(Dynamic Random Access Memory)やSRAM(Static Random Access Memory)などの揮発性記憶装置で構成される。ハードディスク110は、プロセッサ102で実行される各種プログラムや各種データを保持する不揮発性記憶装置である。ハードディスク110に代えて、SSD(Solid State Drive)やフラッシュメモリなどを採用してもよい。ハードディスク110に格納されたプログラムのうち、指定されたプログラムコードがメインメモリ104上に展開され、プロセッサ102は、メインメモリ104上に展開されたプログラムコードに含まれるコンピュータ可読命令(computer-readable instructions)を順次実行することで、後述するような各種機能を実現する。
典型的には、ハードディスク110には、ユーザが任意に作成するソースコード10と、統合開発環境を実現するためのソフトウェア開発プログラム114と、ソースコード10から生成される実行コード30とが格納される。ソフトウェア開発プログラム114は、ユーザが任意に作成するソースコード10から中間表現20を介して実行コード30を生成するものであり、アプリケーションプログラムの開発環境を提供するモジュールを含む。
入力部106は、ソフトウェア開発装置100を操作するユーザの入力操作を受け付ける。入力部106は、例えば、キーボード、マウス、表示デバイス上に配置されたタッチパネル、ソフトウェア開発装置100の筐体に配置された操作ボタンなどであってもよい。
ディスプレイ108は、プロセッサ102での処理結果などを表示する。ディスプレイ108は、例えば、LCD(Liquid Crystal Display)や有機EL(Electro-Luminescence)ディスプレイなどであってもよい。
通信インターフェイス122は、検証サーバ200とのデータ交換を担当する。通信インターフェイス122は、例えば、インターネットを介した通信ができるように、イーサネット(登録商標)ポートを含んでいてもよい。
なお、ソフトウェア開発装置100の全部または一部は、コンピュータ可読命令に相当する回路が組み込まれたASIC(Application Specific Integrated Circuit)などのハードワイヤード回路を用いて実現してもよい。さらにあるいは、FPGA(field-programmable gate array)上にコンピュータ可読命令に相当する回路を用いて実現してもよい。また、プロセッサ102およびメインメモリ、ASIC、FPGAなどを適宜組み合わせて実現してもよい。
ソフトウェア開発装置100は、コンピュータ可読命令を含むソフトウェア開発プログラム114を格納する非一過性(non-transitory)のメディアから、当該格納しているプログラムなどを読み出すためのコンポーネントをさらに有していてもよい。メディアは、例えば、DVD(Digital Versatile Disc)などの光学メディア、USBメモリなどの半導体メディアなどであってもよい。
なお、ソフトウェア開発プログラム114は、メディアを介してソフトウェア開発装置100にインストールされるだけではなく、ネットワーク上の配信サーバから提供されるようにしてもよい。
(b2:検証サーバ200)
検証サーバ200についても、典型的には汎用コンピュータで実現される。
図5は、本実施の形態に従う検証サーバ200のハードウェア構成例を示す模式図である。図5を参照して、検証サーバ200は、主たるコンポーネントとして、1または複数のプロセッサ202と、メインメモリ204と、入力部206と、ディスプレイ208と、ハードディスク210と、通信インターフェイス222とを含む。これらのコンポーネントは、内部バス220を介して接続されている。
プロセッサ202は、例えば、CPUやGPUなどで構成される。複数のプロセッサ202が配置されてもよいし、複数のコアを有するプロセッサ202を採用してもよい。
メインメモリ204は、DRAMやSRAMなどの揮発性記憶装置で構成される。ハードディスク210は、プロセッサ202で実行される各種プログラムや各種データを保持する不揮発性記憶装置である。ハードディスク210に代えて、SSDやフラッシュメモリなどを採用してもよい。ハードディスク210に格納されたプログラムのうち、指定されたプログラムコードがメインメモリ204上に展開され、プロセッサ202は、メインメモリ204上に展開されたプログラムコードに含まれるコンピュータ可読命令を順次実行することで、後述するような各種機能を実現する。
典型的には、ハードディスク210には、外部からの依頼を受けて中間表現20の検証処理を実行する検証エンジン212が格納される。検証エンジン212は、中間表現20に含まれる脆弱性の有無を検証する処理、および、存在する脆弱性の種類を示す情報およびソースコード10の位置を特定する処理などを実行する。
ハードディスク210には、検証エンジン212による検証結果400の内容に応じて証明書を発行するための証明書発行エンジン214をさらに格納するようにしてもよい。証明書発行エンジン214は、検証結果400に問題が無い場合に、中間表現20に関連付けられる証明書40を発行する。
入力部206は、検証サーバ200を操作するユーザの入力操作を受け付ける。ディスプレイ208は、プロセッサ202での処理結果などを表示する。
通信インターフェイス222は、ソフトウェア開発装置100とのデータ交換を担当する。通信インターフェイス222は、例えば、インターネットを介した通信ができるように、イーサネット(登録商標)ポートを含んでいてもよい。
なお、検証サーバ200の全部または一部は、コンピュータ可読命令に相当する回路が組み込まれたASICなどのハードワイヤード回路を用いて実現してもよい。さらにあるいは、FPGA上にコンピュータ可読命令に相当する回路を用いて実現してもよい。また、プロセッサ202およびメインメモリ、ASIC、FPGAなどを適宜組み合わせて実現してもよい。
なお、実行に必要なプログラムは、メディアを介して検証サーバ200にインストールされるだけではなく、ネットワーク上の配信サーバから提供されるようにしてもよい。
<C.手順例>
次に、本実施の形態に従うソフトウェア開発システム1におけるソフトウェア検証方法の具体的な手順例について説明する。
図6は、本実施の形態に従うソフトウェア開発システム1におけるソフトウェア検証方法の手順例を示すシーケンスチャートである。図6に示すソフトウェア開発装置100が実行する処理については、典型的には、ソフトウェア開発装置100のプロセッサ102がソフトウェア開発プログラム114を実行することで実現されてもよい。同様に、検証サーバ200が実行する処理については、典型的には、検証サーバ200のプロセッサ202が必要なプログラムを実行することで実現されてもよい。
図6を参照して、ユーザは、ソフトウェア開発装置100を操作して、ソースコード10を作成する(ステップS100)。ソースコード10の作成後、ユーザは、ソフトウェア開発装置100を操作して、ソースコード10をコンパイルして、ソースコード10から中間表現20を生成する(ステップS102)。
さらに、ユーザは、ソフトウェア開発装置100を操作して、生成した中間表現20に対する検証を検証サーバ200へ依頼し(ステップS104)、検証サーバ200からの応答を待つ(ステップS106)。なお、中間表現20に加えて、デバッグ情報50が検証サーバ200へ送信されてもよい。
検証サーバ200は、ソフトウェア開発装置100からの検証依頼に応答して、依頼された中間表現20を検証する(ステップS200)。検証サーバ200は、検証により生成された検証結果400をソフトウェア開発装置100へ送信する(ステップS202)。すなわち、検証サーバ200は、検証により得られた検証結果400を出力する。
検証結果400に問題があれば(ステップS108においてNO)、ユーザは、ソフトウェア開発装置100を操作して、ソースコード10を修正する(ステップS100)。ソースコード10の修正後、ステップS102以下の処理が再度実行される。
これに対して、検証結果400に問題が無ければ(ステップS108においてYES)、ユーザは、ソフトウェア開発装置100を操作して、対象の中間表現20から実行コード30を生成する(ステップS110)。最終的に、ユーザは、ソフトウェア開発装置100を操作して、生成した実行コード30をターゲットのコントローラ300へ転送する(ステップS112)。そして、処理は終了する。
<D.検証処理の詳細>
次に、本実施の形態に従うソフトウェア開発システム1における検証処理の詳細について説明する。
本実施の形態における中間表現20に対する検証は、中間表現20が脆弱性に関する所定の規則あるいは規約を満たすか否かを判断する処理を含んでいてもよい。脆弱性に関する所定の規則あるいは規約としては、アプリケーションプログラム(中間表現20および対応する実行コード30)が安全であるための条件を含む。このような安全であるための条件としては、典型的には、以下のようなものが挙げられる。
(1)安全性が確認できないメモリ操作(あるいは、危険なメモリ操作)が含まれていないこと(あるいは、そのようなメモリ操作がコンパイル可能ではないこと)
(2)コード中に未定義動作が存在しないこと
(3)エラー処理構文が欠如していないこと(エラー処理構文が適切に含まれていること)
(4)無秩序なエラー処理が含まれていないこと
上記(1)の安全性が確認できないメモリ操作(あるいは、危険なメモリ操作)は、システム領域やセキュア領域に対するメモリアクセスや、メモリ領域に対するブロック消去やブロック書き込みなどのメモリ操作を含む。
上記(2)の未定義動作は、コード中に処理が定義されていないプロシージャやファンクションを含む。
上記(3)のエラー処理構文は、例外が発生した場合の処理を規則する記述を含む。
上記(4)の無秩序なエラー処理は、例外が発生した場合の過度なエラー処理などを含む。
さらに、脆弱性に関する所定の規則あるいは規約としては、アプリケーションプログラム(中間表現20および対応する実行コード30)に、いわゆるバックドアが存在しないことを含んでよい。バックドアとは、アプリケーションプログラムの利用者が認識しない方法で、(通常は、悪意のある)第三者がアプリケーションプログラムにアクセスするための機能や接続口などを意味する。中間表現20に対する検証においては、外部からのアクセス経路が中間表現20に含まれるか否かが判断されてもよい。
上述したような検証は、脆弱性に関する所定の規則あるいは規約を定義するルールを参照することで実現するようにしてもよいし、いわゆるAI(Artificial Intelligence)などの機械学習を用いて、検証を実現するための検証モデルを逐次更新するようにしてもよい。
上述したような検証内容をより具体化した検証処理としては、以下のようなものが挙げられる。
(a)静的バッファオーバーフロー
(b)実行時バッファオーバーフロー
(c)整数オーバーフロー
(d)フォーマット文字列バグ
(e)制御文の不備
(f)未初期化データへのアクセス
(g)未使用関数の抽出
(h)未初期化データへのアクセス
(i)ヌルポインタへの書き込み
(j)ヌルポインタの読み取り
(k)リソースの二重解放
(l)OSコマンドインジェクション
なお、上述した検証処理に限定されるものではなく、任意の検証処理を実装すればよい。また、上述した検証処理のすべてではなく一部のみを実装するようにしてもよい。
次に、いくつかの典型的な検証処理の詳細について説明する。
(d1:静的バッファオーバーフロー)
静的バッファオーバーフローを検証する処理について説明する。
図7は、静的バッファオーバーフローの検証処理を説明するためのコード例を示す図である。図7(A)には、静的バッファオーバーフローを発生させるC言語により記述されたソースコードの例を示す。
図7(A)のソースコードにおいて、配列a[]のうち10番目の要素までは定義されているものの、それ以降の要素については未定義である。そのため、実行するたびに異なる結果が出力されることになる。なお、図7(A)のソースコードには、説明の便宜上、「^^^バッファオーバーフローが発生します」とのコメントが付されている。
例えば、コンパイラの一例であるgcc(9.2.0-Wall-Wextraオプション)を用いて、図7(A)のソースコードをコンパイルした場合には、警告などは出力されずに、コンパイルが完了する。
図7(B)には、図7(A)のソースコードをコンパイルして生成される中間表現の例(一例として、LLVM IR形式)を示す。なお、図7(B)に示す中間表現において、「中略」と記載されている部分のコードの表示は省略されている。
本実施の形態に従う検証エンジン212は、(1)固定長配列にインデックスでアクセスするコードを検索、および、(2)配列の要素数を超える定数でアクセスするコードを脆弱性として検出、の2つの処理により、静的バッファオーバーフローを検出する。
図7(B)に示す中間表現において、4行目の%3から始まる命令の組には、getelementptr命令が規定されている。getelementptr命令は、配列要素のポインタを取得するための命令である。inbounds命令に続く[10 x i64]は、対象の配列が符号付き64ビット整数を10個含むものであることを意味する。最後にある「100」は、対象の配列の100番目の要素にアクセスすることを示す。
このような中間表現を解析することで、10個の要素しか含まない配列に対して、100番目の要素にアクセスしようとしていることが分かる。このような解析によって、検証エンジン212は、脆弱性として、静的バッファオーバーフローを検出する。
(d2:実行時バッファオーバーフロー)
実行時バッファオーバーフローを検証する処理について説明する。
図8は、実行時バッファオーバーフローの検証処理を説明するためのコード例を示す図である。
一般的に、配列へのアクセスにおいて指定されるインデックスはコンパイル時には確定できない。例えば、図8(A)に示すC言語により記述されたソースコードでは、実行時のインデックスの値(変数indexの値)次第でバッファオーバーフローが発生し得る。なお、図8(A)のソースコードには、説明の便宜上、「^^^indexの値次第でバッファオーバーフローが発生します」とのコメントが付されている。
本実施の形態に従うソフトウェア開発システム1における脆弱性に関する規則あるいは規約としては、インデックスの値を事前にチェックすることを要求するようにしてもよい。すなわち、配列へのアクセスに用いるインデックスの値を事前にチャックする処理を含まないソースコードあるいは中間表現は、脆弱性が存在すると評価してもよい。
本実施の形態に従う検証エンジン212は、配列へのアクセスに用いるインデックスの値を事前にチェックする処理が存在するか否かを検証する。
より具体的には、本実施の形態に従う検証エンジン212は、(1)バッファにインデックスでアクセスするコードを検索、および、(2)検索されたコードのうちインデックスの値を事前にチェックしていないコードを脆弱性として検出、の2つの処理により、実行時バッファオーバーフローを検出する。
図8(B)には、図8(A)のソースコードをコンパイルして生成される中間表現の例(一例として、LLVM IR形式)を示す。なお、図8(B)に示す中間表現において、「中略」と記載されている部分のコードの表示は省略されている。
図8(B)に示す中間表現において、6行目の%6から始まる命令の組には、getelementptr命令が規定されている。当該行の最後にある「i64 %5」から、具体的な数値ではなく、変数(ソースコード中の変数index)を用いたインデックスでのアクセスが存在していることを見つけることができる。
ここで、ソースコード中の変数indexに対応する「%5」をチェックせずに、バッファにアクセスするためのインデックスとして用いていることが、実行時バッファオーバーフローが発生する要因である。
図9は、図8に示されるコードを本実施の形態に従うソフトウェア開発システム1における脆弱性に関する規則あるいは規約に適合させた場合のコードの一例を示す図である。
図9(A)に示すC言語により記述されたソースコードでは、変数indexの値をif式でチェックすることでエラーハンドリングを行っている(3〜6行目参照)。このようなエラーハンドリングの処理を含むソースコードを、ソフトウェア開発システム1における脆弱性に関する規則あるいは規約に適合するコードとみなしてもよい。
図9(B)には、図9(A)のソースコードをコンパイルして生成される中間表現の例(一例として、LLVM IR形式)を示す。なお、図9(B)に示す中間表現において、「中略」と記載されている部分のコードの表示は省略されている。
図9(B)に示す中間表現において、「%5」の値をチェックして、そのチェック結果が問題無い場合のみ、「BoundsCheckOK」で配列にアクセスするように規定されている(6〜10行目)。
本実施の形態に従う検証エンジン212は、バッファにインデックスでアクセスするコードが存在する場合、そのアクセスに用いるインデックスの値を事前にチェックする処理が存在する場合に限って、脆弱性に関する規則あるいは規約に適合しており、脆弱性が存在しないと評価する。これに対して、バッファへのアクセスに用いるインデックスの値を事前にチェックする処理が存在しなければ、検証エンジン212は、脆弱性として、実行時バッファオーバーフローを検出する。
このような中間表現を解析することで、検証エンジン212は、脆弱性として、実行時バッファオーバーフローを検出する。
(d3:フォーマット文字列バグ)
フォーマット文字列バグを検証する処理について説明する。
図10は、フォーマット文字列バグの検証処理を説明するためのコード例を示す図である。
図10(A)に示すC言語により記述されたソースコードでは、printf関数のフォーマット文字列を悪用する攻撃を受けて、任意コードを実行される可能性がある。これは、printf関数などのフォーマット文字列の引数に、外部からの入力文字列をフォーマットのチェックを行わずに渡していることが要因である。
本実施の形態に従うソフトウェア開発システム1における脆弱性に関する規則あるいは規約として、フォーマット文字列を引数とするライブラリ関数に、文字列リテラル以外が入力されている場合には、脆弱性が存在すると評価してもよい。
より具体的には、本実施の形態に従う検証エンジン212は、(1)printf関数などのフォーマット文字列を引数に指定するコードを検索、(2)検索されたコードのうち固定長の文字列リテラルを引数が指定されていないコードを脆弱性として検出の2つの処理により、フォーマット文字列バグを検出する。
図10(B)には、図10(A)のソースコードをコンパイルして生成される中間表現の例(一例として、LLVM IR形式)を示す。なお、図10(B)に示す中間表現において、7行目で呼ばれるprintf関数の引数に%7が使用されている。本実施の形態に従う検証エンジン212は、引数で使用される%7にどのような値が格納されるかを解析して、固定長の文字列リテラルでなければ、脆弱性として、フォーマット文字列バグを検出する。
このような中間表現を解析することで、検証エンジン212は、脆弱性として、フォーマット文字列バグを検出する。
(d4:未使用関数の抽出)
未使用関数を抽出する処理について説明する。
一般的に、未使用関数は、プログラムのアタックサーフェスを増加させる。また、未使用関数は、バックドアの疑いもある。そこで、本実施の形態に従う検証エンジン212は、中間表現の静的コールグラフ解析を行うことで、未使用関数を抽出する。
図11は、未使用関数の抽出する処理を説明するための中間表現のコード例を示す図である。図11に示すように、関数呼び出しは、LLVM IR形式の中間表現においてはcall命令が使用される。検証エンジン212は、call命令の関係を解析することで、未使用の関数を抽出する。そして、検証エンジン212は、抽出した未使用の関数を脆弱性として検出する。
(d5:検証結果400の一例)
次に、本実施の形態に従うソフトウェア開発装置100が提供する検証結果400の一例について説明する。
検証結果400は、検証サーバ200の検証エンジン212が中間表現20を解析することで生成可能ではある。但し、より詳細な検証結果400を生成するためには、ソースコード10をコンパイルして中間表現20を生成する際に併せて生成されたデバッグ情報50も利用することになる。
デバッグ情報50は、中間表現20を生成したソースコード10の属性情報や、中間表現20に含まれるコードとソースコード10とを対応付けるための情報を含む。例えば、コンパイラの一例であるgccでは、「−g」オプションを付与してコンパイルすることで、中間表現20およびデバッグ情報50を生成できる。このように、ソースコード10から中間表現20の生成時に、デバッグ情報50を生成する処理が実行されてもよい。
図12は、本実施の形態に従うソフトウェア開発システム1が提供する検証結果400の一例を示す図である。図12には、検証結果400がレポートの形で提供される例を示すが、どのような形式で提供されてもよい。
また、検証結果400は、Webを通じて(すなわち、HTTP形式で)提供されてもよいし、テキスト形式あるいはPDF形式などでメールなどにより提供されてもよい。以下の説明においては、典型的に、Webを通じて検証結果400が提供される場合を想定する。
図12を参照して、検証結果400は、管理部410と、検証対象情報部420と、解析内容部430と、サマリー部440とを含む。
管理部410には、検証結果400に再度アクセスするためのパスワード412と、検証結果400を特定するための識別情報414とが表示される。例えば、検証サーバ200は、対応する識別情報414に関連付けて検証結果400を保持しており、保持されている検証結果400に再度アクセスするためには、識別情報414により対象の検証結果400を特定した上で、パスワード412の入力を必要としてもよい。
対象の検証結果400にアクセスするために、対応する識別情報414を含むURLを用いてもよい。例えば、図12に示す例では、識別情報414として、「F-ELc_cx」が付与されている。この識別情報414を含む「https://server.or.jp/F-ELc_cx」のようなURLを指定することでアクセスできるようにしてもよい。
検証対象情報部420には、検証の対象となった中間表現20に関する情報が表示される。具体的には、検証対象情報部420には、ソースファイル名422と、メモリレイアウト情報424と、コンパイル環境情報426とが表示される。
ソースファイル名422は、検証対象の中間表現20の生成元であるソースコード10のファイル名を示す。ソースファイル名422は、デバッグ情報50が参照できる場合に表示される。メモリレイアウト情報424は、アラインメントやエンディアンなどのメモリ上のレイアウトに関する情報を示す。コンパイル環境情報426は、ソースコード10から中間表現20の生成に用いられたコンパイル環境の情報を示す。
解析内容部430は、検出された脆弱性についての解析内容を含む。より具体的には、解析内容部430は、脆弱性情報432と、ソースコード情報434と、中間表現情報435と、詳細メッセージ436と、結果情報438とを含む。
脆弱性情報432は、検出された脆弱性の種類を示す情報である。図12に示す例では、脆弱性情報432として、「FormatStringBug」と表示されており、フォーマット文字列バグが脆弱性として検出されたことが分かる。
ソースコード情報434および中間表現情報435は、検出された脆弱性の種類および位置を特定するための情報を示す。
ソースコード情報434は、検出された脆弱性が存在するソースコードの位置を示す情報である。図12に示す例では、ソースコード情報434として、「main() in sample.c at line: 3」と表示されており、ソースコード10を含むソースファイル「sample.c」中の関数main()の3行目のコードが検知された場所であることを示す。なお、ソースコード10の行数(位置)を表示するためには、デバッグ情報50が必要になる。
中間表現情報435は、検出された脆弱性が存在する中間表現の位置を示す情報である。図12に示す例では、中間表現情報435には、中間表現のコードスニペットが表示される。図12に示す例では、右矢印記号->で示されている中間表現中のcall命令が、検出された脆弱性の発生要因になったことが分かる。
詳細メッセージ436には、検出された脆弱性に関する詳細を示すメッセージが表示される。図12に示す例では、詳細メッセージ436として、「Format String must be String-Literal」と表示されており、このメッセージにより、フォーマット文字列には、文字列リテラルを指定しなければならないことが分かる。
結果情報438として、「1 violation(s) detected in this rule.」と表示されており、対象の中間表現には、フォーマット文字列バグの脆弱性が1件検出されたことが分かる。
サマリー部440には、対象の中間表現に対して検出された脆弱性の総数が表示される。より具体的には、サマリー部440には、サマリー情報442が表示される。サマリー情報442としては、「1 violation(s) detected in this rule」と表示されており、検証の結果、検出された脆弱性の総数が1件だったことが分かる。
<E.実行環境>
本実施の形態に従うソフトウェア開発システム1において生成された実行コード30は、配布先のコントローラ300を構成するプロセッサあるいはマイクロコンピュータのセキュア領域に格納されることが好ましい。すなわち、中間表現20に対する認証を条件として生成された実行コード30に対して、何らかの改ざん行為が行われないように、実行環境において何らかの対処を行うことが好ましい。
また、本実施の形態に従うソフトウェア開発システム1において生成された中間表現20に対する証明書40および中間表現20から生成される実行コード30のいずれにも共通の署名(例えば、認証された中間表現20から生成されるハッシュ値)を埋め込んでおき、対応する証明書40が存在することを条件としてもよい。この場合には、実行コード30を実行する環境(典型的には、マイクロコンピュータのファームウェア)において、実行コード30に含まれる署名と、対応する証明書40に含まれる署名との一致を確認する処理を実装してもよい。
また、実行環境において、実行コード30を実行するか否かの評価(正当性の判断)は、いずれのタイミングで行ってもよいが、典型的には、実行環境であるコントローラ300の起動時(ファームウェアの起動中あるいは起動直後のアプリケーション実行開始前)に行うようにしてもよい。すなわち、実行コード30が実行されるコントローラ300の起動時に、その実行コード30を生成した中間表現20に関連付けて発行された証明書40に基づいて、その実行コード30の正当性を評価するようにしてもよい。コントローラ300の起動時に、アプリケーションプログラムの実行コード30が認証されていることを確認することで、コントローラ300などで好ましくない処理が実行される事態を回避できる。
<F.変形例>
本実施の形態に従うソフトウェア開発システム1における検証については、検証サーバ200で実行するようにしてもよいし、ソフトウェア開発装置100において実行するようにしてもよい。検証サーバ200で実行する場合には、クラウドコンピューティングの形態で実装してもよい。さらに、ソフトウェア開発装置100において検証を実行する場合には、外部の認証機関により認証されたルールを用いるようにしてもよい。
ソフトウェア開発装置100から検証サーバ200へ中間表現20の検証を依頼する際には、対象の中間表現20を暗号化してもよい。これによって、ソフトウェア開発装置100と検証サーバ200との間でやり取りされる中間表現20に対する改ざんなどを防止できる。
検証サーバ200が発行する証明書40には、認証局からの署名などを付与するようにしてもよい。これによって、検証サーバ200が発行する証明書40に対する改ざんなどの不正行為を防止できる。
上述の説明においては、ソフトウェア開発装置100がソースコード10の生成処理、中間表現20の生成処理および実行コード30の生成処理を実行する構成例について説明した、これらの処理を複数のコンピューティングデバイスで分散的に実行するようにしてもよい。
本実施の形態に従う中間表現20に対する検証および検証結果を示す証明書40の発行というフレームワークは、アプリケーションを販売あるいは配布する仕組みにも応用可能である。例えば、アプリケーションの開発者がアプリケーションプログラム(実行コード30)をアプリケーション配布サーバにアップロードする際には、対応する中間表現20および/または証明書40を併せてアップロードすることを要求することで、アプリケーション配布サーバ側において、配布予定の実行コード30を直接的または間接的に検証できる。このように、本実施の形態に従う仕組みを導入することで、バックドアなどが仕組まれたアプリケーションプログラムが配布されるような事態を回避できる。
このように、中間表現20が脆弱性に関する所定の規則あるいは規約を満たしていることを条件として、中間表現20から生成された実行コード30の実行を許可する処理を採用してもよい。
さらに、証明書40を利用して脆弱性に関する保険の仕組みを構成してもよい。具体的には、実行コード30がインストールされる任意のデバイスの製造者は、検証エンジン212(検証サーバ200)の利用に際して、所定の対価を支払う。検証エンジン212の運営者は、デバイスの製造者から得た対価の一部を保険会社に支払い、あるいは自身の保険金基金に組み入れる。
任意のデバイスで実行される実行コード30は、検証エンジン212(検証サーバ200)により提供される証明書40の存在を条件として実行される。万が一、証明書40の存在を条件として実行されている状態で、何らかのインシデントあるいはセキュリティ事象により、損害が発生した場合には、保険会社あるいは保険金基金から、その損害を補填するための保険金が支払われるようにしてもよい。
このような検証エンジン212(検証サーバ200)が生成する証明書40を利用して、脆弱性を管理するとともに、万が一の場合には、補償される仕組みを実現することで、検証エンジン212の利用を促進する動機付けを与えることができる。
<G.利点>
本実施の形態においては、ソースコード10ではなく中間表現20に対する検証を行うことで、ソースコード10に含まれるノウハウなどを秘匿化した状態で、アプリケーションプログラムに含まれる脆弱性をより効率的に発見できる。
また、中間表現20を検証することで、ソースコード10の言語およびターゲットアーキテクチャなどに依存することなく、汎用的な検証が可能となる。
今回開示された実施の形態はすべての点で例示であって制限的なものでないと考えられるべきである。本発明の範囲は上記した説明ではなくて請求の範囲によって示され、請求の範囲と均等の意味および範囲内でのすべての変更が含まれることが意図される。