ホームページ > バックエンド開発 > PHPチュートリアル > PHPセキュアプログラミング

PHPセキュアプログラミング

WBOY
リリース: 2016-06-23 14:30:40
オリジナル
890 人が閲覧しました

PHP 应用程序中的安全性包括远程安全性和本地安全性。本文将揭示 PHP 开发人员在实现具有这两种安全性的 Web 应用程序时应该养成的习惯。
在提及安全性问题时,需要注意,除了实际的平台和操作系统安全性问题之外,您还需要确保编写安全的应用程序。在编写 PHP 应用程序时,请应用下面的七个习惯以确保应用程序具有最好的安全性:

验证输入
保护文件系统
保护数据库
保护会话数据
保护跨站点脚本(Cross-site scripting,XSS)漏洞
检验表单 post
针对跨站点请求伪造(Cross-Site Request Forgeries,CSRF)进行保护

本部分设定了隐藏,您已回复过了,以下是隐藏的内容


验证输入

在 提及安全性问题时,验证数据是您可能采用的最重要的习惯。而在提及输入时,十分简单:不要相信用户。您的用户可能十分优秀,并且大多数用户可能完全按照期 望来使用应用程序。但是,只要提供了输入的机会,也就极有可能存在非常糟糕的输入。作为一名应用程序开发人员,您必须阻止应用程序接受错误的输入。仔细考 虑用户输入的位置及正确值将使您可以构建一个健壮、安全的应用程序。

虽然后文将介绍文件系统与数据库交互,但是下面列出了适用于各种验证的一般验证提示:

使用白名单中的值
始终重新验证有限的选项
使用内置转义函数
验证正确的数据类型(如数字)
白名单中的值(White-listed value)是正确的值,与无效的黑名单值(Black-listed value)相对。两者之间的区别是,通常在进行验证时,可能值的列表或范围小于无效值的列表或范围,其中许多值可能是未知值或意外值。

在进行验证时,记住设计并验证应用程序允许使用的值通常比防止所有未知值更容易。例如,要把字段值限定为所有数字,需要编写一个确保输入全都是数字的例程。不要编写用于搜索非数字值并在找到非数字值时标记为无效的例程。

保护文件系统

2000 年 7 月,一个 Web 站点泄露了保存在 Web 服务器的文件中的客户数据。该 Web 站点的一个访问者使用 URL 查看了包含数据的文件。虽然文件被放错了位置,但是这个例子强调了针对攻击者保护文件系统的重要性。

如果 PHP 应用程序对文件进行了任意处理并且含有用户可以输入的变量数据,请仔细检查用户输入以确保用户无法对文件系统执行任何不恰当的操作。清单 1 显示了下载具有指定名的图像的 PHP 站点示例。


