|
本稿は『日経オープンシステム 2001年5月(no. 98)』に掲載された記事「オープンセミナー 実践!セキュアなWebプログラミング 第2回 ASP(Active Server Pages)編 Part1」をHTML化したものです。内容は雑誌掲載時とほぼ同じですが,誤記訂正や小さな改善を施してあります。(2001年9月)
|
 |
クラッカが送りつけたSQLを実行してしまう
|
入力データのチェックに不備がある場合,データベースにアクセスするWebアプリケーションでは,クラッカが入力したSQLを実行させられてしまう。
Microsoft SQL Serverにアクセスする,図5のようなASPプログラムがあるとする。入力されたデータでデータベースを検索し,結果を表示する単純なアプリケーションだ。word.aspが条件入力画面で,「Go」ボタンが押されるとaction.aspを呼び出してデータベースに接続,検索処理を実施し,結果を表示する。図5の下の画面がデータベースの中身だ。今回検索するテーブルはsTBLで,カラムはIDとnameだけ。最初に5人のデータが登録されている。
|
図5●Microsoft SQL ServerにアクセスするWebアプリケーション
入力されたキーワードを検索条件としてSQLを作成し,実行結果を表示するシンプルなWebアプリケーション。最初は,テーブルsTBLに5人のデータが格納されている
|

上記ページのソース・コード(word.asp)
1 <HTML>
2 <HEAD>
3 <TITLE>検索画面</TITLE>
4 </HEAD>
5 <BODY>
6 <FORM METHOD="post" ACTION="action.asp">
7 キーワード:<INPUT TYPE="text" NAME="Keyword" SIZE=100><BR>
8 <INPUT TYPE="submit" NAME="SubmitButton" VALUE="Go!">
9 </FORM>
10 </BODY>
11 </HTML>
|
呼び出すプログラム(action.asp)
1 <%
2 Option Explicit
3 %>
4
5 <HTML>
6 <HEAD>
7 <TITLE>検索結果</TITLE>
8 </HEAD>
9 <BODY>
10 <%
11 Dim DBCon
12 Dim DBRS
13 Dim SqlStr
14 Dim HTMLStr
15
16 SqlStr = ""
17 Set DBCon = Server.CreateObject("ADODB.Connection")
18 DBCon.Open "test", "User", "123"
19
20 SqlStr = "SELECT * FROM sTBL WHERE Name=" & _
Request.Form("Keyword") & "'"
21 Response.Write(SqlStr & "<BR><BR>")
22
23 Set DBRS = DBCon.Execute(SqlStr)
24
25 HTMLStr = "検索結果<BR>"
26 If Not DBRS.EOF Then
27 HTMLStr = HTMLStr & DBRS("Name").Value
28 End If
29 Response.Write(HTMLStr)
30 Set DBRS = Nothing
31 Set DBCon = Nothing
32 %>
33 </BODY>
34 </HTML>
|
SQL Server管理ツール
|
 |
パラメータに別のSQLを入力
|
この単純なアプリケーションに,前述のセキュリティ・ホールが生まれてしまっている。このアプリケーションは,入力されたデータをチェックせずに,実行するSQL文の一部に使っている。そのため,データに,別のSQL文を含めることが可能になっている。図5のaction.aspの20行目がSQLを生成している部分だ。
SQL文の区切りを示す記号は「;」。「;」に続いて入力されたSQL文は,本来のSQLに続いて実行されてしまうのである。
図6の例では,本来のSQLとは別に
INSERT sTBL VALUES ('sanaki') ;
が実行されている。図6の下の画面が,実行後のデータベースの内容だ。テーブルsTBLにsanakiが追加され,データは6人になっている。
|
図6●入力データに含めたSQL文を実行させる
SQL文の区切りを示す「;」に続いて,実行させたいSQL文を入力する。この例では,テーブルsTBLに「sanaki」を挿入するSQLが実行されてしまっている
|

 |
