小池啓仁 ヒロヒト応援ブログ By はてな

小池啓仁(コイケヒロヒト)の動画など。

小池啓仁 ヒロヒト応援ブログ By はてな

WEBプログラマー必見!WEB脆弱性基礎知識最速マスター

以下は、WEBプログラマー用のWEB脆弱性の基礎知識の一覧です。
WEBプログラマーの人はこれを読めばWEB脆弱性の基礎をマスターしてWEBプログラムを書くことができるようになっているかもです。
また、WEB脆弱性の簡易リファレンスとしても少し利用できるかもしれません。


WEBアプリケーションを開発するには、開発要件書やプログラム仕様書通りに開発すれば良いというわけにはいきません。
そう、WEB脆弱性を狙う悪意のユーザにも対処しないといけないのです。


今回、WEBアプリケーションを開発にあたってのWEB脆弱性を、以下の一覧にまとめてみました。
このまとめがWEBアプリケーション開発の参考になれば幸いです。

  • インジェクション
  • クロスサイト・スクリプティング
  • セッション・ハイジャック
  • アクセス制御や認可制御の欠落
  • ディレクトリ・トラバーサル(Directory Traversal)
  • CSRF(クロスサイト・リクエスト・フォージェリ)

尚、本コンテンツを作成するにあたって、以下のリンク先を大変参考にさせて頂きました。
下記のリンク先は、凄く勉強になりました。謹んで御礼申し上げます。

追記:

すでに、はてブで指摘されているように、本コンテンツは、参考リンク先で勉強したノートです。
したがって、本コンテンツより、参考リンク先で勉強することを強くお薦めします。

追記(5/23):

以下の暗号化の基礎知識もあわせてご利用ください。



■ インジェクション

◆ インジェクションとは?

インジェクション(injection)の意味は、内部に何かを注入することです。
そして、WEB脆弱性のインジェクションには、以下の3つがあります。

  1. SQLインジェクション
  2. OSコマンドインジェクション
  3. HTTPヘッダインジェクション

つまり、SQL、OSコマンド、HTTPヘッダの各々に、Webページから悪意のコマンドをインジェクションされることなのです。

SQLインジェクション

たとえば、以下のソースで、$uidと$passhをWebページから入力(POST)される以下のSQLがあったとします。

$query = "SELECT * FROM usr WHERE uid = '$uid' AND pass = '$passh'";

$uidに「taro'--」と入力されたら、以下のように展開されます。

$query = "SELECT * FROM usr WHERE uid = 'taro'--' AND pass = ''";

すると、SQLで「--」はコメントになりますので、パスワードがなくても以下のSQLが発行されてしまうのです。

$query = "SELECT * FROM usr WHERE uid = 'taro'";

これ、Delete文もインジェクションされるケースもあり、私は実際にテストで体験しております。DBがクリアされました!


・対策1
バインド機構を利用します。
バインド機構とは、実際の値がまだ割り当てられていない記号文字(プレースホルダ)を使用して、あらかじめSQL文の雛形を用意し、後に実際の値(バインド値)を割り当ててSQL文を完成させるようにします。


