1
0
mirror of https://github.com/php/doc-ja.git synced 2026-03-24 07:02:08 +01:00
Files
archived-doc-ja/security/filesystem.xml
2024-08-31 21:03:50 +09:00

233 lines
8.7 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: 91570644fbbe4d23e79908e1a04c4c4d003fe587 Maintainer: hirokawa Status: ready -->
<chapter xml:id="security.filesystem" xmlns="http://docbook.org/ns/docbook">
<title>ファイルシステムのセキュリティ</title>
<simpara>
<acronym>PHP</acronym> は、ファイルおよびディレクトリ毎に権限を設定する多くのサーバーシ
ステム上に組み込まれたセキュリティを提供します。これにより、ファイ
ルシステム内のファイルを読み込み可能に制御することが可能になります。
全てのファイルは世界中から読み込み可能であり、このファイルシステム
にアクセスした全てのユーザーから読み込まれても安全であることを確認す
る必要があります。
</simpara>
<simpara>
<acronym>PHP</acronym>は、ファイルシステムにユーザーレベルのアクセスを許可するように設
計されているため、<acronym>PHP</acronym>スクリプトから <filename>/etc/passwd</filename> のようなシステム
ファイルを読み込み可能としたり、イーサネット接続を修正したり、巨大
なプリンタジョブを出力したりすることができます。これから明らかにわ
かることですが、読み書きするファイルを適切に設定する必要があります。
</simpara>
<simpara>
各自のホームディレクトリにあるファイルを削除する次のスクリプトを見
てみましょう。これは、ファイル管理用にWebインターフェイスを使用す
る場合に通常生じるような設定を仮定しています。この場合、Apacheユー
ザはそのユーザーのホームディレクトリにあるファイルを削除可能です。
</simpara>
<para>
<example>
<title>甘い変数の確認から生じるリスク</title>
<programlisting role="php">
<![CDATA[
<?php
// ユーザーのホームディレクトリからファイルを削除する
$username = $_POST['user_submitted_name'];
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
unlink("$homedir/$userfile");
echo "ファイルは削除されました!";
?>
]]>
</programlisting>
</example>
username および filename はユーザーフォームから投稿可能であるため、別の
username および filename を投稿して
削除すべきではない削除することが可能となります。この場合、
他の何らかの形式の認証を使用するべきです。投稿された変数が、
<literal>"../etc/"</literal><literal>"passwd"</literal> であった場合について考えてみましょう。簡単
なコードを以下に示します。
<example>
<title>... ファイルシステムへの攻撃</title>
<programlisting role="php">
<![CDATA[
<?php
// 外部からPHPユーザーがアクセス可能なハードドライブを削除します。PHPが
// ルートのアクセス権限を有している場合、
$username = $_POST['user_submitted_name']; // "../etc"
$userfile = $_POST['user_submitted_filename']; // "passwd"
$homedir = "/home/$username"; // "/home/../etc"
unlink("$homedir/$userfile"); // "/home/../etc/passwd"
echo "ファイルは削除されました!";
?>
]]>
</programlisting>
</example>
こうした問題を防止するために必要な重要なチェック手段として以下の2
種類のものがあります。
<itemizedlist>
<listitem>
<simpara>
<acronym>PHP</acronym> Webユーザーバイナリに制限された権限のみを許可する。
</simpara>
</listitem>
<listitem>
<simpara>
投稿された全ての変数を確認する。
</simpara>
</listitem>
</itemizedlist>
以下に改良されたスクリプトを示します。
<example>
<title>より安全なファイル名の確認</title>
<programlisting role="php">
<![CDATA[
<?php
// PHPユーザーがアクセス可能なハードドライブからファイルを削除する。
$username = $_SERVER['REMOTE_USER']; // 認証機構を使用する
$userfile = basename($_POST['user_submitted_filename']);
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (file_exists($filepath) && unlink($filepath)) {
$logstring = "$filepath を削除しました\n";
} else {
$logstring = "$filepath の削除に失敗しました\n";
}
j
$fp = fopen("/home/logging/filedelete.log", "a");
fwrite($fp, $logstring);
fclose($fp);
echo htmlentities($logstring, ENT_QUOTES);
?>
]]>
</programlisting>
</example>
しかし、これでも、傷口を塞いだことにはなりません。
ユーザーが自分用のユーザーログインを作成することをあなたの認証システムが
許可しており、ユーザーが <literal>"../etc/"</literal> へのログインを選択した場合、システム
はまたも公開されてしまいます。このため、よりカスタマイズされたチェッ
クを行なう方がよいでしょう。
<example>
<title>より安全なファイル名の確認</title>
<programlisting role="php">
<![CDATA[
<?php
$username = $_SERVER['REMOTE_USER']; // 認証機構を使用する
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $userfile)) {
die("Bad username/filename");
}
// etc.
?>
]]>
</programlisting>
</example>
</para>
<para>
オペレーティングシステムによって、注意するべきファイルは大きく変化し
ます。これらには、デバイスエントリ(<filename>/dev/</filename> または <filename>/dev/</filename>)、設定ファイル(<filename>/etc/</filename> ファイルおよび <literal>.ini</literal> ファイル)、よく知られたファイル保存領
域 (<filename>/home/</filename><filename>My Documents</filename>)等が含まれます。このため、明示的に許可す
るもの以外の全てを禁止する方針とする方が通常はより簡単です。
</para>
<sect1 xml:id="security.filesystem.nullbytes">
<title>Null バイト関連の問題</title>
<simpara>
<acronym>PHP</acronym> はファイルシステム関連の操作に C 言語の関数を使用しているので、
null バイトの処理を予期せぬかたちで行うことがあります。
C 言語では null バイトは文字列の終端を表すので、
null バイトを含む文字列があった場合に
null バイト以降の内容は文字列として処理されません。
以下に、この問題に関する脆弱性を含むコード例を示します。
</simpara>
<example>
<title>null バイトに対して脆弱なスクリプト</title>
<programlisting role="php">
<![CDATA[
<?php
$file = $_GET['file']; // ここで "../../etc/passwd\0" が渡されたとします
if (file_exists('/home/wwwrun/' . $file . '.php')) {
// file_exists は true を返します。これは、ファイル /home/wwwrun/../../etc/passwd が存在するからです
include '/home/wwwrun/'.$file.'.php';
// ファイル /etc/passwd がインクルードされてしまいます
}
?>
]]>
</programlisting>
</example>
<para>
したがって、ファイルシステム操作で使用する「汚染された」文字列は、
つねに適切に検証しなければなりません。
先ほどの例を改良したものを示します。
</para>
<example>
<title>入力を適切に検証する例</title>
<programlisting role="php">
<![CDATA[
<?php
$file = $_GET['file'];
// 値として与えられる可能性のある、有効な値の一覧を作成します
switch ($file) {
case 'main':
case 'foo':
case 'bar':
include '/home/wwwrun/include/' . $file . '.php';
break;
default:
include '/home/wwwrun/include/main.php';
}
?>
]]>
</programlisting>
</example>
</sect1>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->