サイト攻撃とセキュリティについて
ここまでの説明で、クッキーというのが、小さい割には意外に重要な役割を果たしていることがわかったことと思います。何しろ、セッションを管理するためにも使われていたりするのですから。そうすると、当然ですが、「このクッキーの値が外部に漏れた場合に起こる影響」ということも想像ができるはずです。
例えば利用者名とパスワードを入力してログインすると、預金の確認や振込みなどの操作が行えるようなオンラインバンクがあったとしましょう。こうしたサイトでは、個々の識別をセッションで行っているわけです。もし、ある人がアクセスしているときに、誰かがその人のセッションIDを盗みとり、それとまったく同じセッションIDをクッキーに保存してログインしたらどうなるでしょうか? サーバは、その人がアクセスしてきたと認識し、預金の操作を行ってしまうでしょう。外部からクッキー情報が盗まれると、そんな恐ろしいことが可能になってしまうのです。
しかし、実際問題としてそんなことが可能なのでしょうか。それは、「場合によっては可能」なのです。例えば、この連載の第2回で、JSPによるフォーム送信のサンプルとして次のようなものを作成したのを覚えているでしょうか。
<%@ page language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=windows-31j">
<title>JSP SAMPLE</title>
</head>
<body>
<%
request.setCharacterEncoding("windows-31j");
String str = request.getParameter("text1");
str = str == null ? "(未送信)" : str;
out.println("※あなたが送信した内容<br>");
out.println("<font color=#FF0000>" + str + "</font>");
%>
<form method="post" action="./index.jsp">
<input type="text" name="text1" value="<%=str %>">
<input type="submit">
</form>
</body>
</html>

入力フィールドからテキストを書いて送信すると、そのテキストが表示される。なんてことはない、ごく単純なJSPです。では、この入力フィールドに、次のように書いて送信をしてみてください。どうなるでしょうか。
<script type="text/javascript">alert(document.cookie);</script>
画面にダイアログが現れ、そこに「JSESSIONID=○○」といったメッセージが表示されたはずです。これは、現在保存されているクッキーの内容なのです。なぜこんなものが表示されたのか? それは、入力フィールドに記入した<script>タグが原因です。このテキストをそのままページに出力したため、そこに書かれているスクリプトがその場で実行されてしまい、こうしたメッセージが表示されたのです。
JavaScriptを使えば、クッキー情報にアクセスすることができます。同様に、JavaScriptを使えば、他のページにリダイレクトすることも可能です。例えば、こんな具合です。
<script type="text/javascript">document.location.href="http://google.com/";</script>
これで、送信するとGoogle.comのページにジャンプします。――では、この2つを組み合わせたらどうなるでしょうか。例えば、「あるWebサイトのページに、現在のクッキー情報をクエリー文字として追加してリダイレクトする」というJavaScriptを入力したら?
ここでは単なるサンプルですが、これが例えば掲示板のようなものだったらどうでしょうか。投稿された内容は、そのまま保存され、ページに表示されます。そして、誰かがその掲示板にアクセスすると、その瞬間に自分のクッキー情報を付加してまったく別のサイトにジャンプするわけです。そのサイトに、送られたクッキーを保存して即座にGO BACKする仕掛けを用意しておけば、ユーザーは一瞬、他のサイトにアクセスしたことさえ気づかないでしょう。かくして、あっという間にクッキー収集サイトができあがる、というわけです。もし、くだんのオンラインバンクの掲示板にこんなものが投稿されていたら? 数時間後には、預金を空にされたユーザーによって銀行の電話はパンクすることでしょう。
以上が、スクリプトを挿入し実行させる「スクリプトインサーション」と、クラックした情報をクエリー文字にして他サイトへ送り出す「クロスサイトスクリプティング(XSS)」と呼ばれる攻撃の概要です。この2つが最も簡単にクッキー情報をクラックするサイト攻撃法といってよいでしょう。Webアプリケーション開発を行うなら、少なくともこれらの攻撃への対応ぐらいは知っておく必要があります。
送信情報を無効化する
では、このような場合にはどうやって対処すればよいのでしょうか。最も単純なのは、<>といった記号を置換してタグを無効にしてしまうことです。一般に「サニタイズ」あるいは「無効化」と呼ばれる、最も基本的な対処法です。では、スクリプトを修正してみましょう。
<% request.setCharacterEncoding("windows-31j"); String str = request.getParameter("text1"); str = str == null ? "(未送信)" : getSanitizedString(str); out.println("※あなたが送信した内容<br>"); out.println("<font color=#FF0000>" + str + "</font>"); %> <%! public String getSanitizedString(String s){ String str = s; str = str.replace("<","<"); str = str.replace(">",">"); str = str.replace("\"","""); str = str.replace(" "," "); return str; } %>

先ほどのスクリプトを修正し、getSanitizedStringというメソッドを追加してそれを呼び出して処理をさせるようにしてみました。修正したら、先ほどの<script>タグを送ってみましょう。今度はタグとスクリプトがそのままテキストとして表示されます。送信したJavaScriptが無効化され、スクリプトして機能しなくなっていることが分かります。
ここでは、"<>"と半角スペースをそれぞれHTMLの特殊文字に置換しています。タグを無効化することができれば、ここに挙げたような攻撃はほとんど受けることがなくなるでしょう。「送信された情報をそのまま出力に利用する場合には、その前に出力先で特殊な文字として扱われているものを無効化する」ということは忘れないでおきましょう。
テキスト値以外のパラメータ偽装
無効化については理解していても、意外なところでうっかりと見落としをしてしまうことがあります。中でもよくやってしまうのは「テキスト以外の情報」の見落としです。例えば、こんなスクリプトを考えてみましょう。
<%@ page language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=windows-31j">
<title>JSP SAMPLE</title>
</head>
<body>
<%
request.setCharacterEncoding("windows-31j");
String str = request.getParameter("text1");
str = str == null ? "(未送信)" : getSanitizedString(str);
String str2 = request.getParameter("radio1");
out.println("※あなたが送信した内容<br>");
out.println("<font color=#FF0000>" + str
+ "(" + str2 + ")" + "</font>");
%>
<%!
public String getSanitizedString(String s){
String str = s;
str = str.replace("<","<");
str = str.replace(">",">");
str = str.replace("\"",""");
str = str.replace(" "," ");
return str;
}
%>
<form method="post" action="./index.jsp">
名前:<input type="text" name="text1"><br>
<input type="radio" name="radio1" value="男">男
<input type="radio" name="radio1" value="女">女
<br><input type="submit">
</form>
</body>
</html>

