<?xml version="1.0" encoding="EUC-JP"?>

<topic name="SQL Injection 対策" author="小川 環" date="2002.08.08">
<subsection name="SQL Injection とは？">
<p>
今日の Web アプリケーション開発では、SQL ベースの RDBMS と連携した事例が、たいへん日常的となっています。
そのせいか、最近になって頻繁に見かけるようになったのが <b>SQL Injection</b> です。
正式には、<u><b>Direct SQL Command Injection</b></u> と呼ばれています。<br/>
これは、フォームなどの入力時に不正な文字列 ( SQL 文 ) を打ち込み、それを Web 経由で受け取ったデータベース・サーバが完全なパラメータ値として処理せずに SQL コマンドとしてそのまま実行してしまうという、データベース・サーバを標的としたセキュリティ脆弱性です。
フォームなどに不正な文字列を入力するといった点で、<b>クロスサイト・スクリプティング脆弱性</b>のクラッキング手法とよく似ています。
</p>
</subsection>
<subsection name="原因">
<p>
<b>SQL Injection</b> は、<b>クロスサイト・スクリプティング脆弱性</b>と同様、<u>パラメータ値の入力チェック ( Input Validation ) を正しく処理していないこと</u>が原因となって発生します。<br/>
例えば、「フォーム上で入力した値にシングル・クォーテーション ( &#039; ) が含まれていると、SQL エラーが返ってくる」といった現象が発生するような場合は、特に要注意です。<br/><br/>
<subsection name="コーディングに問題のあるサンプルソース（リスト１）">
<source>
<![CDATA[
<%@ page pageEncoding="Shift_JIS" %>
<%@ page contentType="text/html; charset=Shift_JIS" %>
<%@ page import="java.util.*, java.sql.*, javax.sql.*, javax.naming.*" %>
<%
  // データソースの取得
  Context ctx = new InitalContext();
  DataSource sample = (DataSource)ctx.lookup("java:/comp/env/jdbc/myDatabase");

  // データベースの検索
  Connection conn = sample.getConnection();
  Stamement stmt = conn.createStatement();
  String country = request.getParameter("country");
  String selectSql = "SELECT * FROM profile WHERE country = '" + country + "'";
  ResultSet profiles = stmt.executeQuery(selectSql);
%>
<html>
<head>
<title>データベース検索のサンプル</title>
</head>
<body>
<h2>検索結果</h2>
<table>
<% while (profiles.next()) { %>
    <tr>
      <td><%= profiles.getString("NAME") %></td>
      <td><%= profiles.getString("ADDRESS") %></td>
    </tr>
<% } %>
</table>
</body>
</html>
<%
  // データベース・セッションの解放
  if (profiles != null) profiles.close();
  if (stmt != null) stmt.close();
  if (conn != null) conn.close();
%>
]]></source>
</subsection>
</p>
</subsection>
<subsection name="対策　プリコンパイルされたSQLステートメントを利用する">
<p>
JDBC ドライバには、<b>java.sql.PreparedStatement</b> という API が標準で用意されています。<br/>
この API を利用すると、プリコンパイルされた SQL ステートメントにパラメータ値をセットするよう処理されるため、シングル・クォーテーション ( &#039; ) などの特殊文字のエスケープ処理が不要になるどころか、パラメータ値が SQL コマンドとして機能される心配はまずありません。
なお、SQL パラメータ値をバインドする内部処理のメカニズムは、JDBC ドライバの実装に依存します。<br/>
JSTL では、<b>java.sql.PreparedStatement</b> を用いて SQL ステートメントを発行するように設計されています。<br/><br/>
<subsection name="JSTL を用いて、リスト１を修正したソース（リスト２）">
<source>
<![CDATA[
<%@ page pageEncoding="Shift_JIS" %>
<%@ page contentType="text/html; charset=Shift_JIS" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="sql" uri="http://java.sun.com/jstl/sql" %>

<%-- データソースの取得 --%>
<sql:setDataSource
  var="sample"
  url="java:/comp/env/jdbc/myDatabase"
/>

<%-- データベースの検索 --%>
<sql:transaction dataSource="${sample}">
  <sql:query var="profiles">
    SELECT * FROM profile WHERE country = ?
    <sql:param value="${param.country}"/>
  </sql:query>
</sql:transaction>

<html>
<head>
<title>データベース検索のサンプル</title>
</head>
<body>
<h2>検索結果</h2>
<table>
  <c:forEach var="row" items="${profiles.rows}">
    <tr>
      <td><c:out value="${row.NAME}"/></td>
      <td><c:out value="${row.ADDRESS}"/></td>
    </tr>
  </c:forEach>
</table>
</body>
</html>
]]></source>
</subsection>
</p>
</subsection>
<subsection name="関連リンク">
<p>
<ul>
<li><a href="http://www.ipa.go.jp/security/awareness/vendor/programming/a02_01.html" target="SQLInjection">セキュアデータベースプログラミング - SQL 組み立て時の引数チェック ( 情報処理振興事業協会 )</a></li>
<li><a href="http://www.sqlsecurity.com/faq-inj.asp" target="SQLInjection">SQL Injection FAQ ( SQLSecurity.Com )</a></li>
<li><a href="http://www.spidynamics.com/whitepapers/WhitepaperSQLInjection.pdf" target="SQLInjection">SQL Injection 白書 [ PDF ] ( SPIDynamics.Com )</a></li>
<li><a href="http://www.owasp.org/asac/input_validation/sql.shtml" target="SQLInjection">Input Validation - Direct SQL Command Injection ( OWAS Project )</a></li>
<!--
<li><a href="http://www.silksoft.co.za/data/sqlinjectionattack.htm" target="SQLInjection">Understanding and Preventing SQL Injection Attacks</a></li>
-->
</ul>
</p>
</subsection>
</topic>