清单 1. 下载文件
[php]                
if ($_POST['submit'] == 'Download') {
$file = $_POST['fileName'];
header("Content-Type: application/x-octet-stream");
header("Content-Transfer-Encoding: binary");
header("Content-Disposition: attachment; filename=\"" . $file . "\";" );
$fh = fopen($file, 'r');
while (! feof($fh))
{
echo(fread($fh, 1024));
}
fclose($fh);
} else {
echo("<");
echo("title>Guard your filesystem");
    echo("

"\" method=\"post\">");
    echo("
echo(isset($_REQUEST['fileName']) ? $_REQUEST['fileName'] : '');
echo("\" />");
    echo("
");
    echo("
");
}[/php]    



正 如您所见,清单 1 中比较危险的脚本将处理 Web 服务器拥有读取权限的所有文件,包括会话目录中的文件(请参阅 “保护会话数据”),甚至还包括一些系统文件(例如 /etc/passwd)。为了进行演示,这个示例使用了一个可供用户键入文件名的文本框,但是可以在查询字符串中轻松地提供文件名。

同时配置用户输入和文件系统访问权十分危险,因此最好把应用程序设计为使用数据库和隐藏生成的文件名来避免同时配置。但是,这样做并不总是有效。清单 2 提供了验证文件名的示例例程。它将使用正则表达式以确保文件名中仅使用有效字符,并且特别检查圆点字符:..。


清单 2. 检查有效的文件名字符
[php]                    
function isValidFileName($file) {
    /* don't allow .. and allow any "word" character \ / */
    return preg_match('/^(((?:\.)(?!\.))|\w)+$/', $file);
}[/php]    

保护数据库

2008 年 4 月,美国某个州的狱政局在查询字符串中使用了 SQL 列名,因此泄露了保密数据。这次泄露允许恶意用户选择需要显示的列、提交页面并获得数据。这次泄露显示了用户如何能够以应用程序开发人员无法预料的方法执 行输入,并表明了防御 SQL 注入攻击的必要性。

清单 3 显示了运行 SQL 语句的示例脚本。在本例中,SQL 语句是允许相同攻击的动态语句。此表单的所有者可能认为表单是安全的,因为他们已经把列名限定为选择列表。但是,代码疏忽了关于表单欺骗的最后一个习惯 ? 代码将选项限定为下拉框并不意味着其他人不能够发布含有所需内容的表单(包括星号 [*])。


清单 3. 执行 SQL 语句
[php]                


SQL Injection Example


    method="post">
value=" $_POST['account_number'] : ''); ?>" />



if ($_POST['submit'] == 'Save') {
/* do the form processing */
$link = mysql_connect('hostname', 'user', 'password') or
die ('Could not connect' . mysql_error());
mysql_select_db('test', $link);

$col = $_POST['col'];

$select = "SELECT " . $col . " FROM account_data WHERE account_number = "
. $_POST['account_number'] . ";" ;
echo '

' . $select . '

';

    $result = mysql_query($select) or die('

' . mysql_error() . '

');

    echo '';
    while ($row = mysql_fetch_assoc($result)) {
        echo '';
        echo '';
        echo '';
    }
    echo '
' . $row[$col] . '
';

    mysql_close($link);
}
?>

[/php]



因此,要形成保护数据库的习惯,请尽可能避免使用动态 SQL 代码。如果无法避免动态 SQL 代码,请不要对列直接使用输入。清单 4 显示了除使用静态列外,还可以向帐户编号字段添加简单验证例程以确保输入值不是非数字值。


清单 4. 通过验证和 mysql_real_escape_string() 提供保护
[php]                


SQL Injection Example


    method="post">
value=" $_POST['account_number'] : ''); ?>" /> value="Save" name="submit" />


function isValidAccountNumber($number)
{
return is_numeric($number);
}

if ($_POST['submit'] == 'Save') {

/* 習慣 1 を覚えておいてください - データを検証してください! */
if (isset($_POST['account_number']) &&
isValidAccountNumber($_POST['account_number'])) {

/* フォーム処理を実行します */
$link = mysql_connect('hostname', 'ユーザー', 'パスワード') または
die ('接続できませんでした' .mysql_error());
mysql_select_db('test', $link);

$select = sprintf("SELECT account_number, name, address " .
" FROM account_data WHERE account_number = %s;",
mysql_real_escape_string($_POST['account_number']));
echo '

' 。 $select 。 '

';

$result = mysql_query($select) または die('

' . mysql_error() . '

');

echo '' ;
while ($row = mysql_fetch_assoc($result)) {
echo '';
echo '';
エコー '';
エコー '';
echo '';
}
echo '
' 。 $row['アカウント番号'] 。 '' 。 $row['name'] 。 '' 。 $row['アドレス'] 。 '
';

mysql_close($link);
} else {
エコー " .
「有効なアカウント番号を入力してください!
」;

}
}
?>

[/php]



本 例还展示mysql_real_escape_string() 関数の使用法。この関数は、入力を正確にフィルタリングするため、無効な文字は含まれません。 継続的に magic_quotes_gpc に依存している場合は、使用されていることに注意して、PHP V6 で削除される必要があります。まずはその使用を避け、その場合は安全な PHP アプリケーション プログラムを書き込みます。この方法を使用すると、後で異なる情報を含むテーブルに列が追加された場合に、これらの列をデータベースと組み合わせて使用​​することができます。 SQL テストはフレームワークの安全性を保護するために実行されていますが、それでも確認できない場合は、データベースのやり取りを行うためにさらにテストを実行する必要があります。

保护会话

默认この場合、PHP 内のセッション情報がタイムスケジュールに書き込まれます。
[ php]
session_start();
?>


セッション情報の保存


if ($_POST['submit'] == '保存') {
$_SESSION['userName'] = $_POST['userName'];
$_SESSION['accountNumber'] = $_POST[ 'accountNumber'];
}
?>
method="post">
< ;div>
value="" />


value=" $_POST['accountNumber'] : ''); ?>" />





[/php]



清单 6 显示了 /tmp 目录的内容。


清单 6. /tmp 目录中的会话文件
                
-rw-------  1 _www    wheel       97 Aug 18 20:00 sess_9e4233f2cd7cae35866cd8b61d9fa42b



正如您所见,在输出时(参见清单 7),会话文件以非常易读的格式包含信息。由于该文件必须可由 Web 服务器用户读写,因此会话文件可能为共享服务器中的所有用户带来严重的问题。除您之外的某个人可以编写脚本来读取这些文件,因此可以尝试从会话中取出值。


清单 7. 会话文件的内容
                
userName|s:5:"ngood";accountNumber|s:9:"123456789";


存储密码

不 管是在数据库、会话、文件系统中,还是在任何其他表单中,无论如何密码都决不能存储为纯文本。处理密码的最佳方法是将其加密存储并相互比较加密的密码。虽 然如此,在实践中人们仍然把密码存储到纯文本中。只要使用可以发送密码而非重置密码的 Web 站点,就意味着密码是存储在纯文本中或者可以获得用于解密的代码(如果加密的话)。即使是后者,也可以找到并使用解密代码。


您 可以采取两项操作来保护会话数据。第一是把您放入会话中的所有内容加密。但是正因为加密数据并不意味着绝对安全,因此请慎重采用这种方法作为保护会话的惟 一方式。备选方法是把会话数据存储在其他位置中,比方说数据库。您仍然必须确保锁定数据库,但是这种方法将解决两个问题:第一,它将把数据放到比共享文件 系统更加安全的位置;第二,它将使您的应用程序可以更轻松地跨越多个 Web 服务器,同时共享会话可以跨越多个主机。

要实现自己的会话 持久性,请参阅 PHP 中的 session_set_save_handler() 函数。使用它,您可以将会话信息存储在数据库中,也可以实现一个用于加密和解密所有数据的处理程序。清单 8 提供了实现的函数用法和函数骨架示例。您还可以在 参考资料 小节中查看如何使用数据库。


清单 8. session_set_save_handler() 函数示例
[php]                
function open($save_path, $session_name)
{
    /* custom code */
    return (true);
}

function close()
{
    /* custom code */
    return (true);
}

function read($id)
{
    /* custom code */
    return (true);
}

function write($id, $sess_data)
{
    /* custom code */
    return (true);
}

function destroy($id)
{
    /* custom code */
    return (true);
}

function gc($maxlifetime)
{
    /* custom code */
    return (true);
}

session_set_save_handler("open", "close", "read", "write", "destroy", "gc");[/php]

针对 XSS 漏洞进行保护

XSS 漏洞代表 2007 年所有归档的 Web 站点的大部分漏洞(请参阅 参考资料)。当用户能够把 HTML 代码注入到您的 Web 页面中时,就是出现了 XSS 漏洞。HTML 代码可以在脚本标记中携带 JavaScript 代码,因而只要提取页面就允许运行 JavaScript。清单 9 中的表单可以表示论坛、维基、社会网络或任何可以输入文本的其他站点。


清单 9. 输入文本的表单
[php]                


Your chance to input XSS








[/php]



リスト 10 は、XSS 攻撃を許可するフォームがどのように結果を出力できるかを示しています。


リスト 10. showResults.php
[php] gt;
echo("

次のように入力しました:

");
echo("

") ;
echo($_POST['myText']);
echo("< /p>");
?>

[/php]



リスト11 は、新しいウィンドウがポップアップして Google のホームページを開く基本的な例を示しています。 Web アプリケーションが XSS 攻撃から保護されていない場合、重大な損害が発生する可能性があります。たとえば、誰かがフィッシング目的でサイトのスタイルを模倣したリンクを追加する可能性があります (「参考文献」を参照)。


リスト11. 悪意のある入力テキストの例
[php]
[/php]



XSS 攻撃を防ぐには、変数値は出力に出力されますが、入力は htmlentities() 関数でフィルター処理する必要があります。習慣 #1: Web アプリケーションへの名前、電子メール アドレス、電話番号、請求先情報の入力において、入力データをホワイトリストの値で検証することを忘れないでください。

テキスト入力を表示するより安全なページを以下に示します。


リスト 12. より安全なフォーム
[php] ;
< cho("

次のように入力しました:

");
echo("

");
echo(htmlentities($_POST['myText']));
echo ("

");
?>

[/php]

保護無効な投稿に対して

フォームのなりすましとは、誰かが未知の投稿を適切な場所にあるフォームに変更することを意味します。フォームを偽装する最も簡単な方法は、フォームへの送信を通じてすべての値を渡す Web ページを作成することです。 Web アプリケーションはステートレスであるため、公開されるデータが指定された場所からのものであることを確認する確実な方法はありません。 IP アドレスからホスト名まですべてがスプーフィングされる可能性があります。リスト 13 は、情報を入力できる典型的なフォームを示しています。


リスト 13. テキストを処理するためのフォーム
[php] ;
< ?php
if ($_POST[' submit'] == 'Save') {
echo("

あなたのテキストを処理しています: ");
echo($_POST['myText']);
echo ("

");
}
?>

[/php]



リスト 14 は、リスト 13 のフォームに表示されるフォームに何が投稿されるかを示しています。これを試すには、フォームを Web サイトに配置し、リスト 14 のコードを HTML ドキュメントとしてデスクトップに保存します。フォームを保存したら、ブラウザで開きます。その後、データを入力してフォームを送信すると、データがどのように処理されるかを確認できます。コレクション リスト 14. データを収集するフォーム
& lt; head & gt; データを収集する & lt;/title & lt;/head & gt & gt ; 本文 & gt; フォーム action="processStuff.php" メソッド "post">
<名前="回答">
<オプション値="はい">はい





< ;/html>[/php]



表单欺骗的潜在影响是,如果拥有含下拉框、单选按钮、复选框或其他限制输入的表单,则当表单被欺骗时这些限制没有任何意义。考虑清单 15 中的代码,其中包含带有无效数据的表单。


清单 15. 带有无效数据的表单
[php]                


Collecting your data


method="post"> value="There is no way this is a valid response to a yes/no answer..." />



[/php]



思 考一下:如果拥有限制用户输入量的下拉框或单选按钮,您可能会认为不用担心验证输入的问题。毕竟,输入表单将确保用户只能输入某些数据,对吧?要限制表单 欺骗,需要进行验证以确保发布者的身份是真实的。您可以使用一种一次性使用标记,虽然这种技术仍然不能确保表单绝对安全,但是会使表单欺骗更加困难。由于 在每次调用表单时都会更改标记,因此想要成为攻击者就必须获得发送表单的实例,去掉标记,并把它放到假表单中。使用这项技术可以阻止恶意用户构建持久的 Web 表单来向应用程序发布不适当的请求。清单 16 提供了一种表单标记示例。


清单 16. 使用一次性表单标记
[php]                
session_start();
?>


SQL Injection Test



echo 'Session token=' . $_SESSION['token'];
echo '
';
echo 'Token from form=' . $_POST['token'];
echo '
';

if ($_SESSION['token'] == $_POST['token']) {
    /* cool, it's all good... create another one */

} else {
    echo '

Go away!

';
}
$token = md5(uniqid(rand(), true));
$_SESSION['token'] = $token;
?>
    method="post">

value="" />



[/php]

CSRF から保護

クロスサイト リクエスト フォージェリ (CSRF 攻撃) は、ユーザー権限を使用して実行される攻撃の結果です。 CSRF 攻撃では、ユーザーが意図しない共犯者になってしまう可能性があります。リスト 17 は、特定のアクションを実行するページの例を示しています。このページでは、Cookie からユーザーのログイン情報を検索します。 Cookie が有効である限り、Web ページはリクエストを処理します。


リスト 17. CSRF の例
[php]
[/php]



CSRF 攻撃は通常、ブラウザが画像を取得するために無意識のうちにこの URL を呼び出すため、 タグとして表示されます。ただし、画像ソースは、受信パラメータに基づいて処理される同じサイト内のページ URL にすることもできます。この タグが XSS 攻撃と組み合わされると、ユーザーは気付かずに認証情報に対して何らかのアクションを簡単に実行できるようになります。

CSRF 攻撃から保護するには、フォームの投稿を検証するときに使用されるワンタイム タグ方式を使用する必要があります。また、$_REQUEST の代わりに明示的な $_POST 変数を使用します。リスト 18 は、GET リクエストを介してページを呼び出すか、ページにフォームをポストすることによって、同じ Web ページを処理する悪い例を示しています。


リスト 18. $_REQUEST
[php] からデータを取得する > ;
if ($_REQUEST['submit'] == '保存') {
echo("

処理中ですあなたのテキスト: ");
echo(htmlentities($_REQUEST['text ']));
echo("

");
}
?>




リスト 19 は、POST にフォーム Clean page のみを使用することを示しています。


リスト 19. $_POST からのみデータを取得する
[php] body>
if ($_POST['submit'] == 'Save') {
echo("

私はテキストの処理: ");
echo(htmlentities($_POST[' text']));
echo("

");
}
?>

[/php]

結論

これらの 7 つの習慣から、悪意のある攻撃の被害者を避けるのに役立つ、より安全な PHP Web アプリケーションの作成を始めてください。他の多くの習慣と同様、これらも最初は慣れるのが難しいかもしれませんが、時間の経過とともに、それらに従うことがますます自然になります。

最初の習慣が鍵であることを覚えておいてください: 入力を検証することです。入力に無効な値が含まれていないことを確認した後、ファイル システム、データベース、セッションの保護を継続できます。最後に、PHP コードが XSS 攻撃、フォーム スプーフィング、CSRF 攻撃に耐性があることを確認してください。これらの習慣を身につければ、単純な攻撃から身を守ることができます。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のおすすめ
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート