セキュアなアプリケーションのアーキテクチャ – sandbox化

(Last Updated On: 2018年8月4日)

セキュアなアプリケーションのアーキテクチャの基本は既に書きました。基本は既に書いた通りですが、多少異るアプローチもあります。今回はそれを解説します。タイトルに含まれているsandbox化がキーワードです。

セキュアなソフトウェアの歴史はsandbox化と言える

ChromeやAndroid※のアプリケーションが比較的安全である理由はソフトウェアアーキテクチャとしてsandbox化を行っているからです。

コンピュータセキュリティ技術において、 サンドボックス(sandbox)は、外部から受け取ったプログラムを保護された領域で動作させることによってシステムが不正に操作されるのを防ぐセキュリティ機構のことをいう [1]

実行されるプログラムは保護された領域に入り、ほかのプログラムやデータなどを操作できない状態にされて動作するため、プログラムが暴走したりウイルスを動作させようとしてもシステムに影響が及ばないようになっている。

ウェブページに配置されたJavaアプレットFlashJavaScriptなどのプログラムは自動的に実行される。そのため、気づかないうちにコンピュータ上にあるファイルを盗み見られたり書き換えられたり、あるいはコンピュータウイルスに感染させられたりするおそれがある。そこで安心してウェブサーフィンを楽しめるように提供されたのが、そういった攻撃のできない安全な「砂場」(サンドボックス)である。

sandbox化の歴史を簡単かつざっくりと振り返ります。

※ ChromeはEMETが提供するようなsandbox環境を独自に作っています。Androidは許可された操作しかできないようなsandbox環境を提供しています。

 

言語のsandbox化 – アセンブラから高級言語

初期のコンピュータ開発ではアセンブラさえ無く、マシン語で直接プログラムを記述していました。マシン語の直接記述からアセンブラ(概ねマシン語と一対一で対応。今時のアセンブラは全てマクロアセンブラでありマシン語の命令と一対一で対応する物ではありませんが)になっても、コンピュータのハードウェアを直接操作できます。

アセンブラを使うと何でもできます。CPU上のプログラムカウンタと呼ばれるレジスターを操作して、メモリ中のどの部分でも”命令”として実行できます。ちょっとしたメモリ操作の間違いでデータであるべきメモリ領域を”命令”として実行できます。いわゆる任意コード実行という脆弱性が簡単に作れてしまいます。

これではプログラムは作りづらいので、データ型やプロシージャー/関数をサポートし、人間が解りやすい記述方法でプログラミングできるコンパイラ言語が作られました。FORTRAN、COBOL、Cなどが高級言語として開発されます。Cはメモリを直接操作でき、実行効率も良いでハードウェアを直接取り扱うOSなどのプログラムでは現在でもCで記述することが普通です。

これらの高級言語はデータ型とプロシージャー/関数を言語として定義することにより、簡単にデータが命令として実行される、という問題を不完全ですがsandbox化しています。プロシージャー/関数でプログラムカウンターを保護し、変数は文字列型/整数型/浮動小数点型/構造型などデータ型を使ってデータを保護できるようになりました。

Cにおけるsandbox化は非常に脆弱でした。しかし、アセンブラに比べれば遥かに進んだsandbox化が行われていると言えます。実際、アセンブラに比べれば遥かに簡単に安全なプログラムを早く作ることができます。

OSのsandbox化 – 特権モードとメモリプロテクション

初期のOSにはメモリプロテクションはありませんでした。このためユーザープログラムはカーネル(OS)自体のコード(命令)さえ簡単に書き換えれました。セキュリティなど全くない時代です。

CPUの発達で特権モード、メモリプロテクション(MMUなど)が利用できるようになります。OSのコードとデータ、ユーザープログラムのコードとデータが分離できるようになります。ユーザープログラムはOSのコードとデータを直接変更することが出来ないsandbox化された環境で実行されます。

現在のCPUではハードウェアによるsandbox化が更に進みデータ実行防止(DEP)によりメモリ中のデータ領域が命令として実行されないようにする仕組みもあります。

言語のsandbox化 – メモリ操作の禁止

アセンブラに比べればC言語はsandbox化された環境といえますが、メモリ操作が行えるため本来はデータであるべきメモリ領域が命令として実行できてしまいます。このためメモリ操作が直接行えないVM(仮想マシン)型言語が現れます。Javaはその1つと言えます。スクリプト系言語と言われる言語(PHP, Ruby, Python, Perlなど)も現在はVM型言語になっています。

