>  기사  >  백엔드 개발  >  양식 키를 사용하여 양식 보호

양식 키를 사용하여 양식 보호

WBOY
WBOY원래의
2023-09-07 21:49:141291검색

안전이 화두입니다. 웹사이트 보안은 모든 웹 애플리케이션에 매우 중요합니다. 실제로 내 시간의 70%는 애플리케이션 보안에 사용됩니다. 우리가 지켜야 할 가장 중요한 것 중 하나는 형태입니다. 오늘은 양식에서 XSS(교차 사이트 스크립팅) 및 교차 사이트 요청 위조를 방지하는 방법을 검토하겠습니다.

왜?

POST 데이터는 한 웹사이트에서 다른 웹사이트로 전송될 수 있습니다. 이것이 왜 나쁜가요? 간단한 장면...

귀하의 웹사이트에 로그인한 사용자가 세션 중에 다른 웹사이트를 방문합니다. 웹사이트는 예를 들어 AJAX를 사용하여 POST 데이터를 웹사이트로 보낼 수 있습니다. 사용자가 귀하의 사이트에 로그인되어 있기 때문에 다른 사이트에서도 로그인한 경우에만 액세스할 수 있는 보안 양식으로 게시물 데이터를 보낼 수 있습니다.

또한 cURL을 사용한 공격으로부터 페이지를 보호해야 합니다

이 문제를 어떻게 해결하나요?

양식 키 포함! 귀하의 웹사이트에서 전송될 때만 데이터가 처리되도록 각 양식에 특수 해시(양식 키)를 추가합니다. 양식이 제출되면 PHP 스크립트는 세션에서 설정한 양식 키와 비교하여 제출된 양식 키의 유효성을 검사합니다.

우리가 해야 할 일:

  1. 각 양식에 양식 키를 추가하세요.
  2. 세션에 양식 키를 저장합니다.
  3. 양식을 제출한 후 양식 키를 확인하세요.

1단계: 간단한 양식

먼저 시연할 간단한 양식이 필요합니다. 우리가 보호해야 할 가장 중요한 양식 중 하나는 로그인 양식입니다. 로그인 양식은 무차별 공격에 취약합니다. 새 파일을 만들고 웹 루트 디렉터리에 index.php로 저장합니다. 본문에 다음 코드를 추가하세요.

으아아아

이제 로그인 양식이 포함된 간단한 XHTML 페이지가 생겼습니다. 웹사이트에서 양식 키를 사용하려면 위 스크립트를 자신의 로그인 페이지로 바꿀 수 있습니다. 이제 실제 동작으로 넘어가겠습니다.

2단계: 수업 만들기

양식 키에 대한 PHP 클래스를 생성하겠습니다. 각 페이지에는 하나의 양식 키만 포함될 수 있으므로 클래스가 올바르게 사용되도록 클래스에 대한 싱글톤을 만들 수 있습니다. 싱글톤을 만드는 것은 좀 더 고급 OOP 주제이므로 이 부분은 건너뛰겠습니다. formkey.class.php라는 새 파일을 만들어 웹 루트 디렉터리에 넣습니다. 이제 우리에게 필요한 기능에 대해 생각해야 합니다. 먼저, 양식에 넣을 수 있도록 양식 키를 생성하는 함수가 필요합니다. PHP 파일에 다음 코드를 배치하세요:

으아아아

위에서는 두 개의 변수와 함수라는 세 부분으로 구성된 클래스를 볼 수 있습니다. 이 함수는 나중에 생성할 출력 함수에서만 사용되므로 비공개로 만듭니다. 이 두 변수에 양식 키를 저장합니다. 또한 클래스 내의 함수에서만 사용할 수 있으므로 비공개입니다.

이제 양식 키를 생성하는 방법을 찾아야 합니다. 양식 키는 고유해야 하기 때문에(그렇지 않으면 보안이 적용되지 않음) 사용자의 IP 주소 조합을 사용하여 사용자에게 키를 바인딩하고 mt_rand()를 사용하여 키를 고유하게 만들고 uniqid() 함수를 사용합니다. 더 독특하게 만들려고요. 또한 md5()를 사용하여 이 정보를 암호화하여 고유한 해시 값을 생성한 다음 이를 페이지에 삽입합니다. md5()를 사용했기 때문에 사용자는 키를 생성하는 데 사용한 내용을 볼 수 없습니다. 전체 기능:

으아아아

위 코드를 formkey.class.php 파일에 삽입하세요. 해당 기능을 새로운 기능으로 교체하세요.

3단계: 양식에 양식 키 삽입

이 단계에서는 양식 키를 사용하여 숨겨진 HTML 필드를 출력하는 새로운 함수를 만듭니다. 이 기능은 세 단계로 구성됩니다:

  1. generateKey() 함수를 사용하여 양식 키를 생성합니다.
  2. $formKey 변수와 세션에 양식 키를 저장합니다.
  3. 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.