フォームキーを使用してフォームを保護する

WBOY
リリース: 2023-09-07 21:52:01
オリジナル
1412 人が閲覧しました

安全性はホットな話題です。 Web サイトのセキュリティを確保することは、どの Web アプリケーションにとっても非常に重要です。実際、私の時間の 70% はアプリケーションのセキュリティ保護に費やされています。私たちが守らなければならない最も重要なものの 1 つはフォームです。今日は、フォーム上の XSS (クロスサイト スクリプティング) とクロスサイト リクエスト フォージェリを防ぐ方法を確認します。

###なぜ?

POST データは、ある Web サイトから別の Web サイトに送信できます。なぜこれが悪いのでしょうか?単純なシナリオ...

サイトにログインしているユーザーが、セッション中に別のサイトにアクセスします。 Web サイトは、AJAX などを使用して POST データを Web サイトに送信できるようになります。ユーザーはサイトにログインしているため、他のサイトも、ログインしている場合にのみアクセスできる安全なフォームに投稿データを送信できます。

cURL を使用した攻撃からページを保護する必要もあります

この問題をどうやって解決すればよいでしょうか?

フォームキー付き! Web サイトから送信された場合にのみデータが処理されるように、特別なハッシュ (フォーム キー) を各フォームに追加します。フォームが送信されると、PHP スクリプトは送信されたフォーム キーをセッションで設定したフォーム キーと照合して検証します。

私たちがしなければならないこと:

    各フォームにフォーム キーを追加します。
  1. フォームキーをセッションに保存します。
  2. フォームを送信した後、フォームキーを確認してください。
ステップ 1: 単純なフォーム

まず、デモンストレーション用の簡単なフォームが必要です。保護する必要がある最も重要なフォームの 1 つはログイン フォームです。ログイン フォームはブルート フォース攻撃に対して脆弱です。新しいファイルを作成し、Web ルート ディレクトリに

index.php という名前で保存します。次のコードを本文に追加します: リーリー

これで、ログイン フォームを備えた単純な XHTML ページが完成しました。 Web サイトでフォーム キーを使用したい場合は、上記のスクリプトを独自のログイン ページに置き換えることができます。さて、実際のアクションに移りましょう。

ステップ 2: クラスを作成する

フォームキーのPHPクラスを作成します。各ページには

1 つの フォーム キーしか含めることができないため、クラスが正しく使用されることを保証するためにクラスのシングルトンを作成できます。シングルトンの作成はより高度な OOP トピックであるため、この部分は省略します。 formkey.class.php という名前の新しいファイルを作成し、Web ルート ディレクトリに配置します。次に、必要な機能について考えなければなりません。まず、フォームに入力できるようにフォーム キーを生成する関数が必要です。次のコードを PHP ファイルに配置します: リーリー

上には、2 つの変数と 1 つの関数という 3 つの部分からなるクラスが表示されています。この関数は後で作成する出力関数でのみ使用されるため、プライベートにします。これら 2 つの変数にフォーム キーを保存します。これらはクラス内の関数でのみ使用できるため、プライベートでもあります。

次に、フォームキーを生成する方法を見つける必要があります。フォームキーは一意である必要があるため (そうしないとセキュリティがありません)、ユーザーの IP アドレスの組み合わせを使用してキーをユーザーにバインドし、mt_rand() を使用して一意にし、uniqid() 関数を使用してキーを一意にします。よりユニークです。また、md5() を使用してこの情報を暗号化し、一意のハッシュ値を作成し、それをページに挿入します。 md5() を使用したため、ユーザーはキーの生成に何を使用したかを見ることができません。関数全体:

リーリー

上記のコードを

formkey.class.php ファイルに挿入します。関数を新しい関数に置き換えます。

ステップ 3: フォーム キーをフォームに挿入します

このステップでは、フォーム キーを使用して非表示の HTML フィールドを出力する新しい関数を作成します。この機能は 3 つのステップで構成されます:

    generateKey() 関数を使用してフォーム キーを生成します。
  1. フォーム キーを $formKey 変数とセッションに保存します。
  2. 出力 HTML フィールド。
関数はクラス外で使用する必要があるため、関数

outputKey() と名前を付けてパブリックにしました。この関数はプライベート関数 generateKey() を呼び出して新しいフォーム キーを生成し、ローカル セッションに保存します。最後に、XHTML コードを作成します。次に、PHP クラスに次のコードを追加します。 リーリー 次に、ログイン フォームを安全に保つためにフォーム キーをログイン フォームに追加します。このクラスを

index.php

ファイルに含める必要があります。また、クラスは生成されたキーを保存するためにセッションを使用するため、セッションを開始する必要があります。これを行うには、doctype タグと head タグの上に次のコードを追加します。 リーリー 上記のコードは一目瞭然です。セッションを開始し (フォーム キーを保存しているため)、PHP クラス ファイルをロードします。その後、new formKey()

を使用してクラスを開始します。これにより、クラスが作成され、

$formKey に保存されます。ここで、フォーム キーを含むようにフォームを編集するだけです:

<form action="" method="post">
<dl>
	<?php $formKey->outputKey(); ?>
	<dt><label for="username">Username:</label></dt>
	<dd><input type="text" name="username" id="username" /></dd>
	<dt><label for="username">Password:</label></dt>
	<dd>input type="password" name="password" id="password" /></dd>
<dl>
</form>
ログイン後にコピー

仅此而已!因为我们创建了函数 outputKey(),所以我们只需将它包含在表单中即可。我们可以在每个表单中使用表单键,只需添加 outputKey(); ?> 现在只需查看网页的源代码,您就可以看到表单上附加了一个表单密钥。剩下的唯一步骤是验证请求。

第 4 步:验证

