Protégez vos formulaires avec des clés de formulaire

WBOY
Libérer: 2023-09-07 21:52:01
original
1389 Les gens l'ont consulté

La sécurité est un sujet brûlant. La sécurisation de votre site Web est extrêmement importante pour toute application Web. En fait, 70 % de mon temps est consacré à la sécurisation des applications. Les formulaires sont l’une des choses les plus importantes que nous devons protéger. Aujourd'hui, nous allons examiner un moyen d'empêcher la falsification de requêtes XSS (cross-site scripting) et cross-site sur vos formulaires.

Pourquoi ?

Les données POST peuvent être envoyées d'un site Web à un autre. Pourquoi est-ce mauvais ? Une scène simple...

Un utilisateur connecté à votre site Web visite un autre site Web au cours de sa session. Le site Web pourra envoyer des données POST à ​​votre site Web, par exemple en utilisant AJAX. Étant donné que l'utilisateur est connecté à votre site, d'autres sites peuvent également envoyer des données de publication vers un formulaire sécurisé qui n'est accessible que s'il est connecté.

Nous devons également protéger nos pages des attaques utilisant cURL

Comment résoudre ce problème ?

Avec les clés du formulaire ! Nous ajouterons un hachage spécial (clé de formulaire) à chaque formulaire pour garantir que les données ne sont traitées que lorsqu'elles sont envoyées depuis votre site Web. Une fois le formulaire soumis, notre script PHP validera la clé du formulaire soumis par rapport à la clé du formulaire que nous avons définie lors de la session.

Ce que nous devons faire :

  1. Ajoutez des clés de formulaire à chaque formulaire.
  2. Stockez les clés du formulaire en session.
  3. Vérifiez la clé du formulaire après avoir soumis le formulaire.

Étape 1 : Formulaire simple

Tout d’abord, nous avons besoin d’un formulaire simple pour démontrer. L'un des formulaires les plus importants que nous devons protéger est le formulaire de connexion. Les formulaires de connexion sont vulnérables aux attaques par force brute. Créez un nouveau fichier et enregistrez-le sous index.php dans votre répertoire racine Web. Ajoutez le code suivant dans le corps :

	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
	<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
	<head>
		<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
		<title>Securing forms with form keys</title>
	</head>
	<body>
		<form action="" method="post">
		<dl>
			<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>
			<dt></dt>
			<dd><input type="submit" value="Login" /></dd>
		</dl>
		</form>
	</body>
	</html>
Copier après la connexion

Nous avons maintenant une simple page XHTML avec un formulaire de connexion. Si vous souhaitez utiliser les clés de formulaire sur votre site Web, vous pouvez remplacer le script ci-dessus par votre propre page de connexion. Passons maintenant à l'action réelle.

Étape 2 : Créer la classe

Nous allons créer une classe PHP pour la clé du formulaire. Étant donné que chaque page ne peut contenir qu'une seule clé de formulaire, nous pouvons créer un singleton pour notre classe afin de garantir que notre classe est utilisée correctement. Étant donné que la création de singletons est un sujet POO plus avancé, nous ignorerons cette partie. Créez un nouveau fichier appelé formkey.class.php et placez-le dans votre répertoire racine Web. Nous devons maintenant réfléchir aux fonctionnalités dont nous avons besoin. Tout d’abord, nous avons besoin d’une fonction pour générer la clé du formulaire afin de pouvoir la mettre dans le formulaire. Placez le code suivant dans votre fichier 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;
	
	//Function to generate the form key
	private function generateKey()
	{
		
	}
}
?>
Copier après la connexion
Ci-dessus, vous voyez une classe composée de trois parties : deux variables et une fonction. Nous rendons cette fonction privée car elle ne sera utilisée que par la fonction de sortie que nous créerons plus tard. Dans ces deux variables nous stockerons les clés du formulaire. Ils sont également privés puisqu’ils ne peuvent être utilisés que par des fonctions au sein de notre classe.

Maintenant, nous devons trouver un moyen de générer la clé du formulaire. Parce que notre clé de formulaire doit être unique (sinon nous n'avons aucune sécurité), nous lions la clé à l'utilisateur en utilisant une combinaison de l'adresse IP de l'utilisateur, utilisons mt_rand() pour la rendre unique et utilisons la fonction uniqid() pour la rendre unique. plus singulier. Nous chiffrons également ces informations à l'aide de md5() pour créer une valeur de hachage unique, que nous insérons ensuite dans notre page. Parce que nous avons utilisé md5(), l'utilisateur ne peut pas voir ce que nous avons utilisé pour générer la clé. Fonctionnalité complète :

//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);
}
Copier après la connexion

Insérez le code ci-dessus dans votre fichier

formkey.class.php

. Remplacez la fonction par la nouvelle fonction. Étape 3 : Insérez la clé du formulaire dans le formulaire

Pour cette étape, nous créons une nouvelle fonction qui génère les champs HTML masqués à l'aide des clés de formulaire. Cette fonction se compose de trois étapes :

Générez des clés de formulaire à l'aide de notre fonction generateKey().
  1. Stockez la clé du formulaire dans la variable et la session $formKey.
  2. Champs HTML de sortie.
  3. Nous nommons la fonction
outputKey()

et la rendons publique puisque nous devons l'utiliser en dehors de la classe. Notre fonction appellera la fonction privée generateKey() pour générer une nouvelle clé de formulaire et la sauvegarder dans la session locale. Enfin, nous créons le code XHTML. Ajoutez maintenant le code suivant dans notre classe PHP :

//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."' />";
}
Copier après la connexion
Nous allons maintenant ajouter la clé du formulaire à notre formulaire de connexion pour le garder sécurisé. Nous devons inclure cette classe dans le fichier

index.php

. Nous devons également démarrer la session car notre classe utilise la session pour stocker les clés générées. Pour ce faire, nous ajoutons le code suivant au dessus des balises doctype et head :

<?php
//Start the session
session_start();
//Require the class
require('formkey.class.php');
//Start the class
$formKey = new formKey();
?>
Copier après la connexion
Le code ci-dessus est assez explicite. Nous démarrons la session (puisque nous stockons la clé du formulaire) et chargeons le fichier de classe PHP. Après cela, nous démarrons la classe en utilisant

new formKey()

, qui créera notre classe et la stockera dans $formKey. Il ne nous reste plus qu'à éditer le formulaire pour qu'il contienne la clé du formulaire :

<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>
Copier après la connexion

仅此而已!因为我们创建了函数 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'];
	}
}
Copier après la connexion

构造函数应始终命名为__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;
	}
}
Copier après la connexion

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!';
	}
}
Copier après la connexion

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

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

	<div><?php if($error) { echo($error); } ?></div>
Copier après la connexion

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

Protégez vos formulaires avec des clés de formulaire

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

完整代码

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

index.php


	



	
	Securing forms with form keys


	
outputKey(); ?>
Copier après la connexion

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;
		}
	}
}
?>
Copier après la connexion

结论

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

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

进一步阅读

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!