PHP防止站外表单跨站提交的几种办法详解

Posted zhoupenghui168

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP防止站外表单跨站提交的几种办法详解相关的知识,希望对你有一定的参考价值。

1.原理

功击者自己伪造了一个和网站一样的表单,然后在他自己站内或别处,向网站提交表单数据,给安全造成了问题

2.解决方式

(1).传统的浅层阻止

$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : NULL;
$host = $_SERVER['HTTP_HOST'];
echo "提交的地址:" . $referer;
echo "<br/>本站域名:" . $host
if (substr($referer, 7, strlen($host) != $host)) 
    echo "非法操作";
 else 
    echo "合法操作";

(2).加密令牌

生成一个随机串,比如char_str , 然后随表单生成一个隐藏域<input type="hidden" value="char_str " name="char_str " /> 并把这个值保存到服务器的 SESSION上,等到用户提交后,检查这个表单值和SESSION对比,不符就阻止。不过,这个方式如果用户多的话,会造成大量的SESSION,消耗服务器资源, 不推荐。一种优化的方式是把这个口令放在CACHE中,但是因为缓存会不定时清除,所以也有问题

(3).加密方式(推荐)

分为 单向加密可逆向加密:就是生成一个随机且变换频繁加密字符串(可逆和不可逆),跟方式2一样放在表单中,等到表单提交后检查。这个随机字符串如果和当前用户身份相关联的话,那么攻击者伪造请求会比较麻烦,对付伪造跨站请求的办法是在表单里加入一个叫.crumb的随机串;而facebook也有类似的解决办法,它的表单里常常会有post_form_id和fb_dtsg。这种方式中,字符串的有效时间要设置好,太短了用户体验不好,比如好不容易写好文章提交,令牌却到期了,不得不重写。因此,TTL过期时间应可设置。下面是一个不可逆的验证方式
class Token
    CONST KEY = "secret-salt";
    static $ttl = 3; //$ttl表示这个随机串的有效时间(秒),很重要

    //加密
    public function get($uid, $action = -1)
        $i = ceil(time() / self::$ttl);
        return substr(self::challenge($i . $action . $uid), -12, 10);
    

    //解密
    public function verify($uid, $crumb, $action = -1)
        $i = ceil(time() / self::$ttl);
        if(substr(self::challenge($i . $action . $uid), -12, 10) == $crumb ||
            substr(self::challenge(($i - 1) . $action . $uid), -12, 10) == $crumb)
            return true;
        return false;
    

    //内部私有加密函数
    private static function challenge($data) 
        return hash_hmac('md5', $data, self::KEY);
    
使用:
在表单中插入一个隐藏的随机串crumb,其中,$uid可以是会员的ID,这样就有独立性
<input type ="hidden" name = "crumb" value= "<?php echo  Token::get($uid) ?>">
在PHP服务器接收端,这样检验。默认下这个字串的有效期是7200秒
<?php
if(Token::verify($uid, $_POST['token']))
//按照正常流程处理表单
else
//校验失败,错误提示流程
上面的就是不可逆的加密方式
有时,还希望在表单中加入私密数据,跟随用户表单加密串一直生成,在前端不被用户看到,这样就可以用到可解密的函数,生成的字串在PHP端解密,起到2个作用:
(1).得到私密数据
(2).验证表单来源的合法性

php防止表单重复提交的几种方法

  1. 引言

为什么要避免form表单被重复提交呢?因为不想让服务器重复处理没必要的数据,同时也是避免数据库产生重复的数据,避免表单重复提交也是让网站更安全的一种表现
  1. 什么情况下会导致表单重复提交

  • 点击提交按钮两次

  • 点击刷新按钮

  • 使用浏览器后退按钮重复之前的操作,导致重复提交表单

  • 使用浏览器历史记录重复提交表单

  • 浏览器重复的HTTP请求

  • 网页被恶意刷新

  1. 后端防止表单重复提交的几种解决办法

3.1原理