・対策2
対策1のバインド機構が利用できない場合は、入力されるパラメータや、データベースに格納される情報や、SQL文を構成する全ての変数や演算結果に対し、エスケープ処理を行います。
エスケープ処理の対象は、SQL文にとって特別な意味を持つ記号文字(たとえば、「'」→「''」、「\」→「\\」等 )が対象です。
尚、特別な意味を持つ記号文字は、利用しているデータベースエンジンによって異なるので、それぞれに応じて対策を行います。


・対策3
たとえば、データ検索のみのケースでは、削除や更新系のSQLが使えないようにDBの権限設定を行います。


・対策4
あとは、SQLのエラーメッセージをWeb画面に表示しないとか、hiddenパラメータにSQL文をそのまま指定しないとか、があります。

◆ OSコマンド・インジェクション

たとえば、以下のソースで、$fromをWebページから入力(POST)される以下のopen文があったとします。

$from =~ s/"|;|'|<|>|\|| //ig;
open(MAIL, "|/usr/sbin/sendmail -t -i -f $from");

$from に「`touch[0x09]/tmp/foo`」と入力されたら、以下のように展開されます。

$from =~ s/"|;|'|<|>|\|| //ig;
open(MAIL, "|/usr/sbin/sendmail -t -i -f `touch[0x09]/tmp/foo`);

すると、touchコマンドが実行されてしまいます。


・対策1
OSコマンドを起動できる言語機能の利用を避けます。
たとえば、Perlでは、open関数でなくsysopen関数を使用すれば、OSコマンドを起動することができません。


・対策2
対策1が利用できない場合は、引数に埋め込む前にチェックをかけ、本来想定する動作のみが実行されるようします。

◆ HTTPヘッダ・インジェクション

たとえば、以下のソースで、$numをWebページから入力(POST)される以下のHTTPのLocationがあったとします。

$cgi = new CGI;
$num = $cgi->param('num');
print "Location: http://example.jp/index.cgi?num=$num\n\n";

$num に「3%0D%0ASet-Cookie:SID=evil」と入力されたら、以下のようなクッキーが発行されてしまうのです。

Set-Cookie: SID=evil


・対策1
ヘッダの出力を直接行わず、Webアプリケーションの実行環境や言語に用意されているヘッダ出力用APIを使用します。


・対策2
対策1が利用できない場合は、ヘッダの出力時に想定外の改行を許可しないようにします。
これは、HTTPヘッダは改行によって区切られる構造となっているため、これを許すと、任意のヘッダフィールドや任意のボディを注入される原因となるからです。



■ クロスサイト・スクリプティング

◆ クロスサイト・スクリプティングとは

普通CGIなどWebアプリケーションは、Webページから検索のキーワード入力や個人情報の登録入力などを処理して、Webページとして出力します。
ここで、Webページへの出力処理に問題があると、そのWebページにスクリプトなどを埋め込まれてしまうことがあります。
このようなスクリプトのインジェクション(注入)を、特にクロスサイト・スクリプティング攻撃と言います。


対策としては、入力されたHTMLテキストを出力する際にエスケープ処理を行い、HTMLテキストを許可しない方法があります。
どうしても、HTMLテキストが必要な場合は、構文解析を行い、「ホワイトリスト方式」で許可する要素のみを抽出します。
また、共通の対策としては、HTTPレスポンスヘッダのContent-Typeフィールドに文字コード(charset)の指定を行います。

◆ 補足

・エスケープ処理
エスケープ処理には、Webページの表示に影響する特別な記号文字(「<」、「>」、「&」、「"」、「'」など)を、
HTMLエンティティ(「&lt;」、「&gt;」、「&amp;」、「&quot;」、「&#39;」など)に置換します。
また、URLを出力するときは、「http://」や 「https://」で始まるURLのみを許可するようにします。
これは、「javascript:」の形式で始まるURLがあるからです。


以下は、上記の置換をするPerlでのソースです(前に書いた記事の引用です)。

sub htmlf {
    my ($strhtml) = @_;
    $strhtml =~ s/&/&amp;/g;
    $strhtml =~ s/>/&gt;/g;
    $strhtml =~ s/</&lt;/g;
    $strhtml =~ s/"/&quot;/g;
    $strhtml =~ s/'/&#39;/g;
    return $strhtml;
}
http://d.hatena.ne.jp/chaichanPaPa/20080329/1206748795


ホワイトリスト方式
リストに登録された内容に一致する文字列の通過を「許可」する方式です。
未知の攻撃パターンにも有効であり、漏れが生じにくい点で安全性は高いです。
ちなみに、ブラックリスト方式は、ホワイトリスト方式の逆で、リストに登録された内容に一致する文字列の通過を「禁止」する方式です。
これは、未知の攻撃パターンを検出できない可能性があり、漏れが生じやすいという性質あります。


・HTTPレスポンスヘッダの文字コード指定
HTTPレスポンスヘッダに文字コードを指定していないと、ブラウザは、文字コードを推測して画面表示を処理します。
たとえば、UTF-7に推測された場合、上記のような「エスケープ処理」を施してもエスケープをスルーしてしまうケースがあるとのことでした(参考リンク参照)。



■ セッション・ハイジャック

◆ セッション・ハイジャックとは

HTTPは、ステートレスなので、普通は、ステートを引き継ぐセッション管理ができません。
しかし、ログインが必要なサイトでは、セッション管理がないと実現ができません。
そのために、クッキーが考案され、クッキーにステート(セッションID)を保存することにより、セッション管理を実現します。


しかし、セッション管理がクッキーにより実現できても、その不備を狙うWEB脆弱性があり、それをセッション・ハイジャック攻撃と言います。
セッション・ハイジャック攻撃には、以下の3通りがあります。

  1. セッションIDの推測
  2. セッションIDの盗用
  3. セッションIDの固定化(Session Fixation)
◆ セッションIDの推測

悪意のある人は、セッションIDの生成規則を割り出し、有効なセッションIDを推測します。
これにより、善意の利用者に成りすましすることができるのです。
対策:セッションIDは、生成アルゴリズムに安全な擬似乱数生成系を用いるなどして、予測困難なものにします。

◆ セッションIDの盗用

悪意のある人は、罠を仕掛けたり、ネットワークを盗聴したりし、利用者のセッションIDを盗みます。
これにより、善意の利用者に成りすましすることができるのです。
対策:セッションIDをURLパラメータに格納しないようにしたり、HTTPS通信で利用するCookieにはsecure属性を加えます。

◆ セッションIDの固定化(Session Fixation)

悪意のある人は、以下のような『何らかの方法』で、自分が取得したセッションIDを善意の利用者のセッションIDに送り込みます。
これにより、善意の利用者に成りすましすることができるのです。
対策:ログイン成功後に、新しくセッションを開始するようにします。


・『何らかの方法』の一例

  1. 悪意の利用者が、BBS掲示板等へ「セッションIDを送り込む細工をされたURL」を貼り付けます。
  2. 善意の利用者は、そのURLをクリックし、ログインします。
  3. 悪意の利用者は、送り込んだセッションIDを使って善意の利用者のページを閲覧できるようになります。


・細工されたURLの例

<a href="javascript:document.cookie='PHPSESSID=aaa;expires=Thu, 1-Jan-2011 00:00:00 GMT';location.href='login.php'">click me!</a>


・説明
悪意のある人が、予め用意したセッションID(例ではクッキーのPHPSESSID=aaa)をdocument.cookieで善意の利用者のに送り込み、ログインページに遷移します。
するとその後、悪意の利用者は、そのセッションIDでログインすることにより、善意の利用者に成りすましすることができるようになるのです。



■ アクセス制御や認可制御の欠落

◆ アクセス制御の欠落と対策

アクセス制御の欠落とは、ログインが必要なサイトでは、ログイン情報によりアクセス制御を掛けますが、たとえば、そのログイン情報がメールアドレスだった場合は、他人にも知られ得る情報であり、そのような情報の入力だけでログインできてしまうのは、アクセス制御機能が欠落していると言えます。

アクセス制御の対策は、他人にも知られ得る情報でなく、パスワードなどの入力を必要とするように、Webアプリケーションを設計する必要があります。

◆ 認可制御の欠落と対策

認可制御の欠落とは、上記の「アクセス制御の欠落」をなくして、ログインを実装しても、ログイン者が他のログイン者の情報(データベース)にアクセスできてしまうことがあります。このような実装は、認可制御機能が欠落していると言えます。

認可制御の対策は、ログイン者がアクセスできる情報(データベース)の範囲をつねに考慮するように、Webアプリケーションを設計する必要があります。



■ ディレクトリ・トラバーサル(Directory Traversal)

◆ ディレクトリ・トラバーサルとは?

「../../」のような相対パスを使用し、システム内の任意ファイルへアクセスする攻撃手法をディレクトリ・トラバーサルと言います。
システム内のディレクトリ間を自由に横断(トラバース)できることが攻撃名称の由来で、パス・トラバーサルとも呼ばれます。


対策は、CGIでは、GET(引数)とPOST(hidden)で、ファイル名のみで、ファイルパスを指定しないように設計します。
また、万が一、ファイル名にファイルパスを付加された場合を想定して、そのようなパスを削除するように設計します。

◆ ディレクトリ・トラバーサル対応のサンプルプログラム
#!c:/perl/bin/perl.exe
use strict;
use File::Basename;
use CGI;

my $form = new CGI;
my $file_name = $form->param('file_name');
local $/;

my $dir = 'c:/wk/';                # パス情報をプログラム内で持つ
$file_name = basename($file_name); # パスを指定されてもファイル名だけにする
                                   # openは、パス名とファイル名で連結する
open(my $fh, '<', $dir . $file_name) or die "$file_name, open err ($!)";

my $wk = <$fh>;
print <<"HERE1";
Content-type: text/plain; charset=Shift_JIS;

<html>
<body>
<h1>test</h1>
<p>$wk</p>
</body>
</html>
HERE1


尚、上記は、XSS対応にはなっていませんので、ご注意を!
XSSに関しては、『クロスサイト・スクリプティング』を参照してください。

◆ 補足

『./』は自ディレクトリです。
『../』は、親ディレクトリです。
たとえば、bbbディレクトリにtest.txtがあったとします。
すると、

cat /aaa/bbb/test.txt

cat /aaa/bbb/ccc/ddd/../../test.txt

は、同じになるのです。



CSRF(クロスサイト・リクエスト・フォージェリ)

◆ クロスサイト・リクエスト・フォージェリとは?

普通、Webページの入力項目に対して入力をして、書き込みを押すと、その入力データがサーバーに送られます。
たとえば、このWebページで、入力をしないで、悪意のあるページにアクセスしたとします。


すると、それだけで悪意のある入力データがサーバーへ送られる可能性があるのです。
そして、このような攻撃をCSRF(クロスサイト・リクエスト・フォージェリ)と言います。


たとえば、確認のページが、以下のようになっていたとします。

<form action="commit.php" method="post">
<input type="hidden" name="new_name" value="脆弱 太郎">
<input type="hidden" name="new_address" value="大阪府">
<input type="submit" name="back" value="戻る">
<input type="submit" name="commit" value="実行">
</form>

このときに、実行を押さず、以下のような悪意のページにアクセスしたら…。

<form action="http://○○/commit.php" method="post" name="f1">
<input type="hidden" name="new_name" value="いぱくん">
<input type="hidden" name="new_address" value="東京都">
</form>
<script>document.forms['f1'].submit();</script>

アクセスしただけで、submit()が実行されて、脆弱 太郎・大阪府でなく、いぱくん・東京都が書き込みされてしまうのです。


これ、mixiミクシィ)のような会員制のサイトでは、自分のIDで悪意の第三者に書き込みがされてしまうのです。
当然、その書き込みが、不正送金、商品購入、退会処理の場合があるわけです!


CSRF対策としては、使用者と開発者では以下があります。

◆ 使用者としてのCSRF対策

とにかく、会員制のサイトにログインしたら、ログアウトするまではサイト以外のページにアクセスしない。

◆ 開発者としてのCSRF対策

更新ページで、秘密情報挿入やRefererチェック等で、なりすましを厳しくチェックします。