我们不会验证整个表单;只有表单键。验证表单是基本的 PHP 操作,并且可以在网络上找到教程。让我们验证表单密钥。因为我们的“generateKey”函数会覆盖会话值,所以我们向 PHP 类添加一个构造函数。创建(或构造)我们的类时将调用构造函数。在我们创建新密钥之前,构造函数会将前一个密钥存储在类中;所以我们将始终拥有以前的表单密钥来验证我们的表单。如果我们不这样做,我们将无法验证表单密钥。将以下 PHP 函数添加到您的类中:

//The constructor stores the form key (if one exists) in our class variable.
function __construct()
{
	//We need the previous key so we store it
	if(isset($_SESSION['form_key']))
	{
		$this->old_formKey = $_SESSION['form_key'];
	}
}
ログイン後にコピー

构造函数应始终命名为__construct()。当调用构造函数时,我们检查是否设置了会话,如果是,我们将其本地存储在 old_formKey 变量中。

现在我们可以验证表单密钥了。我们在类中创建一个基本函数来验证表单密钥。这个函数也应该是公共的,因为我们将在类之外使用它。该函数将根据表单键的存储值验证表单键的 POST 值。将此函数添加到 PHP 类中:

//Function that validated the form key POST data
public function validate()
{
	//We use the old formKey and not the new generated version
	if($_POST['form_key'] == $this->old_formKey)
	{
		//The key is valid, return true.
		return true;
	}
	else
	{
		//The key is invalid, return false.
		return false;
	}
}
ログイン後にコピー

index.php中,我们使用刚刚在类中创建的函数来验证表单密钥。当然,我们仅在 POST 请求后进行验证。在 $formKey = new formKey(); 后添加以下代码

$error = 'No error';

//Is request?
if($_SERVER['REQUEST_METHOD'] == 'post')
{
	//Validate the form key
	if(!isset($_POST['form_key']) || !$formKey->validate())
	{
		//Form key is invalid, show an error
		$error = 'Form key error!';
	}
	else
	{
		//Do the rest of your validation here
		$error = 'No form key error!';
	}
}
ログイン後にコピー

我们创建了一个变量$error来存储我们的错误消息。如果已发送 POST 请求,我们将使用 $formKey->validate() 验证表单密钥。如果返回 false,则表单键无效,并且我们会显示错误。请注意,我们仅验证表单密钥 - 您需要自己验证表单的其余部分。

在 HTML 中,您可以放置​​以下代码来显示错误消息:

	<div><?php if($error) { echo($error); } ?></div>
ログイン後にコピー

这将回显 $error 变量(如果已设置)。

フォームキーを使用してフォームを保護する

如果您启动服务器并转到index.php,您将看到我们的表单和消息“无错误”。当您提交表单时,您将看到消息“无表单键错误”,因为它是有效的 POST 请求。现在尝试重新加载页面并在浏览器请求再次发送 POST 数据时接受。您将看到我们的脚本触发了一条错误消息:“表单键错误!”现在,您的表单可以免受来自其他网站的输入和页面重新加载错误的影响!刷新后也会显示该错误,因为我们提交表单后生成了新的表单密钥。这很好,因为现在用户不会意外地将表单发布两次。

完整代码

以下是完整的 PHP 和 HTML 代码:

index.php


	



	
	Securing forms with form keys


	
outputKey(); ?>
ログイン後にコピー

fomrkey.class.php

<?php

//You can of course choose any name for your class or integrate it in something like a functions or base class
class formKey
{
	//Here we store the generated form key
	private $formKey;
	
	//Here we store the old form key (more info at step 4)
	private $old_formKey;
	
	//The constructor stores the form key (if one excists) in our class variable
	function __construct()
	{
		//We need the previous key so we store it
		if(isset($_SESSION['form_key']))
		{
			$this->old_formKey = $_SESSION['form_key'];
		}
	}

	//Function to generate the form key
	private function generateKey()
	{
		//Get the IP-address of the user
		$ip = $_SERVER['REMOTE_ADDR'];
		
		//We use mt_rand() instead of rand() because it is better for generating random numbers.
		//We use 'true' to get a longer string.
		//See http://www.php.net/mt_rand for a precise description of the function and more examples.
		$uniqid = uniqid(mt_rand(), true);
		
		//Return the hash
		return md5($ip . $uniqid);
	}

	
	//Function to output the form key
	public function outputKey()
	{
		//Generate the key and store it inside the class
		$this->formKey = $this->generateKey();
		//Store the form key in the session
		$_SESSION['form_key'] = $this->formKey;
		
		//Output the form key
		echo "<input type='hidden' name='form_key' id='form_key' value='".$this->formKey."' />";
	}

	
	//Function that validated the form key POST data
	public function validate()
	{
		//We use the old formKey and not the new generated version
		if($_POST['form_key'] == $this->old_formKey)
		{
			//The key is valid, return true.
			return true;
		}
		else
		{
			//The key is invalid, return false.
			return false;
		}
	}
}
?>
ログイン後にコピー

结论

将此代码添加到您网站上的每个重要表单中将显着提高表单的安全性。它甚至会停止刷新问题,正如我们在步骤 4 中看到的那样。由于表单密钥仅对一个请求有效,因此不可能进行双重发布。

这是我的第一个教程,希望您喜欢它并使用它来提高您的安全性!请通过评论让我知道您的想法。有更好的方法吗?让我们知道。

进一步阅读

  • WordPress 还使用表单键(将其命名为 Nonce):Wordpress Nonce
  • 编写安全 PHP 应用程序的七个习惯
  • 在 Twitter 上关注我们,或订阅 NETTUTS RSS Feed 以获取更多日常 Web 开发教程和文章。

以上がフォームキーを使用してフォームを保護するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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