バックアップ・ファイルは残さない
|
この例のように,クラッカがデータベースに不正なレコードを挿入するためには,デーブルの構造を知っていなければならないというハードルがある。しかし,例えばECサイト構築パッケージ・ソフトウェアをひな型としてアプリケーションを構築した場合,パッケージを扱ったことのある人間ならばテーブル構造についての知識を持っているだろう。
また,バックアップ・ファイルによってソース・コードが外部から読まれてしまうケースもある。通常,ASPファイルにアクセスしてもスクリプトを読むことはできない。スクリプトを実行した結果が表示されるだけである。しかし,例えばaction.aspファイルをエディタで編集し,保存したとする。この時,エディタによってはバックアップ・ファイルをaction.bakなどの名称で作成する。このaction.bakファイルにアクセスすれば,ASPスクリプトの内容が外部から読めてしまうのだ。そもそも,本番サーバーのファイルを直接変更すべきではない。開発用サーバーで変更し,テストしてから本番サーバーにリリースすべきである。また,その際バックアップ・ファイルを一緒にコピーしてしまわないように注意しよう。
bakファイルが閲覧されてしまった場合,今回のサンプルでは他の問題も発生する。ASPファイル内に書かれているデータベース・ユーザーのパスワードが漏えいしてしまうのである。このような事態に備えて,パスワードは外部からアクセスできない別のファイルなどで管理することを強く推奨する。
|
 |
データベースへの引数をチェック
|
話を元に戻そう。SQLが実行されてしまうようなセキュリティ・ホールを作らないためには,具体的にはどのような措置を取ればよいだろうか。図5の不正アクセスの例では,入力値全体を「'」で囲んでいた。「'」で囲んだことで,入力データ全体が文字列として扱われ,本来のSQLに挿入されてしまったのである。対策としては文字列として解釈されないようにすればよい。一つの方法として,文字列中に「'」が登場した場合,「''」と重複してやることで,文字列を囲むシングル・クォーテーションではなく,文字としてのシングル・クォーテーションとして評価されるようにするというやり方がある。
ASPの場合,ユーザーからの入力データ(Request.Cookieの値,Request.QueryStringの値,Request.Formの値)を図7のような関数に入れ,「'」を重複させる。
特にセキュリティを重視した場合においては,Webアプリケーションの仕様上許されるのであれば,「'」以外にも,半角の特殊文字列やSQL文の予約文字列(SELECTやINSERTなど)を全角にしてからデータベースに格納するというようなチェック機構を作っておくことが望ましい。
|
図7●入力データ中のSQL実行に対する対策
シングル・クォーテーションを重複させて,入力データがSQL文として実行されないようにする
|
シングル・クォーテーションを重複させる関数の例
Function AddSQ(str)
AddSQ = Replace(str, "'", "''")
End Function
|
図3のaction.aspの場合,20行目を次のように変更する
SqlStr = "SELECT * FROM sTBL WHERE Name='" & _
AddSQ(Request.Form("Keyword")) & "'"
|
|
 |
ユーザーのアクセス権限は最小限に
|
また,今回のようなデータベースに対する書き込みは,当然のことながら書き込みアクセス権が与えられていなければ不可能である。
検索しか行わないのであれば,ASPが使用するデータベースのユーザーに対して,更新権限を与える必要はない。SELECT権限だけを与えればよい。写真1は(1)Webアプリケーションが使用するすべてのテーブル(システム・テーブルは除く)に対してSELECTのみ実行できるユーザー「ReadUser」と,(2)フル・アクセス可能なユーザー「WriteUser」の2つを用意した例である。更新が必要でない場合はReadUserでアクセスするようにすれば,セキュリティの向上が期待できる。
少なくともシステム・テーブルにアクセスできるユーザーは,一般のユーザーと別のグループとし,一般のユーザーはシステム・テーブルにアクセスできないようにすべきである*8。
|
写真1●入力データ中のSQL実行に対する対策
Webアプリケーションが使用するデータベースアカウントには必要最小限の権限だけを与える。この例では,SELECTのみが実行可能なアカウントとINSERTとUPDATEが実行可能なアカウントを分けている。(◆表現を改善)
|