名前と、性別のラジオボタンがあるフォームです。送信すると、名前と性別が表示されます。ここでは送られたテキストを先のgetSanitizedStringで無効化し表示しています。これなら問題なし……と思った人。これは間違いです。なぜなら、「ラジオボタンの値」が無効化されていないからです。
ラジオボタンというのは、テキストと違い、ユーザーが何かを入力したりできません。ボタンを押すと、そのボタンの値が送られるだけですから、一見するとあまり問題はないように思えます。では、これに攻撃をしてみましょう。Webブラウザのアドレスバーから、以下のURLを直接入力してアクセスをしてください。
http://localhost:8080/myweb/index.jsp?radio1=<script>alert(document.cookie);</script>
ページがロードされると同時に、クッキー情報が画面に表示されます。サニタイズをすり抜け、悪質なスクリプトが実行可能であることがこれで分かりますね。JSPでは、request.getParameterで送信された値を取り出しますが、「POSTでもGETでも同じように値を得てしまう」ところに問題があります。フォーム自体はPOSTで送信していますが、URLにクエリー文字をつけて送信しても、JSPは「GETで送信されたパラメータ」として値を取り出します。
従って、偽装されたパラメータをクエリー文字として送信すれば、ラジオボタンだろうがチェックボックスだろうが危険な値を送り込めることになります。ラジオボタンの値も無効化しておくべきなのです。基本的に、送信された値を直接出力に利用する場合は、どんなものであれ、すべて無効化を行う、と考えましょう。
まとめ
今回は、Webサイトにおける非常に重要な機能について説明してきました。セッション、クッキー、そしてサイト攻撃への対処。これらは、別々のもののようでいて実は一つのものです。サーバとクライアントがどのように重要な情報をやり取りしているか、その仕組みをどれだけ理解しているかということなのです。
特に、サイト攻撃については、ここであげた対処をすれば万全というわけではまったくありません。非常に広く知られた攻撃に対する一般的な対策ということであり、サイト攻撃も進化しますから、それにあわせて対処法も日々変わってくることでしょう。重要なのは、「Webとは、どのような仕組みで動いているか」を理解するということです。個々の方法を学ぶことも重要ですが、この根本的な仕組みの理解があってこそ、それらの知識も生きてくるのだ、ということを忘れないようにしましょう。


"http://google.com/";