C言語のsandbox化は非常に脆弱であった、と言うより直接メモリ操作を許可していたため、簡単に不正な命令が実行されるバグが作れてしまいました。これでは危なくて使えない、ということでVM型言語がアプリケーション開発用言語の主力になりました。

アプリケーション開発のsandbox化 – フレームワーク

VM型言語がアプリケーション開発の主力となったため、メモリ操作による任意コード実行は困難になりました。代わりにテキストインターフェースを持つ処理系への攻撃が主力になりました。JavaScriptインジェクション、SQLインジェクション、XPathインジェクション、LDAPインジェクション、OSコマンドインジェクションなど、可変長テキストで命令とデータを表現する処理系に不正な命令を紛れ込ませ攻撃する手法が広く利用されています。

これらに対応するため、アプリケーション開発用のフレームワークが利用されています。攻撃が行われるテキスト処理をできる限り安全に実行できるよう、フレームワークが利用されています。しかし、現在のフレームワークは

  • アセンブラからC言語に変えた程度

の安全性しか提供しません。JavaScript、SQL、XPath、LDAP、OSコマンドなど従来のテキストインターフェースを用いた利用を”禁止”できないので当然です。汎用プログラミング言語を用いている限り、これらを禁止することはできません。C言語からVM型言語に変えるような変更が必要です。

フレームワークの仕組みに則って、フレームワークが提供したsandbox上でのみコードを書けばそれなりに安全なコードが書けます。かなり良い線を行っているのはRailsです。しかし、かなり良い線とはいえ「アセンブラからC言語に変えた程度」のsandbox化なので、セキュリティ知識が無いとヘルパー、モジュールやライブラリを書き始めると直ぐに問題が発生します。APIにも危険な部分があるので盲信すると問題になります。

 

アプリケーション開発者が認識すべき事実

現在のフレームワークは非常に脆弱なsandbox化しか行えていないことを認識しなければなりません。C言語のプログラマーに「メモリ管理をしないコードだけ書け」と指示することは可能ですが、言語としてサポートしている機能を完全に禁止することは不可能です。どうしてもメモリの操作が必要な場合もあります。

アプリケーション開発でも同じです。「フレームワークが提供するsandbox上だけでアプリケーションを作れ」と指示することは可能ですが、現実的にはそれだけでは開発できません。フレームワークに機能を追加したり、フレームワークが提供しないモノを実装しなければならない場合があります。そして、危険な操作も自由です。

C言語が自由にメモリ操作できるように、VM型言語はテキストを自由に操作できます。

不完全なsandbox環境がフレームワーク環境である、と認識すべきです。

 

アプリケーション開発者が実践すべきプラクティス

安全なアプリケーション開発をフレームワークだけに頼ることはできません。しかし、sandbox化がセキュリティ向上に大きく役立つことは間違いありません。sandbox化が足りていない部分を確実に把握する必要があります。

また、自分が書くコードをできるだけsandbox化することも重要です。自分が書くコードをsandbox化する指針としてCERT Top 10 Secure Coding Practicesは良い指針となります。自分が書くコードで最も重要なsandbox化を行う部分は入力と出力です。入力を確実に制御することにより、自分が書くアプリケーションロジックが有害な入力によって誤作動する可能性を低くできます。出力を確実に制御することにより、出力先のシステムが有害な入力により誤作動する可能性を低くできます。

参考

 

まとめ

Sandbox化は不完全であったとしても強力なセキュリティ対策になります。アセンブラとC言語、フレームワークの有り無し、どちらでプログラムを作った方が安全かは一目瞭然だと思います。フレームワーク開発者(改造者含む)やAPI開発者はできる限り、それを利用する利用者(開発者)のコードがsandbox上で動くようにすると利用者が安全なコードが書き易くなります。Sandboxとして不完全である部分は明記し、利用者が間違えないようにドキュメントを整備することも重要です。

フレームワークやAPIを利用する開発者はsandboxが不完全(例:ActiveRecordのSQLインジェクションパターン)であることを良く理解しなければなりません。制限や仕様をよく知りフレームワークとAPIを利用しなければなりません。入力元、出力先の仕様を正確に理解し排除可能なリスクは徹底的に排除します。このようにして、できる限り自分のコードがsandbox化された(ような)環境で動作するようにすべきです。

C言語の開発者が必ずメモリ管理を習得するように、フレームワークを利用してアプリケーションを開発する開発者も、(上を目指すなら)テキストインターフェースを自在に扱えるように習得すべきです。少なくとも当分の間、命令とデータが混在するテキストインターフェースを持つシステムが無くなることは考えられません。

参考

 

投稿者: yohgaki