1、目的:每次表单POST提交(ajax的POST也适用)过来数据,都必须校验formhash参数是否和服务器端的一致,不一致说明重复提交或者 跨站攻击提交csrf
2、原理:参照了 KPPW 的formhash生成和校验示例。将formhash的生成写入基类构造函数,每次登陆用户操作数据,都生成hash并进行比较。
(用户未登录状态,也可以校验formhash,可以让你的网站免于 遭受 无状态脚本提交数据 攻击)
3、formHash的生成代码:注意 这里的formhash一般都是每个用户均不相同,且每个用户的formhash值在几个月以内都是不会变更的。
<?php
/*
* 再次封装的BaseController基类 --by xzz 2018/02/07
* 包括:表单formhash校验原理如下:
* 将formhash放在构造函数里,GET访问页面生成formhash并渲染在页面,用户提交表单,POST经过构造函数再一次生成一样的formhash
*/
namespace Admin\Controller;
user Think\Controller; class BaseController extends Controller {
public function __construct(){
parent::__construct();
define("FORMHASH", self::formhash());
@session_start();
}
/* 生成表单随机码,防止XSS攻击,无需同步存储于内存缓存(redis/Mencached)中
* 返回6位随机码
*/
static function formhash() {
$uid = null;
$username = null;
if (isset ( $_SESSION ['uid'] )) {
$uid = $_SESSION ['uid'];
}
if (isset ( $_SESSION ['username'] )) {
$username = $_SESSION ['username'];
}
return substr ( md5 ( substr ( time (), 0, - 7 ) . $uid . $username ), - 6 );
}
/*
* formhash校验,传过来的formhash和提交动作再次生成的FORMHASH变量一致,校验通过;否则重复提交
*/
static function submitcheck($formhash, $return_json = false) {if (! empty ( $formhash ) && $_SERVER ['REQUEST_METHOD'] == 'POST') {
if ((empty ( $_SERVER ['HTTP_REFERER'] ) || preg_replace ( "/https?:\/\/([^\:\/]+).*/i", "\\1", $_SERVER ['HTTP_REFERER'] ) == preg_replace ( "/([^\:]+).*/", "\\1", $_SERVER ['HTTP_HOST'] )) && $formhash == FORMHASH) {
return true;
} elseif ($return_json == true) {
return false;
}
} else {
return false;
}
}
/* 发送邮件类 -xzz 2018/02/06 */
public function add2(){ //校验用户是否登录 // do user is login ? header("content-type:text/html;charset=utf-8");
if(IS_POST){
var_dump($_POST);
if(self::submitcheck(trim($_POST['formhash']))){
if(SendMail($_POST['mail'],$_POST['title'],$_POST['content']))
$this->success('发送成功!');
else
$this->error('发送失败');
}else{
exit("formhash错误");
}
}elseif(IS_GET){
$this->assign("FORMHASH",FORMHASH);$this->display();
}
}
}
4、代码解读:
a. 假设 我们访问上面 add2 页面,首先 输出变量$FORMHASH 并展示在页面隐藏域;
b. post方式提交后,校验登录态->校验formhash->do what u want to do ..
c. 第二步,校验formhash:提交的formhash 和 再次生成formhash比较,还看不懂 请再尝试看5遍这个方法 【submitcheck()】。
5、结束。
|
请发表评论