*7
(欠番)
*8
例えばMicrosoft SQL Serverでは,システム・テーブルに対する権限があれば,ストアド・プロシージャxp_cmdshellを使ってDOSコマンドを実行できる。そのためサーバー全体を乗っ取られる可能性すらある。
|
 |
SQLからDOSコマンドを実行される
|
WebアプリケーションでJetエンジンやMDAC(Microsoft Data Access Components)を使用している場合,パラメータにSQLを混入されるだけでなく,DOSコマンドを含められ,実行されてしまう恐れがある。
この問題は,Windows NT 4.0 Option Packによりデフォルトでインストールされるサンプル・アプリケーションにも存在する。Windows NT 4.0のユーザーは,JetエンジンやMDACを使用していない場合でも,後述するサンプル・アプリケーションの削除などの対策が必要である。
具体的な例を示そう。Accessデータベースのデータを検索して結果を表示するWebアプリケーションがあるとする(図8)。
このWebアプリケーションでは,ユーザーがフォーム入力したデータ(word)をSQL文の検索条件に挿入して,Accessデータベース・ファイル(btcustmr.mdb)を検索している。btcustmr.mdbは,IIS 4.0のヘルプの一部としてデフォルトでインストールされるファイルである。
さて,この例では,フォームから「|shell("cmd /c echo aaa >c:\test.txt")|」という文字列が入力されている。Jetエンジンは「|」と「|」に囲まれた文字列をVBAスクリプトとして実行する。そして,VBAのshell関数は,DOSコマンドを呼び出す関数である。
つまり,この例では,サーバー上で「cmd /c echo aaa >c:\test.txt」が実行されてしまうのだ。cmd.exeはコマンド・インタプリタである。内部コマンドechoが実行され,その結果,Cドライブ直下にtest.txtが生成される。サンプルとして示したのは単にファイルが生成されるコマンドであるが,FTPで不正なexeファイルを送り込んで実行し,サーバーを乗っ取ることも可能だ。極めて深刻な問題である。
これから開発するアプリケーションでJetエンジンを使用するケースは少ないだろうが,この問題は,MDACに含まれるRDS(Remote Data Services)が使用できるようになっている場合にも発生する。MDACとは,Microsoftが提供するあらゆるデータに対するアクセス方法をまとめたコンポーネントである。その中のRDSとは,WWWからデータベースへのアクセスを提供するサービスであり,Windows NT 4.0のOption Packをインストールすると,自動的にインストールされてしまう。今回サンプルとして使用したbtcustmr.mdbも同時にインストールされる。RDSはmdbファイルに対しJetエンジンを利用してアクセスをしているため,Windows NT 4.0のOption Packインストール直後の状態では,WWWからJetエンジンを介してDOSコマンドを実行することが可能になってしまっている。
対策としては,JetエンジンおよびRDSを利用しないこと。MDACを使用したサンプル・アプリケーションはWWWの仮想ディレクトリmdacにインストールされているので,削除しておこう。RDSを利用せざるを得ない場合は,MDAC 2.0以降にバージョンを上げ,レジストリを変更し,DOSコマンドを呼び出せないセーフモードで運用しなければならない*9。
また,図8のWebアプリケーション側での問題は,入力チェックがなされていなかったことだ。今回のJetエンジンの問題の対策であれば,半角文字の「|」を全角「|」に置換することで対処できる*10。
|
図8●Jetエンジンが入力データ中のコマンドを実行する
mdbファイルを使用している場合だけでなく,MDAC(Microsoft Data Access Components)に含まれるRDS(Remote Data Services)を使用している場合も同様な危険がある
|

