<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/security.database.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ja',
  ),
  'this' => 
  array (
    0 => 'security.database.sql-injection.php',
    1 => 'SQLインジェクション',
    2 => 'SQLインジェクション',
  ),
  'up' => 
  array (
    0 => 'security.database.php',
    1 => 'データベースのセキュリティ',
  ),
  'prev' => 
  array (
    0 => 'security.database.storage.php',
    1 => 'ストレージの暗号化',
  ),
  'next' => 
  array (
    0 => 'security.errors.php',
    1 => 'エラーのレポート',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ja',
    'path' => 'security/database.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="security.database.sql-injection" class="sect1">
    <h2 class="title">SQLインジェクション</h2>
    <p class="simpara">
     SQLインジェクションは、
     攻撃者が動的なSQLクエリを組み立てる責任があるアプリケーションコードの欠陥を突く手法です。
     攻撃者は、アプリケーションの権限が必要な部分にアクセスでき、
     データベースからすべての情報を引き出し、既存のデータを改ざんしたり、
     危険なシステムレベルのコマンドをデータベースのホスト上で実行できてしまいます。
     こうした脆弱性は、開発者が任意の入力をSQL文に結合したり、挿入したりすることで発生します。
    </p>
    <p class="para">
     <div class="example" id="example-1">
      <p><strong>例1 
       表示するデータを分割し ... そしてスーパーユーザーを作成します。(PostgreSQLの例)
      </strong></p>
      <div class="example-contents"><p>
       以下の例では、ユーザーからの入力が直接SQLクエリに挿入されているため、
       攻撃者がデータベースの superuser アカウントを取得できてしまいます。
      </p></div>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000">$offset = $_GET['offset']; // 入力チェックが行われていません！<br />$query  = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";<br />$result = pg_query($conn, $query);</span></code></div>
      </div>

     </div>
      通常のユーザーは、<var class="varname">$offset</var> が <abbr title="Uniform Resource Locator">URL</abbr> に埋め込まれている
      &#039;次へ&#039;または&#039;前へ&#039;リンクをクリックします。スクリプトは、受け取った
      <var class="varname">$offset</var> が数字であることを期待します。しかしながら、
      以下のような値を <abbr title="Uniform Resource Locator">URL</abbr> に追加して攻撃を試みるとどうなるでしょうか？
      <div class="informalexample">
       <div class="example-contents">
<div class="cdata"><pre>
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
    select &#039;crack&#039;, usesysid, &#039;t&#039;,&#039;t&#039;,&#039;crack&#039;
    from pg_shadow where usename=&#039;postgres&#039;;
--
</pre></div>
       </div>

      </div>
      このようなことが行われると、スクリプトは攻撃者にスーパーユーザー権限での
      アクセスを提供してしまいます。<code class="literal">0;</code>が正しいオフセットを
      指していると同時に、クエリをそこで終端させていることに気をつけてください。
    </p>
    <blockquote class="note"><p><strong class="note">注意</strong>: 
     <p class="para">
      SQLパーサーにクエリの残りの部分を無視させるために開発者によく使わ
      れる技法として、SQLのコメント記号である<code class="literal">--</code>があ
      ります。
     </p>
    </p></blockquote>
    <p class="para">
    パスワードを取得する恐るべき手段に、サイトの検索結果のページを欺く
    というものがあります。攻撃者が必要な作業は、投稿された変数
    の中でSQL命令で使用される際に正しく扱われていないものがあるかどう
    かを確かめることだけです。これらのフィルタは、通常、
    <code class="literal">SELECT</code> 文の <code class="literal">WHERE, ORDER BY,
    LIMIT</code> 及び <code class="literal">OFFSET</code> 句をカスタマイズするた
    めに前に置かれる形で設定されます。使用するデータベースが
    <code class="literal">UNION</code>構造をサポートしている場合、
    攻撃者は元のクエリに任意のテーブルからパスワードのリストを取得する
    クエリを追加しようとするかもしれません。
    パスワードそのものではなく、セキュアなハッシュ化されたパスワードだけを保存することを強く推奨します。
     <div class="example" id="example-2">
      <p><strong>例2 
      記事...そして(全てのデータベースサーバーの)いくつかのパスワード
       のリストを表示する
      </strong></p>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000">$query  = "SELECT id, name, inserted, size FROM products<br />           WHERE size = '$size'";<br />$result = odbc_exec($conn, $query);</span></code></div>
      </div>

     </div>
     クエリの静的な部分は、以下のように全てのパスワードを外部にもらす別の
     <code class="literal">SELECT</code>文と組み合わせることができます。
     <div class="informalexample">
      <div class="example-contents">
<div class="cdata"><pre>
&#039;
union select &#039;1&#039;, concat(uname||&#039;-&#039;||passwd) as name, &#039;1971-01-01&#039;, &#039;0&#039; from usertable;
--
</pre></div>
      </div>

     </div>
    </p>
    <p class="para">
     <code class="literal">UPDATE</code> や <code class="literal">INSERT</code> 文も、
     データベースを攻撃するために使用されます。
     <div class="example" id="example-3">
     <p><strong>例3 
     パスワードのリセットから ... (全てのデータベースサーバーで)より多
     くの権限を得るまで
     </strong></p>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000">$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";</span></code></div>
      </div>

     </div>
     もし悪意のあるユーザーが管理者のパスワードを変更するために
     値 <code class="literal">&#039; or uid like&#039;%admin%</code> を
     <var class="varname">$uid</var> に代入するか、または、より多くの権限を得
     るために、単純に<var class="varname">$pwd</var> に
     <code class="literal">hehehe&#039;, trusted=100, admin=&#039;yes</code>と設定すると、
     このクエリは以下のように改謬されてしまいます。
     <div class="informalexample">
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #FF8000">// $uid: ' or uid like '%admin%<br /></span><span style="color: #0000BB">$query </span><span style="color: #007700">= </span><span style="color: #DD0000">"UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%';"</span><span style="color: #007700">;<br /><br /></span><span style="color: #FF8000">// $pwd: hehehe', trusted=100, admin='yes<br /></span><span style="color: #0000BB">$query </span><span style="color: #007700">= </span><span style="color: #DD0000">"UPDATE usertable SET pwd='hehehe', trusted=100, admin='yes' WHERE<br />...;"</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

     </div>
    </p>
    <p class="simpara">
     攻撃者がデータベースの構造に関して最低限の知識を持っていないと攻撃は成功しないのが明らかです。
     しかし、その手の情報はたいてい、とても簡単に入手できます。
     たとえば、コードの一部がオープンソースソフトウェアの一部のため、
     公開されている可能性があります。
     こうした情報は、クローズドソースの場合でも漏洩する可能性があります -
     エンコードされたり、難読化されたり、コンパイルされていてもです。 -
     さらに、自作のコードであっても、エラーメッセージを表示することで漏れてしまう可能性があるのです。
     他の方法としては、ありがちなテーブルやカラムの名前を使うことが挙げられます。
     たとえば、&#039;id&#039;, &#039;username&#039;, &#039;password&#039; カラムを持つ &#039;users&#039; テーブルを使うログインフォームが挙げられます。
    </p>
    <p class="para">
     <div class="example" id="example-4">
     <p><strong>例4 データベースホストのオペレーティングシステムを攻撃する
     (MSSQLサーバー)</strong></p>
      <div class="example-contents"><p>
       恐ろしい例として、
       いくつかのデータベースホストで、
       オペレーティングシステムレベルのコマンドがアクセスできる方法を示します。
      </p></div>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000">$query  = "SELECT * FROM products WHERE id LIKE '%$prod%'";<br />$result = mssql_query($query);</span></code></div>
      </div>

     </div>
     攻撃者が、値
     <code class="literal">a%&#039; exec master..xp_cmdshell &#039;net user test testpass /ADD&#039; --</code>
     を<var class="varname">$prod</var>に投稿した場合、
     <var class="varname">$query</var> は以下のようになります。
     <div class="informalexample">
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000">$query  = "SELECT * FROM products<br />           WHERE id LIKE '%a%'<br />           exec master..xp_cmdshell 'net user test testpass /ADD' --%'";<br />$result = mssql_query($query);</span></code></div>
      </div>

     </div>
     MSSQLサーバーは、新規ユーザーをローカルアカウント用データベースに追
     加するコマンドを含むSQL命令をバッチ実行します。
     このアプリケーションが<code class="literal">sa</code>で実行され、
     MSSQLSERVERサービスが充分な権限で実行されていた場合、攻撃者は
     このマシンにアクセスする権限を有することになります。
    </p>
    <blockquote class="note"><p><strong class="note">注意</strong>: 
     <p class="para">
      上記の例は、データベースサーバーの種類に依存しています。
      しかし、他の製品に対して同様な攻撃ができないことを意味するもので
      はありません。使用しているデータベースが他の手段で攻撃できる可能性もあります。
     </p>
    </p></blockquote>
    <p class="para">
     <div class="mediaobject">
      
      <div class="imageobject">
       <img src="images/7484d159262ae6f905a917ed1f6bab4a-xkcd-bobby-tables.png" alt="SQL インジェクションで発生する面白い問題の例" width="666" height="205" />
      </div>
      <div class="caption">
       <p class="simpara">
        この画像は <a href="http://xkcd.com/327" class="link external">&raquo;&nbsp;xkcd</a> から提供いただいたものです。
       </p>
      </div>
     </div>
    </p>

    <div class="sect2" id="security.database.avoiding">
     <h3 class="title">回避策</h3>
     <p class="para">
      SQLインジェクションを回避するおすすめの方法は、
      すべてのデータをプリペアドステートメント経由でバインドすることです。
      パラメータ化されたクエリは、
      SQLインジェクションをすべて防ぐのに十分ではありませんが、
      SQL文への入力を与える一番簡単かつ安全な方法です。
      <code class="literal">WHERE</code>,
      <code class="literal">SET</code>, <code class="literal">VALUES</code>
      句に与えるすべての動的なデータリテラルは、
      すべてプレースホルダーで置き換えなければいけません。
      すべての実データは実行中にバインドされ、SQLコマンドとは別に送信されます。
     </p>
     <p class="para">
      パラメータのバインドは、データに対してのみ使えます。
      SQLクエリの他の動的な部分については、
      許される値の既知の値でフィルタしなければいけません。
     </p>
     <p class="para">
      <div class="example" id="example-5">
      <p><strong>例5 PDO のプリペアドステートメントを使い、SQLインジェクションを回避する</strong></p>
       <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">// SQL の動的な部分は、期待する値になるように検証されています<br /></span><span style="color: #0000BB">$sortingOrder </span><span style="color: #007700">= </span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'sortingOrder'</span><span style="color: #007700">] === </span><span style="color: #DD0000">'DESC' </span><span style="color: #007700">? </span><span style="color: #DD0000">'DESC' </span><span style="color: #007700">: </span><span style="color: #DD0000">'ASC'</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$productId </span><span style="color: #007700">= </span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'productId'</span><span style="color: #007700">];<br /></span><span style="color: #FF8000">// SQL はプレースホルダを使って準備します。<br /></span><span style="color: #0000BB">$stmt </span><span style="color: #007700">= </span><span style="color: #0000BB">$pdo</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">prepare</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT * FROM products WHERE id LIKE ? ORDER BY price </span><span style="color: #007700">{</span><span style="color: #0000BB">$sortingOrder</span><span style="color: #007700">}</span><span style="color: #DD0000">"</span><span style="color: #007700">);<br /></span><span style="color: #FF8000">// 値は LIKE ワイルドカードを使って与えられます<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">([</span><span style="color: #DD0000">"%</span><span style="color: #007700">{</span><span style="color: #0000BB">$productId</span><span style="color: #007700">}</span><span style="color: #DD0000">%"</span><span style="color: #007700">]);<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
       </div>

      </div>
     </p>

     <p class="simpara">
      プリペアドステートメントは
      <a href="pdo.prepared-statements.php" class="link">PDO</a> や
      <a href="mysqli.quickstart.prepared-statements.php" class="link">MySQLi</a>、
      そして他のデータベースライブラリでも使えます。
     </p>

     <p class="simpara">
      SQLインジェクションによる攻撃は、
      セキュリティを考慮して書かれていないコードを攻撃する方法です。
      特にクライアント側から入力されるあらゆる種類の入力を決して信用しないでください。これは、select ボックス や hidden input フィールド、Cookie の場合も同様です。最初に示した例は、簡単なクエリが破滅をもたらしうることを示しています。
     </p>

     <p class="para">
      攻撃を防ぐ戦略として、以下に示すいくつかのコーディングプラクティスに従うことが挙げられます:
      <ul class="itemizedlist">
       <li class="listitem">
        <span class="simpara">
         データベースにスーパーユーザーまたはデータベースの所有者として接続しないでください。
         非常に制限された権限を有するカスタマイズされたユーザーを常に使用してください。
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         指定された入力が期待するデータ型であることを確認してください。
         <abbr title="PHP: Hypertext Preprocessor">PHP</abbr>は、
         多くの種類の入力検証用関数を有しており、
         <a href="ref.var.php" class="link">変数関連の関数</a> や
         <a href="ref.ctype.php" class="link">文字型関数</a> にある簡単な関数
         (例: それぞれ、<span class="function"><a href="function.is-numeric.php" class="function">is_numeric()</a></span>, <span class="function"><a href="function.ctype-digit.php" class="function">ctype_digit()</a></span>) や、<a href="ref.pcre.php" class="link">Perl互換の正規表現</a>のサポートまであります。
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         アプリケーションが数値入力を期待している場合、
         <span class="function"><a href="function.ctype-digit.php" class="function">ctype_digit()</a></span> を使ってデータを検証するか、
         <span class="function"><a href="function.settype.php" class="function">settype()</a></span> で暗黙の型変換を行うか、
         <span class="function"><a href="function.sprintf.php" class="function">sprintf()</a></span> で数値表現を使用することを検討してみてください。
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         データベースがバインド変数をサポートしていない場合は、
         データベースに渡される数値以外のユーザー入力を
         データベース固有の文字列エスケープ関数
         (e.g.
         <span class="function"><a href="function.mysql-real-escape-string.php" class="function">mysql_real_escape_string()</a></span>,
         <span class="function"><strong>sqlite_escape_string()</strong></span> など)
         を使ってクォートしてください。
         <span class="function"><a href="function.addslashes.php" class="function">addslashes()</a></span> のような汎用関数が使える場面は、
         とても限定された環境に限られます。
         (MySQL をシングルバイト文字セットで使っていて、かつ <var class="varname">NO_BACKSLASH_ESCAPES</var> を無効にしている場合など)
         よって、この関数は使わないほうが良いです。
        </span>
       </li>
       <li class="listitem">
        <span class="simpara">
         正しい手段でも、そうでなくても、データベース固有の情報、特にスキーマに関する情報は出力してはいけません。
         <a href="security.errors.php" class="link">エラー出力</a> および <a href="ref.errorfunc.php" class="link">エラー処理およびログ関数</a>
         も参照ください。
        </span>
       </li>
      </ul>
     </p>

    <p class="simpara">
     これらのケースにおいて、スクリプトまたはサポートされている場合はデータベース自体でクエリのログをとることが有益です。
     明らかにログは破壊的な行為を防止することはできませんが、 
    攻撃されたアプリケーションを追跡する際には有効です。ログ自体は有益ではありませんが、含まれている情報は有益です。通常、より詳細なログをとる方が良いでしょう。
    </p>
   </div>
  </div><?php manual_footer($setup); ?>