在from表单提交的时候,用户网络或者设备原因,有时候会导致表单的重复提交,如果数据库没有做好防范的话,容易产生不需要的数据记录,其实 一种方法是:可以在提交的时候解决这个问题,在数据库添加一个字段,每次提交上去的时候去随机修改这个字段,每次在提交操作之前去比对这个值,如果相同就允许提交,不同就不允许表单提交; 第二种方法是:服务器返回表单页面时,会先生成一个subToken保存于session,并把该subToen传给表单页面。当表单提交时会带上subToken,服务器获取表单信息判断session保存的subToken和表单提交subToken是否一致。若不一致或session的subToken为空或表单未携带subToken则不通过。首次提交表单时session的subToken与表单携带的subToken一致走正常流程,然后拦截器内会删除session保存的subToken。当再次提交表单时由于session的subToken为空则不通过。从而实现了防止表单重复提交

3.2 解决办法

(1).利用js设置按钮点击后变成灰色

点击完按钮之后变成灰色就不能点击了,用户需要再次提交表单的话就要刷新页面之后重新填写数据再提交了
<p>
<input type="”text”" name="”T1″" size="”20″">
<input type="”button”" value="”提交”" onclick="”Javascript:this.disabled=true;document.form1.submit();”">
</p>

(2).利用session

在session中放一个特殊标志,当表单页面被请求时,生成一个特殊的字符标志串,存在session中,同时放在表单的隐藏域里,接受处理表单数据时,检查标识字串是否存在,并立即从session中删除它,然后正常处理数据,如果发现表单提交里没有有效的标志串,这说明表单已经被提交过了,忽略这次提交,这使web应用有了更高级的XSRF保护加载提交的页面时候,生成一个随机数,存储在表单的隐藏输入框中,在接收页面的PHP代码如下
<?php
 
$session = Yii::app()->session;
$user_id = $this->user->id;
$sessionKey = $user_id.'_is_sending';
if(isset($session[$sessionKey]))
    $first_submit_time = $session[$sessionKey];
    $current_time = time();

    if($current_time - $first_submit_time < 10)
        $session[$sessionKey] = $current_time;
        $this->response(array('status'=>1, 'msg'=>'不能在10秒钟内连续发送两次。'));
     else 
        unset($session[$sessionKey]);//超过限制时间,释放session";
    


//第一次点击确认按钮时执行
if(!isset($session[$sessionKey]))
    $session[$sessionKey] = time();

?>

(3).利用cookies

原理和session差不多,但是 cookies一旦用户浏览器禁用 cookies,这功能就失效了
if(isset($_POST[‘submit']))
    setCOOKIE("tempCOOKIE","",time()+30); 
    header("Location:".$_SERVER[PHP_SELF]);
    exit(); 

 
if(isset($_COOKIE["tempCOOKIE"])) 
    setCOOKIE("tempCOOKIE","",0);
    echo "您已经提交过表单";

(4).利用header函数跳转

一旦用户点击提交按钮,处理完数据后跳到其他页面

if (isset($_POST[‘submit'])) 
    header(‘location:index.php');//处理数据后,转向到其他页面 

(5).利用数据库来添加约束

直接在数据库里添加唯一约束或创建唯一索引,一旦发现用户重复提交了,直接抛出警告或者提示,或者只处理第一次提交的数据,这是最直接有效的方法,要求前期的数据库设计和架构要考虑周全

(6).Post/Redirect/Get模式

在提交后执行页面重定向,这就是所谓的Post-Redirect-Get (PRG)模式,简言之,当用户提交了表单后,去执行一个客户端的重定向,转到提交成功信息页面
if (isset($_POST["ction"]) && $_POST[‘action'] == "submitted")  
//处理数据,如插入数据后,立即转向到其他页面
header("location:submits_success.php"); 

以上是关于PHP防止站外表单跨站提交的几种办法详解的主要内容,如果未能解决你的问题,请参考以下文章

[基础] PHP网站如何防止CSRF(跨站请求伪造)

php防止表单重复提交的几种方法

PHP防止表单重复提交的几种常用方法汇总

如何在 PHP 中有效地防止跨站请求伪造 (CSRF)

php安全字段和防止XSS跨站脚本攻击过滤函数

php防止sql注入以及xss跨站脚本攻击