早め早めの脆弱性対策! 開発チームでできるアプリとサーバのセキュリティ診断と要件定義の作り方
Webセキュリティ対策はなにかと面倒ですが、昨今はフレームワークが脆弱性に対応するなど、プログラミングは効率的になっています。その上でサービス全体の安全のため、開発チームがすぐ実施できるWebセキュリティ診断と要件定義について解説します。
こんにちは、松本(@ym405nm)です。
みなさんは業務やコミュニティ、趣味などでWebサイト作ってますか? SEO対策、ユーザビリティ、レスポンジブル、オートスケールなどなど、Webサイトを1つ作るだけでもさまざまな技術や考え方が必要であり、非常に奥深いものであるということは、このエンジニアHubの記事の多さが物語っているのではないでしょうか。
その中でもWebサイト開発者・運用者を悩ませるのは、Webセキュリティです。この記事では、開発フェーズから試すことができるセキュリティ診断の手法と、要件定義フェーズでやっておくべきセキュリティ対策について解説します。
- Webセキュリティに診断と要件定義がなぜ必要か
- 事例からみる脆弱性: SQLインジェクションとShellShock
- OWASP ZAPによるWebアプリケーションの脆弱性診断
- Vulsによるサーバの脆弱性検知
- セキュリティ要件定義と開発段階からのセキュリティ対策
- 終わりに
Webセキュリティに診断と要件定義がなぜ必要か
Webアプリケーションでは、動的な機能を作り込む際に脆弱性が作り込まれる可能性もあります。また昨今では、VPSやクラウドサービスなどを使用するケースもあり、ミドルウェアから自分でインストールする機会が増えました。インストールするのはよいのですが、インストールしたっきりになり、アップデートされてないまま放置され、既知の脆弱性も対策できていないまま運用されているサイトも見られます。
一般的に、Webサイトのセキュリティがどうなっているかを確認するには、セキュリティ診断(脆弱性診断)というサービスを利用します。これは診断員が攻撃者目線で疑似攻撃を行い、Webサイトに脆弱性がないかどうかを確認するサービスです。誤解を恐れずに言うと、悪いハッカーが脆弱性を見つける前に、良いハッカーが脆弱性を見つけて報告するイメージです。
通常はセキュリティ診断を提供している会社に依頼するのですが、皆さんも依頼された経験はあるでしょうか。正直なところ、見積もりを見て「高っ!」と思われた方も多いのではないでしょうか。
また、セキュリティ診断は、診断してもらって終わりではありません。帰るまでが遠足であるのと同様に、開発チームにとっては脆弱性を修正するまでがセキュリティ診断だとも考えられます。私にもセキュリティを診断した経験はいくつかあるのですが、中には大量の脆弱性が見つかったり、根本的な修正を必要とする脆弱性があったりします。その場合も、セキュリティ診断を依頼されている以上、心を鬼にして報告します。
そのような脆弱性を修正するには、追加の開発が必要になるなど、予期しないコストの増大や、スケジュールの遅れが発生します。そこでお勧めしているのは、セキュリティ対策をリリース直前にやるだけではなく、プロセスの前に戻って、要件定義のフェーズや、開発のフェーズで行うことです。
注意しておくべき点として、事前に要件定義や自己診断を行っていても、従来と同じように、専門家によるセキュリティ対策は必要です。第三者の目線で脆弱性がないかを確認し、網羅性をもった調査を実施するためです。
ただし、ここで解説するようにセキュリティの要件定義を作っておき、早い段階から自身でセキュリティ診断を行うことにより、前述したような診断結果から開発コストが増大したりスケジュールが遅れたりする可能性は大幅に下がり、健全的なプロジェクト運営が可能になります。
事例からみる脆弱性: SQLインジェクションとShellShock
この記事では、実際の脆弱性に対してセキュリティ診断を行います。ここで取り上げるいくつかの脆弱性について、事例をもとに紹介しておきます。
Webアプリケーションに埋め込まれる脆弱性の中でも、個人情報の漏えいなどを引き起こしやすいのが、SQLインジェクションです。
例えば、2019年1月にはある放送局で個人情報が約6万件漏えいしたと発表されました。この発表によると、2018年10月ごろからSQLインジェクションの攻撃が行われ、データベース内の情報を不正に取得されたとのことです。
不正アクセスによるお客様情報流出に関するお詫びとご報告| ニュースリリース
SQLインジェクションの対策が争点となった裁判事例
2014年には、SQLインジェクションによる情報漏えいで、興味深い判例がありました。
これはX社がY社に対して約900万円のWebサイトの発注したのですが、このサイトにSQLインジェクションの脆弱性があり、個人情報が漏えいしました。この件について、X社がY社に対して約1億1000万円の支払いを求めました。
私はこの話を聞いて、Y社に責任はあるものの、X社とY社は事前に契約をしており、損害賠償の制限についても何らかの契約を取り交わしているはずで、発注額の900万程度になるものかなと、なんとなく思っていました。しかしながら、判決は概ね原告の主張が認められ、発注額を大幅に超える2,262万円の損害賠償金支払いが命じられました。
ここで注目すべき点はいろいろあると思いますが、2点ご紹介します。
まずは、SQLインジェクションの対策を行う義務があったかどうかについて。裁判所は、独立行政法人情報処理推進機構(IPA)が当時公開していた文書を例に出しており、この件を当時の技術水準とし、その対策を行うことは黙示的に合意されていたとしました。つまり、裁判では、SQLインジェクション対策をするのは技術者として当然であると考えられるのです。
また、Y社の業務内容や前述の技術水準を鑑みると、本件はY社の重過失と認められました。このため、契約書に責任限定条項があったとしても、重過失により適用されないとされ、高額な損害賠償が認められました。
皆さんもWebサービスの開発を受注することがあるかもしれませんが、このように必要なセキュリティ対策を行っていないと、同様に高額の損害賠償を支払うことになるかもしれません。
私は法律の専門家ではありませんので、法律や本判決への解釈は個人的なものであり、正確ではない可能性があります。詳しくは、弁護士の伊藤雅浩さんによるブログ記事を参照してください。
クレジットカード情報の漏えい事故の責任 東京地判平26.1.23判時2221-71 - IT・システム判例メモ
サーバにおけるShellShock脆弱性
脆弱性は、アプリケーションだけではなくサーバに存在することもあります。
ShellShockという脆弱性をご存じでしょうか。これは2014年9月に、Unix/Linux系のデストリビューションでよく使用されるBashに発見された脆弱性です。
GNU bash の脆弱性に関する注意喚起 - JPCERT/CC
この脆弱性では任意のコードが実行され、かつ攻撃が容易であり、リモートから攻撃できることから、深刻な脆弱性であると考えられ「ShellShock」と名付けられました。
この記事でも、このShellShockをもとにサーバの脆弱性診断を行います。
Struts2の修正が間に合わなかった攻撃事例
もうひとつ、サーバにおける脆弱例の事例を紹介しておきます。
Struts2は、Javaで開発されているオープンソースのWebアプリケーション用のフレームワークです。2017年3月に、深刻な脆弱性が発見されました。
Apache Struts 2 の脆弱性 (S2-045) に関する注意喚起 - JPCERT/CC
この脆弱性では、攻撃者がリモートからHTTPリクエストを送るだけで、サーバで任意コードが実行される可能性がありました。Struts2は国内でも多くのWebサイトが使用しており、大規模サイトでも運用されています。
この脆弱性を悪用されて、国内のある決済代行サービスが攻撃をうけ、クレジットカード情報が漏えいしました。被害を受けたサービスの運営会社が発表した報告書によると、脆弱性を長期間放置していたわけではなく、修正が間に合わなかったとのことです。
報告書には、脆弱性が公開された約17時間後に攻撃コードが公開され、その約13時間後に当該システムに攻撃が到達したとのことです。サービスの特性上、1~2日間で修正を適用することは現実的に難しく、攻撃を受けしまったことが分かりました。
OWASP ZAPによるWebアプリケーションの脆弱性診断
ここからは、実際に脆弱性を診断してみましょう。
OWASP ZAP(Zed Attack Proxy)は、OWASP(Open Web Application Security Project )が管理している、オープンソースのプロキシ型の診断ツールです。
OWASP Zed Attack Proxy Project - OWASP
Webアプリケーションの脆弱性を確認する際には、プロキシ型の診断ツールがよく使用されます。プロキシ型というのは、下図のように動作します。
通常、Webサイトにアクセスする際は、上段のようにブラウザからサーバに対してリクエストを送り、サーバ側の処理を終わったあとにレスポンスを返します。
ここに、プロキシサーバのように診断ツールを挟みます(下段)。プロキシとしても動作しますので、リクエストとレスポンスを確認できますが、診断ツールではリクエストを変更でき、自由にリクエストを送る、またはリクエストパラメータをもとにスキャンできます。
それでは、OWASP ZAPをインストールしてみましょう。
Downloads・zaproxy/zaproxy Wiki
各OS環境のインストーラーが上記のページからダウンロードできます。また、実行にはJava環境(JRE)が必要です。
インストーラーを実行後、OWASP ZAPを起動して次の画面が表示されればOKです。
類似の商用製品としては、PortSwigger社が提供しているBurp Suiteなどがあります。Burp Suiteは、セキュリティ診断の現場でも使用される機会が多く、日本ではユーザグループがあり、イベントや議論が活発的に行われています。
サンプルとなるアプリケーションを用意する
続いて、脆弱性を診断するWebアプリケーションを用意しましょう。問題のないアプリケーションにSQLインジェクションを意図的に仕込んで、脆弱性の検証と対策を体験することにします。
フレームワークなどを使い始めるときに、チュートリアルとして簡単なアプリケーションを作ってみることがあります。今回は、PHPフレームワークのLaravel用のチュートリアルとして公開されている、次のTODOアプリを使用します1。
Laravel tutorial (MasahiroHarada/laravel-tutorial)
このアプリを使ったチュートリアルは下記にあります。
入門Laravelチュートリアル (1) イントロダクションと環境構築 | Hypertext Candy
注意! 誤解がないよう重ねて説明すると、このアプリケーションに脆弱性があるわけではありません。後で解説しますが、このアプリではSQLインジェクション対策がされたORマッパーを適切に使用しています。
本記事では、これを意図して脆弱になるよう書き換えます。決して本記事のようなコードを書いてはいけません。
それでは環境を作ります。サクッとDockerで立ち上げましょう。LaravelのDocker環境は、こちらのものを使います。
Build laravel development environment with docker (ucan-lab/docker-laravel)
適当なディレクトリを作ります。
$ mkdir myapp $ cd myapp
Docker環境とTODOアプリの両方をクローンします。
$ git clone https://github.com/ucan-lab/docker-laravel.git $ git clone https://github.com/MasahiroHarada/laravel-tutorial.git
環境のソース部分docker-laravel/.env
を変更します。
COMPOSE_PROJECT_NAME=laravel-tutorial PROJECT_PATH=../laravel-tutorial
Laravelのenvファイルを作成します。
$ cd ./laravel-tutorial
$ cp .env.example .env
デバッグログがWebブラウザに表示されることにより診断ツールが誤検知するのを避けるため、envファイル内のAPP_DEBUG
をfalse
にします。
docker-compose
で立ち上げます。
$ cd ./docker-laravel $ docker-compose up -d
Laravel内のComposerの依存を追加して、インストールします。
$ docker-compose exec app composer require predis/predis
これでTODOアプリが立ち上がりましたが、まだデータが何もない状態なので、次のユーザアカウント2つを登録して、アプリ内のタスクを管理するフォルダを作成します。
ユーザ名 | メールアドレス | パスワード | 作成するフォルダ |
---|---|---|---|
UserA | userA@example.com | test1234 | a1 a2 |
UserB | userB@example.com | test1234 | b1 |
Webブラウザからアプリのフォームで登録することもできますが、今回はLaravelのシーダー2で設定します。データベースをマイグレーションして、シーダーファイルを作成します。
$ docker-compose exec app php artisan migrate
database/seeds
にあるシーダーファイルを、次のように書き換えます。
UserTableSeeder.php
public function run() { DB::table('users')->insert([ 'name' => 'UserA', 'email' => 'userA@example.com', 'password' => bcrypt('test1234'), 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); DB::table('users')->insert([ 'name' => 'UserB', 'email' => 'userB@example.com', 'password' => bcrypt('test1234'), 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); }
FolderTableSeeder.php
public function run() { DB::table('folders')->insert([ 'title' => "a1", 'user_id' => 1, # UserA 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); DB::table('folders')->insert([ 'title' => "a2", 'user_id' => 1, # UserA 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); DB::table('folders')->insert([ 'title' => "b1", 'user_id' => 2, # userB 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); }
DatabaseSeeder.php
public function run() { $this->call(UsersTableSeeder::class); $this->call(FoldersTableSeeder::class); }
これをデータベースに流し込みます。
$ docker-compose exec app php artisan db:seed
この状態で、Webブラウザからhttp://127.0.0.1:10080/
にアクセスすると、次のログイン画面が表示されます。
userA@example.com
とtest1234
でログインしてみましょう。
図のように画面左に「a1」「a2」のフォルダが登録されており、先ほどのデータが反映されていることが分かります。「b1」に関しては別ユーザ(UserB)が登録していることになっているので、ここでは表示されません。
Webブラウザでプロキシを設定する
次に、Webブラウザでプロキシを設定します。これは前述のように、ブラウザからアプリへのリクエストをZAPに送るため必要です。ここでは、Firefoxを使って解説します。
Firefoxの設定画面で、一般(デフォルト)の一番下に「ネットワーク設定」があります。左の「接続設定...」ボタンを押してください。
プロキシの設定画面が表示されるので「手動でプロキシーを設定する」をチェックし、HTTPプロキシーに127.0.0.1
、ポートに8080
を入力してください。すぐ下の「すべてのプロトコルでこのプロキシーを使用する」にもチェックを入れて「OK」を押します。
8080番は、OWASP ZAPがデフォルトで使用するポートです。これは、ZAPの設定画面から「ローカル・プロキシ」メニューで変更できます。
実際に診断を行う際には、プロキシを頻繁に切り替えることがあります。Firefoxには、数回のクリックでプロキシ設定を切り替えられるアドオンがあります。
FoxyProxy Standard ‐ Firefox向け拡張機能を入手
このアドオンでは、URLのルールによりプロキシ経由かどうかを自動的に振り分けることもできます。
プロキシが設定できたところで、あらためてWebブラウザから対象アプリ(http://127.0.0.1:10080/
)にアクセスしてみましょう。おそらく下図のような警告が表示されます。
これは、HTTPS接続に必要なサーバ証明書が自己証明書(オレオレ証明書と呼ぶ人もいます)だという警告です。ZAPのHUD(Heads Up Display)機能3がデフォルトで有効になっていて、これがHTTPSで通信するためです。
警告画面の「詳細情報...」ボタンをクリックして「危険性を承知で続行」を選択すると接続できますが、 今回はそもそもアプリがHTTPSではないので、この機能を無効にすれば警告を回避できます。
ZAPのオプションから「HUD」を選び、一番上の「Enable When using the ZAP Desktop」のチェックを外してください。
再度アプリにアクセスします。このときhttps://
ではなくhttp://
に戻ってることを確認してください。
SQLインジェクションの実装と実行
それでは、現状では安全なTODOアプリに、脆弱性をわざわざ仕込んでいきます。
app/HTTP/Controllers/TaskController.php
内のindex
アクションを、以下のように書き換えます。
public function index(Folder $folder, Request $request) { // ユーザーのフォルダを取得する $user_id = Auth::user()->id; $query_title = $request->input('q'); if($query_title){ $folders = DB::select('select * from folders where user_id = '. $user_id .' and title = "' . $query_title . '"; '); }else{ $folders = DB::select('select * from folders where user_id = '. $user_id .' ; '); } // 選ばれたフォルダに紐づくタスクを取得する $tasks = $folder->tasks()->get(); return view('tasks/index', [ 'folders' => $folders, 'current_folder_id' => $folder->id, 'tasks' => $tasks, ]); }
また、同ファイルの上部に以下を追記します。
use Illuminate\Support\Facades\DB;
書き方を変えましたが、内容は同じで、ログインしているユーザが作ったフォルダ一覧を取得しています。ここに機能を追加して、GETパラメータにq
があった場合、その値と完全一致したものだけ取得するようにしました。簡易フィルタリング機能のイメージです。
本来なら部分一致にしたりユーザ向けのフォームを作ったりすべきですが、脆弱性は無事に仕込めたので省略します。動作を確認します。
http://127.0.0.1:10080/folders/1/tasks?q=a1
上記のURLにアクセスしてください。「a1」フォルダだけがフィルタリングされているはずです。
ここで、パラメータのa1
を以下のように変更します。
http://127.0.0.1:10080/folders/1/tasks?q=a1" or 1=1; -- ;
すると、下図のようにフォルダが全て表示されます。
ここで注目したいのは、UserBが登録したはずのフォルダ「b1」も表示されていることです。
先ほど改変したソースコードで、以下の部分に注目してください。
$folders = DB::select('select * from folders where user_id = '. $user_id .' and title = "' . $query_title . '"; ');
$query_title
にはa1" or 1=1; -- ;
が入ると、結果としてSQLは以下のようになります。
select * from folders where user_id = 1 and title = "a1" or 1=1; -- ;";
a1
のあとに入力された"
がSQLの文字列を囲んでいるものと認識されてしまい、以降の文字列がSQLクエリとして評価されます。ここですかさずor 1=1;
と書いています。最後に以降のSQL文は消したいのでMySQLでコメントアウトを意味する--
で終えます。
こうすることにより、folders
テーブルの全てを表示するSQL文が発行されます。これがSQLインジェクションの仕組みです。
環境にもよりますが、場合によってはより危険なSQL文が攻撃者に発行され、データベース内の改ざんや消去などが行われる可能性があります。
SQLインジェクションの検証
それでは先ほど紹介したOWASP ZAPを使用して、脆弱性の検証を試してみましょう。
ZAPを起動します。SQLインジェクションのアドオンも入れておきましょう。
図のようにアドオン管理画面で、Advanced SQLInjection Scanner
がインストールされていることを確認してくささい。
この画面は、下図のボタンを押すと表示されます。
ZAPのプロキシが通ってる状態で、http://127.0.0.1:10080/folders/1/tasks?q=a1
にアクセスします。下部の履歴で、次のように表示されます。
この状態でアラートタブをみると、既にいくつか検知されています。これはPassive Scan(受動的スキャン)と呼ばれ、アクセスするだけで分かる情報が出ています。
次に、Active Scan(動的スキャン)を行います。
ZAPではデフォルトでは「プロテクトモード」の状態になっており、動的スキャンできないようになっています。右上のリストからアプリを右クリックして、コンテキストに追加します。これで攻撃対象になります。
追加するときには、URLを正規表現で指定できます。
右上のサイトリストが「的」のようなアイコンに変わってることが分かります。
再び右クリックして[攻撃]→[動的スキャン]を選択します。
ダイアログで「動的スキャン」の設定画面が表示されます。今回はデフォルトのまま[OK]を押して、しばらく待ちます。
アラートを見てみると「SQLインジェクション」が検知されていることが分かります。
今回は事前に脆弱性を仕込んでから試したので、うまく検知されていることが分かります。
ただし、本来は脆弱性があるかどうか分からない状況でスキャンしますから、アラート情報をヒントに手動で検証することになります。自動的なスキャンですので、どうしても「脆弱性があるのに見逃してしまった」あるいは「脆弱性がないのにアラートが出た」といった誤検知もあります。
SQLインジェクションの対策 ─ サンプルのアプリが安全な理由
この結果を見て「Laravelやべー」と思った方がいるかもしれませんが、もちろんそれは誤解です。
今回のアプリケーションで、改変する前のソースコードがどうだったかを確認してみましょう。
public function index(Folder $folder) { // ユーザーのフォルダを取得する $folders = Auth::user()->folders()->get(); //省略 }
ここでは、まずユーザ情報を取得して、ユーザモデルのfolders()
関数を呼び出しています。
モデル側ではこのように受け取っています。
public function folders()
{
return $this->hasMany('App\Folder');
}
このように取得することで、Laravelに実装されているORマッパーを使用できます。
LaravelのORマッパーはEloquentと呼ばれますが、このように主要なフレームワークで使用されるORマッパーでは、SQLインジェクション対策がされています。
一方で、私が改変したソースコードのように、SQL文を文字列連結で作ると、脆弱性が作り込まれることがあります。今回のパラメータでは"
をエスケープすることで攻撃を防ぐことはできますが、エスケープでは対策漏れの可能性もあります。
データの取得などを独自実装することは避け、必ずフレームワークが推奨する方法を使用しましょう。
Vulsによるサーバの脆弱性検知
ここまで、Webアプリケーションの脆弱性にフォーカスを当ててきましたが、アプリを運用するサーバに脆弱性がある場合もあります。
特に近年では、VPSやクラウド環境を使用することが多くなり、自身でミドルウェアなどを選択してインストールする機会がよくあります。一度インストールしたまま放置すると、脆弱性を持ったまま運用される可能性があります。
管理しているサーバが1つなら運用は容易かもしれませんが、Webサイトの規模が大きくになると管理するサーバも増加し、全てを管理することは難しくなります。
今回は、Vulsというオープンソースの脆弱性検知ツールを使います。Vulsでは、脆弱性関連情報の収集と検知を自動化できます。
Vuls・Agentless Vulnerability Scanner for Linux/FreeBSD
今回検証する構成は、次の図のようになります。検証の対象となるサーバはCentOS 6系で運用されており、ShellShockの脆弱性があるという想定です。
Vulsのインストール
Vulsをインストールしますが、今回はDockerで簡単にセットアップできる、Vulsctl
を使用します。
適当な場所でリポジトリをクローンします。なお、事前にDockerが動作していることを前提としています。
$ git clone https://github.com/vulsio/vulsctl.git
$ cd vulsctl
脆弱性のデータベースをアップデートします。初回は量が多いので時間がかかります。
$ ./update-all.sh
ディレクトリ内に、config.toml.tempalte
があります。このファイルをテンプレートとして、設定ファイルconfig.toml
ファイルに、次のように記載します。
[servers] [servers.hostos] host = "(対象サーバのIPアドレス)" port = "22" user = "centos"
Vulsは対象サーバに公開鍵認証を行いますが、今回はデフォルトの秘密鍵を使うので記載はしていません。
続いて対象サーバにログインします。CentOS 6系でVulsのスキャンを行うにはyum-utils
が必要ですので、インストールします。
$ sudo yum update $ sudo yum install yum-utils
ここまで準備できたら、対象サーバをいったん再起動します。
安全な環境でVulsを実行する
Vulsでスキャンを開始します。
$ ./start.sh
スキャンの進捗が表示されますが、ここでは詳細な結果は表示されません。スキャン完了後、レポートを以下のコマンドで確認します。
$ ./report.sh
今回の結果は以下のようになりました。
hostos (centos6.10) =================== Total: 0 (High:0 Medium:0 Low:0 ?:0), 0/0 Fixed, 248 installed, 0 updatable, 0 exploits, en: 0, ja: 0 alerts No CVE-IDs are found in updatable packages. 248 installed, 0 updatable
Total:0
とのことで、脆弱性は発見されませんでした。
ShellShockの脆弱性を作り込む
対象サーバにShellShockの脆弱性をインストールします。この操作はサーバに脆弱性を埋め込むことになるため、外部から攻撃される可能性があります。アクセス制限をしっかりと行ってください。
現在インストールされているBashのバージョンは以下の通りです。
[centos@host ~]$ bash --version GNU bash, version 4.1.2(2)-release (x86_64-redhat-linux-gnu) ...略...
Bashはyumでパッケージ管理されているため、yumを使用して、脆弱性があるバージョンにダウングレードできます。
$ sudo yum downgrade http://vault.centos.org/6.4/os/x86_64/Packages/bash-4.1.2-14.el6.x86_64.rpm
先ほどと同じコマンドの結果は以下の通りで、ダウングレードされていることが分かります。
[centos@host ~]$ bash --version GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu) ...略...
ShellShock(CVE-2014-6271)を試してみる - けけずんセルフハッキング
危険な環境でVulsを実行する
Vulsの環境に戻り再びスキャンを行います。レポートをみると以下のものが追加されています。
hostos (centos6.10) =================== Total: 7 (High:5 Medium:1 Low:1 ?:0), 7/7 Fixed, 248 installed, 1 updatable, 2 exploits, en: 4, ja: 0 alerts +---------------+------+--------+-----+--------+---------+------------------------------------------------+ | CVE-ID | CVSS | ATTACK | POC | CERT | FIXED | NVD | +---------------+------+--------+-----+--------+---------+------------------------------------------------+ | CVE-2014-6271 | 10.0 | AV:N | POC | USCERT | fixed | https://nvd.nist.gov/vuln/detail/CVE-2014-6271 | | CVE-2014-7169 | 10.0 | AV:N | POC | USCERT | fixed | https://nvd.nist.gov/vuln/detail/CVE-2014-7169 | ...略...
以上のことから、仕込んだ脆弱性がVulsにより検知されていることが分かります。2行あるうち上のCVE-2014-6271
がShellShockを指します。他にも関連する脆弱性があることが分かります。
CVE(Common Vulnerabilities and Exposures)は、共通脆弱性識別子と呼ばれ、脆弱性を一意に特定するものです。一方で、ShellShockは脆弱性の通称です。ShellShockの中に脆弱性の種類がいくつかあるため、複数のCVEが割り当てられています。
ShellShockと、該当するCVEに関しては、以下のページをご確認ください。
■ JVNVU#97219505: GNU Bash に OS コマンドインジェクションの脆弱性 - JVN
脆弱性に対策するため日常的に行っておくべきこと
最初にVulsをスキャンした際には、脆弱性は発見されていません。これは、準備段階でyum update
コマンドを行い、パッケージを全て最新のものにあげていたからです。
Unix/Linuxではパッケージ管理ツールにより、さまざまなソフトウェアが簡単にインストールできます。Centos/Redhad環境の多くではyum
コマンド、Ubuntu/Debian環境ではapt
コマンドが、パッケージ管理に使われています。
アップデートも同様で、日頃からこのようなコマンドを使用してアップデートすることで、簡単に最新の状態を保つことができます。
脆弱性情報は、ほぼ毎日のように公開されています。脆弱性はピンキリで、深刻度の低いものから高いものまでさまざまです。
注意すべきこととして、深刻な脆弱性には緊急を要するものが多く、ある日突然に公開されます。私の経験上、時差の関係で夜中に発表されることが多く、朝起きる頃には広域的なスキャンや攻撃が始まっているものもありました。
脆弱性情報の収集は、本格的にやろうとすると、それだけで1日かかってしまうことがあります。今回は、いくつかお勧めのWebサイトをご紹介します。
まず、先ほども紹介したJVN(Japan Vulneravility Notes)です。JPCERT/CC(Japan Computer Emergency Response Team Coordination Center)と、IPA(情報処理推進機構)が共同で運営している脆弱性情報データベースです。
■ JVN (Japan Vulneravility Notes)
国内で多く流通している製品の脆弱性情報が記載されており、脆弱性への対策やベンダーごとの対象製品一覧といった情報もあります。JVN-VUとして、海外のCERT/CCが公開した脆弱性情報の翻訳版も掲載されています。
話題となった脆弱性が実際にどこまで攻撃されているかについては、警察庁が運営する@police内にある「トピックス」で見ることができます。
この2つに限ってもまだ量が多く感じられる方には、JPCERT/CCのWebサイトにある「注意喚起」というページがお勧めです。ここでは、明らかになった脆弱性の中から、国内のシステムに大きな影響を及ぼす恐れのあるものだけを紹介しています。
脆弱性全体では年間で1万を超える件数がありますが、ここでは例年50件程度が紹介されています。つまり、専門家による選りすぐりの脆弱性が並んでおり、ここに載ったものは対策に緊急を要するものだと思って間違いないでしょう。
また、今回紹介したVulsでは、最新の脆弱性情報をコマンド1つで複数の箇所から収集でき、その情報を元にスキャンできます。Vuls自体はCUIのツールですので、CircleCIなどのCIツールと連携することで、日々のスキャンを全自動化することもできます。
セキュリティ対策を「面倒くさい」と思いがちだった方でも、このようなツールを活用することで、Webサイトのセキュリティレベルを高く保つことが可能になります。
セキュリティ要件定義と開発段階からのセキュリティ対策
OWASPでは、ZAPの利用事例として、次のように開発時以外も想定しています。
このようにさまざまなフェーズでセキュリティ診断の技術を活用することが、効率的にセキュリティレベルを上げる鍵になります。
要件定義からのセキュリティ
実際の開発の現場では、規模によってはメンバーが増えたり、別の会社やパートナーに開発を委託することがよくあります。開発者のバックグラウンドはそれぞれ異なるため、開発プロセスの中でセキュリティレベルを統一することは簡単ではありません。
開発する案件について「こういう機能を作ってください」とか「こういう処理をお願いします」といった依頼はしますが、セキュリティについてどう依頼するかは、非常に悩ましいところです。
仮に「セキュリティにも気をつけてください」と依頼したとして、開発する際に「SQLインジェクションの対策を行う」と考える人もいれば、「サイト全体にSSL/TLSを適用する」と思う人もいます。これを見誤ってしまうと、工数の見積もりにも大幅なズレが生じます。
もっと細かい話をすると「SQLインジェクションの対策を行う」方法についても、人によって異なってくるのではないでしょうか。
そこで必要になってくるのは、セキュリティ要件定義です。
どのような開発手法でも、開発要件をメンバー内で共有することは必要です。セキュリティ要件定義では、開発の要件定義とは異なり、セキュリティ対策やセキュリティに特化した要件です。
セキュリティ要件定義を一度作成しておけば、企業やチームとしてのセキュリティ要件を固めることができます。理想的には、セキュリティ要件は汎用的なものとして、システム要件に依存せず、いつでも使える定義を作っておくことです。
セキュリティ要件定義を作るには
とはいえ、いきなり要件定義を作るのは厳しいと思われる方が多いのではないでしょうか? そういう場合は「Webシステム/Webアプリケーションセキュリティ要件書 3.0」を活用することをお勧めします。
これは、特定非営利活動法人日本ネットワークセキュリティ協会の日本セキュリティオペレーション事業者協議会のセキュリティオペレーションガイドラインWG(WG1)と、OWASP Japan主催の共同ワーキンググループのメンバーによって作成されました。
Pentester Skillmap Project JP - OWASP
2019年1月にバージョン3としてアップデートされ、チェックリストも公開されています。こちらから無料でダウンロードできます。
Webシステム/Webアプリケーションセキュリティ要件書 (ueno1000/secreq)
この要件定義書を、自身の会社やチームにブレイクダウンしていくことで、前述したような開発メンバーとのセキュリティレベル合わせを行うこともでき、よりセキュリティレベルの高いシステムを開発できるようになります。
極論を言うと「セキュリティ対策をしっかりしてください」を 「セキュリティレベルはこのドキュメントに準じてください」と言うことで、ざっくりとしたセキュリティ対策が明確になります。
Webシステム/Webアプリケーションセキュリティ要件書を読む
それでは「Webシステム/Webアプリケーションセキュリティ要件書 3.0」を読んでみましょう。冒頭にあるように、以下を目的として作られています。
- 開発会社・開発者に安全なWebシステム/Webアプリケーションを開発してもらうこと
- 開発会社と発注者の瑕疵担保契約の責任分界点を明確にすること
- 要求仕様やRFP(提案依頼書)として利用し、要件定義書に組み込むことができるセキュリティ要件として 活用していただくこと
構成としては、次の表のような内容が掲載されています。
題目 | 内容 |
---|---|
認証・認可 | ログイン画面(認証)や、データのアクセス制限(認可)などを解説する |
セッション管理 | サーバから発行されるセッションIDについて解説する |
パラメーター | ユーザが入力した値の扱い方について解説する |
出力処理 | 攻撃者のスクリプト実行や、SQLインジェクション対策について解説する |
HTTPS | WebサイトのHTTPS化に関して解説する |
cookie | サイトから発行されるCookieの設定について解説する |
画面設計 | ブラウザのセキュリティ機能の扱い方について解説する |
その他 | 上記事項に分類されていないもの |
例えば「認証・認可」では、パスワードリセット機能について、以下のように解説しています。
- パスワードリセットを実行する際にはユーザー本人しか受け取れない連絡先(あらかじめ登録しているメールアドレス、電話番号など)に再設定方法を通知すること
- パスワードはユーザー自身に再設定させること
パスワードリセット機能を誤って実装してしまうと、他人のパスワードを変更でき、情報漏えいを引き起こしてしまう可能性があります。システムを設計する前に、このようにセキュリティ要件を決めておくことが重要です。
ドキュメントを見ると、項目によっては赤字で「*」マークが付いているものがあります。これは「必須事項ではないが、あると望ましい要件」を意味します。ご自身のサイトが必要としているセキュリティレベルを確認の上、この要件を必須とするかどうか検討することをお勧めします。
例えば、決済情報などを扱うのであれば「*」の項目も必要でしょうが、当該機能によってユーザビリティが著しく低下し、ユーザが離脱する恐れがあるならば、あえて実装しない選択肢もあります。実運用において、ときにはビジネス継続性に鑑みてリスクを容認することも必要で、これを文書化できることも、要件定義の利点になります。
チェックシートを活用しよう
続いて、付録のチェックシートを見てみます。
Excel版とPDFファイルが配布されています。PDF版を開くと、次のような画面が表示されます。
ここでは、先ほどの要件定義書がまとめられており、表の右側では必須項目かどうかも記載されています。
このシートで要件をチェックしながら、確認していくのがよいでしょう。
終わりに
この記事では、開発チームがすぐにできるセキュリティ診断の方法と、より前の段階である要件定義フェーズについて解説しました。
セキュリティ対策は面倒くさいと思われがちです。しかし昨今では、フレームワークが脆弱性に対応して原理的に発生しなくなっていたり、既存のオペレーションに適用しやすいツールが開発されたりと、非常に効率的になってきました。
苦しみながらセキュリティ対策をするのではなく、便利なツールやドキュメントを利用して、サービスのセキュリティレベルを効果的に上げる参考になれば幸いです。
付記・ZAPの証明書をブラウザにインポートする
本文の「Webブラウザでプロキシを設定する」セクションでは、HUD機能を無効にして証明書の警告を回避しました。しかし、これではHTTPSのサイトを診断しようとするときに同様の問題が発生します。
このため、ZAPで生成したルートCA証明書を、Webブラウザにインポートしておくことをお勧めします。ZAPの証明書は「設定」から「ダイナミックSSL証明書」を選んで保存ボタンを押すことで取得できます。
この証明書データをFirerfoxにインポートするには、設定画面で「プライバシーとセキュリティ」を選び、「証明書を表示」ボタンをクリックします。「証明書マネージャー」で下部の「読み込む...」ボタンをクリックし、先ほど保存したファイルを選択します。
これで、HTTPSのサイトを診断できるようになります。また、HTTPSで通信できるので、HUD機能も利用できます。
先ほど外した「Enable When using the ZAP Desktop」にチェックを入れて再度読み込むと、HUDが有効になり次の画面が表示されます。
HUDが有効な状態では、Webブラウザの画面上に次のようなボタンがオーバーラップで表示され、ZAPの各種機能を使用できます。
このとき、FoxyProxyなどのアドオンを入れてプロキシ設定をルール化している場合には、https://zap
宛のリクエストがZAP経由になるよう設定しておく必要があります。