*9
マイクロソフトの技術文書「[IIS]Microsoft Security Bulletin MS99-025のFAQ(J049349)」(http://www.microsoft.com/JAPAN/support/kb/articles/J049/3/49.htm?LN=JA&SD=SO&FR=0),「Microsoft Internet Information Server 4.0セキュリティ チェックリスト」(http://www.microsoft.com/japan/technet/security/checklist.asp),「Internet Information Services 5 セキュリティのチェックリスト」(http://www.microsoft.com/japan/technet/security/iis5chk.asp)を参照されたい。
*10
半角文字「|」を全角文字「|」に置換するには次のようにすればよい。
str2 = Replace (str1, ”|”, ”|”)
(◆コードを改善。当初は次のようなコードを掲載していた)
Function InputChk (InputData)
Dim temp
Dim i
temp = InputData
i = InStr (1, temp, “|”)
While Not i = 0
temp = Left (temp, i - 1) & “|” & Mid (temp, i + 1)
i = InStr (1, temp, “|”)
Wend
InputChk = temp
End Function
|
 |
WWWブラウザでの入力値チェックを すり抜けられる
|
筆者は過去に,HTMLのテキスト・フィールドの文字数制限で,データベースへの不正な入力を防ごうとしているシステムに出会ったことがある。具体的には,INPUTタグのMAXLENGTHオプションを指定する方法だ。
<INPUT TYPE="text" MAXLENGTH="10">
10文字以内で不正侵入を実現するためのスクリプトやSQL文を書くことはできないであろう。では,これで安心だろうか?
結論から言えば,セキュリティ対策としては上記の方法にはほとんど効果がない。
クライアント側のHTMLやJavaScriptなどで処理する方法は,ネットワークを介さないので処理が速いなどの利点がある。しかし,クライアント側スクリプトはWWWブラウザの設定で無効にされているかもしれない。それだけでなく,処理結果を改ざんされる恐れを払拭できない。入力チェックをクライアントの処理だけに依存することは非常に危険である。個人情報や金銭を扱う,高いセキュリティが要求されるWebアプリケーションにおいては,最終的な入力チェックはサーバー側で行う必要がある。
|
 |
URLで送信データを直接指定
|
クライアント側でのチェックがう回されてしまう例を示す。図9のような,ユーザーに月を入力してもらうWebアプリケーションがあるとする。WWWブラウザのJavaScriptで入力データをチェックしており,月の数として不適切な「13」が入力されたため,JavaScriptがエラーを示すダイアログを表示している。今回のセミナーのテーマはASPだが,サーバー側がASPでもクライアントではJavaScriptを使うことが多いであろう。
画面のソース・コードがclient.htm。そこからデータを受け取るプログラムがclient2.aspで,受け取った数値を「○月です」と表示するだけのプログラムだ。
IEやNetscape Communicatorでは,設定でJavaScriptをOFFにされる恐れがある。その対策も抜かりはない。client.htmでは,フォームの送信先をJavaScriptで指定している(client.htmの16行目)。JavaScriptをOFFにすれば,入力値チェック機構が働かないだけでなくclient2.aspへ送信する事もできなくしている。
それでは,これで安全だろうか? 実は,下のように直接ブラウザでURLを指定することで,チェックをすり抜けることができる。
http://target/client2.asp?month=13
client.htmで「送信」ボタンが押された時行われる動作は,上記のように,フォームのデータを「?」の後ろに続くクエリー文字列として送信することだ。そのため,WWWブラウザで直接URLをリクエストすれば,クライアント側での入力チェックはたやすくすり抜けられてしまう。図10のように,不正なはずのデータが入力されてしまうのだ。
容易に想像できるように,この攻撃方法を応用すれば,ドロップダウン・リスト・ボックスや,ボタンやチェック・ボタンのフィールドで指定する場合など,提示した候補の中から選択するフォームであっても,候補にないデータを送信することが簡単にできる。
年月日のようなデータであれば,偽られても大した問題は起きないかもしれない。しかし,これがユーザーIDであれば,Webアプリケーションの構成によっては,他のユーザーの個人情報を表示したり,他のユーザーになりすまされて操作されてしまったりすることが起こり得る。あるいはECサイトで,注文の価格が偽られてしまうことがあるかもしれない。
今回のサンプルは,不正な動作をイメージしやすいように,GETメソッドを利用した。フォームからデータを送信する方法には,GETのほかにPOSTがある。GETでは送信データがURLのクエリー文字列に含まれるのに対し,POSTでは送信データはリクエストのボディー部で送信されるため,URLには現れない。であれば,「GETメソッドだから,ブラウザから直接アクセスされる。POSTなら大丈夫」なのだろうか。残念ながらPOSTでも危険性に変わりはない。ブラウザがPOSTメソッドによってデータを送っているやり方をそのまま真似れば,データを直接入力することができる。ブラウザのアクセスをシミュレートするツールは,インターネットで容易に入手できる(写真2)。
クライアントでの入力値チェックが無駄というわけではない。サーバーにアクセスすることなく,ユーザーに入力した時点ですぐにエラーを知らせることができるので,入力効率の向上という面では意味がある。しかし,セキュリティ対策という観点からはほとんど効果がないことが理解していただけたと思う。
|
図9●クライアントで入力データをチェックするプログラム例
JavaScriptを無効にすると,データを送信できないようになっている
|

HTMLのソース(client.htm)
1 <HTML>
2 <HEAD><TITLE>サンプル</TITLE></HEAD>
3 <BODY>
4 <FORM METHOD="get" NAME="myFrm">
5 <INPUT TYPE="text" NAME="month" SIZE="3"><BR>月
6 <INPUT TYPE="submit" VALUE="Go!" onClick="chk()">
7 </FORM>
8 <SCRIPT LANGUAGE="JavaScript"%gt;
9
10 <!--
11 // 整数かどうかチェックして,かつ1以上12以下だった場合,
12 // FORMのACTIONを指定するJavaScript
13 function chk() {
14 if (parseInt(document.myFrm.month.value).toString(10) == _
document.myFrm.month.value) {
15 if (0 < parseInt(document.myFrm.month.value) && _
parseInt(document.myFrm.month.value) < 13) {
16 document.myFrm.action = "client2.asp";
17 }
18 else {
19 alert('月を1-12の範囲で入力してください');
20 }
21 }
22 else {
23 alert('月を1-12の範囲で入力してください');
24 }
25 }
26 // -->
27
28 </SCRIPT>
29
30 </BODY>
31 </HTML>
|
上のページから呼び出すプログラム(client2.asp)
1 <%
2 Option Explicit
3 Response.Write(Request.QueryString("month") & "月です")
4 %>
|
|
図10●クライアントでの入力データ・チェックをすり抜ける
図9のWebアプリケーションに対し,直接ブラウザで以下のURLを入力する。ユーザーIDなど重要なデータを偽られると深刻な問題になる可能性がある。
|

写真2●フォームのPOSTメソッドを再現できるソフトウェアの例
このようなツールは,プログラミングとTCP/IPについての初歩的な知識があれば作成できる
|

 |
非公開のはずのファイルにアクセスされる
|
Webアプリケーションからファイルを呼び出す際にも注意が必要である。ファイル名にユーザー入力データを使用している場合,対象ディレクトリ以外のファイルにアクセスされてしまうかもしれない。
写真3は,ユーザーがファイル名を入力し,ファイルの内容を表示するWebアプリケーションである。ここで表示されているファイルはC:\repair\setup.logファイルである。WWWで公開しているディレクトリはC:\inetpub以下だけのはずなのに,その外にあるディレクトリが見えてしまっている。同様な方法でsystem32ディレクトリなどにアクセスされ,システム情報を入手されれば,サーバーそのものに侵入される恐れがある。
問題が発生した原因は,「..」により上位ディレクトリを指定されてしまったことにある*11。「..」を削除する関数の例を図11に挙げておく。
|
写真3●参照ファイル名として非公開のはずのファイルを指定される
入力データをファイル名として使用しファイルを呼び出す場合,上位ディレクトリを示す「..」を使い,公開ディレクトリ外のファイルにアクセスされる恐れがある
|

図11●入力データ中の「..」を排除する関数の例
ファイル名をユーザーに入力させる場合は,このような処理を行う
|
Function DelDDot(str)
DelDDot = Replace(str, "..", "")
End Function
|
*11
IISの設定の中に「親のパスは有効にする」という項目がある。IIS4.0ではデフォルトでチェックされている。しかし,この設定を無効にしても,セキュリティ上はほとんど効果がない。
これを無効にすれば,Server.MapPathメソッドで引数に「..」を指定できなくなる(Server.MapPathは仮想ディレクトリ名から物理パスを求めるメソッド)。しかしASPスクリプト・ファイルのあるディレクトリの物理パスを取得後に,FileSystemObjectオブジェクトを呼び出すことで,1つ上のディレクトリを取得する事は可能なのである。以下にコードの例を示す。
まず,ASPが実行しているURLを取得する。
CD = Server.MapPath (“.”)
Set FS = Server.CreateObject (“Scripting.FileSystemObject”)
次に,親ディレクトリにあるファイルtext.txtをオープンする。
Set FR = FS.OpenTextFile (CD & “\..\text.txt”, 1, False, 0)
この問題は米国のセキュリティ総合サイトSecurityFocusに詳細が記述されている(NT Using ASP And FSO To Read Server Files Vulnerability http://www.securityfocus.com/bid/230)。
また,筆者が確認したところ,SSIでも「親のパスは有効にする」のチェックをはずしてあっても,
<!--#EXEC CMD="..\hello.exe"-->
(◆誤記訂正。コメントの閉じ方)
として,Windowsディレクトリ(%SystemRoot%)にあるhello.exe を動かすことができた。
|
 |
フィルタリング・ツールで入力チェック
|
以上,今回は主に不正な入出力データのチェック漏れが生むセキュリティ・ホールについて見てきた。これらのセキュリティ・ホールをふさぐには,すべてのデータが想定したものかどうか検査し*12,特殊文字をチェックし置換,あるいは削除する必要がある。
しかし,すべての入力をチェックできているかどうか不安,またはもう作ってしまったシステムで改良することはコストがかかりすぎる,といったケースもあるだろう。そのような場合は,NTTデータCCS アウトソーシング事業本部 ネットワークサービス部 シニアスペシャリストの長谷川武氏が開発し,無償で配布しているguard3.dllというツールを使ってみてはいかがであろうか。
guard3.dllは,IISが受け取った入力データをフィルタし,あらかじめ設定した種類の文字を削除,変換したり,設定値を超える長さのデータを削除したりするツールである。ソース・コードを含め無償公開されており,実際に我々の顧客で不正アクセス対策のために使用されている。詳しい情報についてはguard3.dllのホームページ*13を参照されたい。
|
*12
VBScriptでデータが数値かどうかをチェックするための関数としてIsNumeric()関数があるが,全角の数字でもTRUE(数字である)と返すことに注意。これを回避するための簡単な方法としては,Visual BasicのStrConv()関数によって,全角を半角にするという方法がある。そのためには,あらかじめVisual BasicのStrConv()関数をASPから利用できるようにしておく。COMコンポーネントを作るというのが簡単な方法であろう。
*13
http://www.trusnet.com/tools/guard3/index.html
|
著者プロフィル
佐名木 智貴(さなき ともき)
元セントラル・コンピュータ・サービス株式会社 事業推進センター インターネットビジネス部所属。システムのセキュリティ・ホールの検査を専門としている。システム開発のセキュリティ問題にも詳しい。
|
Copyright (C) 2001-2008 NTT DATA CCS CORPORATION
|