我将遵循本教程,尝试制作一个PHP表单密钥验证脚本。由于某些原因,我的构造函数没有为$old_formKey
变量创建值。
教程提到Singleton更安全,但它没有深入到实现中。我知道Java面向对象编程的基本原理,但对PHP面向对象编程原理了解不多。我只是想通过使类变量为静态来扩展它——我认为这就是我的问题所在。然而,当调用我的构造函数时,它应该给$old_formKey
$_POST[form_key]
"if isset()"的值
我无法让构造函数将旧formKey的值放在所需的变量空间中。
所以这就是我遇到问题的地方。
<?php
class formKey
{
private static $formKey;
private static $old_formKey;
public function validate()
{
echo $_POST['form_key'];
//We use the old formKey and not the new generated version
if($_POST['form_key'] == $old_formKey) {
//The key is valid, return true.
return true;
}
else {
//The key is invalid, return false.
return false;
}
}
}
?>
我的formKey验证脚本看起来像这样。
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
//Validate the form key
if(!isset($_POST['form_key']) || !$formKey->validate()) {
//Form key is invalid, show an error
$error_msg = 'Security Error.';
$die = "die";
}
else {
/*continue validation*/
}
}
?>
脚本返回错误消息,然后显示它,因为$old_formKey
在validate()
方法内部从未给定值。
由于某些原因,我无法从validate()
方法中修改类变量。很抱歉,如果以前已经解决过这个问题。我真的看了看却找不到!
function __construct()
{
//We need the previous key so we store it
if(isset($_SESSION['form_key']))
{
self::$old_formKey = $_SESSION['form_key'];
}
}
对不起,我忘了包含我的构造函数。它已添加到上面。
是否扩展formKey类?
class B extend formKey{
public function validate($formKey, $oldFormKey)
{
return $formKey == $oldFormKey;
}
}
像这样称呼
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$oldFormKey = "abc";
$formKey = new B();
if (!isset($_POST['form_key'])
|| !$formKey->validate($_POST['form_key'], $oldFormKey)) {
//Form key is invalid, show an error
$error_msg = 'Security Error.';
$die = "die";
} else {
//continue validation
}
}
它可能对您有用,而且,我很好奇"由于某种原因,我无法从validate()方法中修改类变量"
要使变量通过对象(__construct()
到validate()
),您需要在类作用域中分配它们,然后从同一作用域调用它们:
class formKey
{
private $formKey,
$old_formKey;
public function __construct()
{
// I just set to false as default
$this->old_formKey = false;
// Assign here
if(isset($_SESSION['form_key']))
$this->old_formKey = $_SESSION['form_key'];
}
public function validate()
{
// Use $this-> to recall it from the construct
return ($_POST['form_key'] == $this->old_formKey);
}
}
正如我作为评论所写的那样,你提到的教程(如Dagon所写)已经过时,其次是完全伪造的。
这与验证表单无关,本主题的重点是识别表单。
您生成一个唯一的标识符密钥,该密钥由服务器(本例为PHP)生成,以便跟踪表单状态。如果唯一密钥发生变化,与服务器存储的键不同,则表单可能会在无法识别的地方被拒绝(这并不意味着表单无效)。
本教程使用了面向对象的方法,所以我会尊重这一点。以下步骤正在进行
- 生成一个唯一的密钥并将其存储在用户(或访问者,取决于此处的术语;从现在起,我将把它们描述为
users
)SESSION
- 以窗体形式输出键
-
在提交时识别表单,并将(未识别)密钥与存储在
SESSION
中的4.1.当身份符合时验证表单
4.2.如果无法识别,则拒绝表单
从生成实例开始,类的布局非常简单,它不需要是单例,也不应该静态地包含任何。
类的基础可以分为三个部分,创建、存储和识别。
class FormIdentifier {
const CONTAINER_KEYSPACE = 'form_keys';
/**
* Generate a key
* @return string
*/
public function generateKey() {
// you can use any complicated mumbo-jumbo to generate a key instead
$key = uniqid();
// store it
$this->storeKey($key);
// return for use, e.g. in a form
return $key;
}
/**
* Store a key into users session
*/
protected function storeKey($key) {
session_start();
$_SESSION[self::CONTAINER_KEYSPACE][] = $key;
session_write_close();
}
/**
* Identifies a key against the keys in the users stored session.
* @return true if identified, false otherwise
*/
public function identifyKey($key) {
session_start();
if( ($found = search_array($key, $_SESSION[self::CONTAINER_KEYSPACE])) !== false ) {
unset($_SESSION[self::CONTAINER_KEYSPACE][$found]);
session_write_close();
return true;
}
session_write_close();
return false;
}
}
要生成密钥,我们不会将HTML输出与类和方法混合使用。FormIdentifier
的目的只是创建,存储和识别表单密钥,而不是其他。因此,在表单中使用的示例
<form>
<input type="hidden" name="form_key" value="<?php (new FormIdentifier)->generateKey() ?>" />
</form>
您也可以将返回的密钥存储在某个地方,并以不同的方式使用它,无论您需要什么。
<?php $key = (new FormIdentifier)->generateKey(); ?>
<input type="hidden" name="form_key" value="<?= $key ?>" />
要识别(并最终验证)表单,您可以指定密钥发布的表单,并将其与存储在用户会话中的密钥进行比较。这里使用的结构最有可能向用户显示密钥不匹配的通知。这完全取决于您希望如何实现这一步骤,您可以将其封装在一个函数中,甚至可以将其与类FormIdentifier
组合。
/**
* Simple if/else, a POST was made so act on `form_key`s presence only
*/
if($_POST && array_key_exists('form_key', $_POST)) {
if((new FormIdentifier)->identifyKey($_POST['form_key')) {
// identity was verified
// validate your form
$fv = new FormValidator($_POST);
// or whatever ..
$fv->validate();
}
else {
// unable to determine identity
// do something silly ..
}
}
一些注意事项
- 提供的代码均未经过测试
- 重要的是要意识到,演示逻辑应该与这里所做的所有步骤分开
- 停止阅读两年以上的教程!如果作者不介意更新内容,那么也不介意阅读