森林中的木棉 發表於 2019-5-20 13:31:00

【php】php表单防止重复提交(防csrf漏洞)

<p>这篇文章介绍的内容是关于php表单防止重复提交(防csrf漏洞) ,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下<br>Token浅谈</p>
<p>Token,就是令牌,最大的特点就是随机性,不可预测。一般黑客或软件无法猜测出来。</p>
<p>那么,Token有什么作用?又是什么原理呢?</p>
<p>Token一般用在两个地方——防止表单重复提交、anti csrf攻击(跨站点请求伪造)。</p>
<p>两者在原理上都是通过session token来实现的。当客户端请求页面时,服务器会生成一个随机数Token,并且将Token放置到session当中,然后将Token发给客户端(一般通过构造hidden表单)。下次客户端提交请求时,Token会随着表单一起提交到服务器端。</p>
<p>然后,如果应用于“anti csrf攻击”,则服务器端会对Token值进行验证,判断是否和session中的Token值相等,若相等,则可以证明请求有效,不是伪造的。</p>
<p>不过,如果应用于“防止表单重复提交”,服务器端第一次验证相同过后,会将涩session中的Token值更新下,若用户重复提交,第二次的验证判断将失败,因为用户提交的表单中的Token没变,但服务器端session中Token已经改变了。</p>
<p>上面的session应用相对安全,但也叫繁琐,同时当多页面多请求时,必须采用多Token同时生成的方法,这样占用更多资源,执行效率会降低。因此,也可用cookie存储验证信息的方法来代替session Token。比如,应对“重复提交”时,当第一次提交后便把已经提交的信息写到cookie中,当第二次提交时,由于cookie已经有提交记录,因此第二次提交会失败。</p>
<p>不过,cookie存储有个致命弱点,如果cookie被劫持(xss攻击很容易得到用户cookie),那么又一次gameover。黑客将直接实现csrf攻击。</p>
<p>1.首先防范xss攻击</p>
<p>2.验证referrer</p>
<p>3.重要cookie设置https only ,比如token</p>
<p>4.使用签名,令牌</p>
<p>5.get只用于查询信息</p>
<p>6.表单提交用post</p>
<p>7.谨慎使用跨脚本注入</p>
<p>所以,安全和高效相对的。具体问题具体对待吧。</p>
<p>&nbsp;</p>
<p>php表单加入Token防止重复提交</p>
<p>原理在于生成一个随机字符串放在session里,提交表单后来验证这个字符串,可以做到防止他人自己写form来欺骗提交,重复提交或者双击提交。</p>
<p><img src="https://img2018.cnblogs.com/blog/662503/201905/662503-20190520132739735-2085012460.png" alt=""></p>
<p>简单的用php实现的代码如下:</p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">

* PHP简单利用token防止表单重复提交

* 此处理方法纯粹是为了给初学者参考

</span><span style="color: rgba(0, 128, 0, 1)">*/</span>

<span style="color: rgba(0, 128, 128, 1)">session_start</span><span style="color: rgba(0, 0, 0, 1)">();

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> set_token() {

</span><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['token'] = <span style="color: rgba(0, 128, 128, 1)">md5</span>(<span style="color: rgba(0, 128, 128, 1)">microtime</span>(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">));

}

</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> valid_token() {

</span><span style="color: rgba(128, 0, 128, 1)">$return</span> = <span style="color: rgba(128, 0, 128, 1)">$_REQUEST</span>['token'] === <span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['token'] ? <span style="color: rgba(0, 0, 255, 1)">true</span> : <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;

set_token();

</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$return</span><span style="color: rgba(0, 0, 0, 1)">;

}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果token为空则生成一个token</span>

<span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 255, 1)">isset</span>(<span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['token']) || <span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['token']==''<span style="color: rgba(0, 0, 0, 1)">) {

set_token();

}

</span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 0, 255, 1)">isset</span>(<span style="color: rgba(128, 0, 128, 1)">$_POST</span>['test'<span style="color: rgba(0, 0, 0, 1)">])){

</span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 0, 1)">valid_token()){

    </span><span style="color: rgba(0, 0, 255, 1)">echo</span> "token error"<span style="color: rgba(0, 0, 0, 1)">;

}</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{

    </span><span style="color: rgba(0, 0, 255, 1)">echo</span> '成功提交,Value:'.<span style="color: rgba(128, 0, 128, 1)">$_POST</span>['test'<span style="color: rgba(0, 0, 0, 1)">];

}

}

</span>?&gt;

&lt;form method="post" action=""&gt;

&lt;input type="hidden" name="token" value="&lt;?php echo <span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['token']?&gt;"&gt;

&lt;input type="text" name="test" value="Default"&gt;

&lt;input type="submit" value="提交" /&gt;

&lt;/form&gt;</pre>
</div>
<p>上面的比较简单一点的方法,下面的代码更加安全一点。</p>
<p>Token.php</p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">

* Created on 2013-3-25

*

* To change the template for this generated file go to

* Window - Preferences - PHPeclipse - PHP - Code Templates

</span><span style="color: rgba(0, 128, 0, 1)">*/</span>

<span style="color: rgba(0, 0, 255, 1)">function</span> getToken(<span style="color: rgba(128, 0, 128, 1)">$len</span> = 32, <span style="color: rgba(128, 0, 128, 1)">$md5</span> = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {

</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Seed random number generator</span>

<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Only needed for PHP versions prior to 4.2</span>

<span style="color: rgba(0, 128, 128, 1)">mt_srand</span>((<span style="color: rgba(0, 0, 255, 1)">double</span>) <span style="color: rgba(0, 128, 128, 1)">microtime</span>() * 1000000<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Array of characters, adjust as desired</span>

<span style="color: rgba(128, 0, 128, 1)">$chars</span> = <span style="color: rgba(0, 0, 255, 1)">array</span><span style="color: rgba(0, 0, 0, 1)"> (

    </span>'Q',

    '@',

    '8',

    'y',

    '%',

    '^',

    '5',

    'Z',

    '(',

    'G',

    '_',

    'O',

    '`',

    'S',

    '-',

    'N',

    '&lt;',

    'D',

    '{',

    '}',

    '[',

    ']',

    'h',

    ';',

    'W',

    '.',

    '/',

    '|',

    ':',

    '1',

    'E',

    'L',

    '4',

    '&amp;',

    '6',

    '7',

    '#',

    '9',

    'a',

    'A',

    'b',

    'B',

    '~',

    'C',

    'd',

    '&gt;',

    'e',

    '2',

    'f',

    'P',

    'g',

    ')',

    '?',

    'H',

    'i',

    'X',

    'U',

    'J',

    'k',

    'r',

    'l',

    '3',

    't',

    'M',

    'n',

    '=',

    'o',

    '+',

    'p',

    'F',

    'q',

    '!',

    'K',

    'R',

    's',

    'c',

    'm',

    'T',

    'v',

    'j',

    'u',

    'V',

    'w',

    ',',

    'x',

    'I',

    '$',

    'Y',

    'z',

    '*'<span style="color: rgba(0, 0, 0, 1)">

);

</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Array indice friendly number of chars;</span>

<span style="color: rgba(128, 0, 128, 1)">$numChars</span> = <span style="color: rgba(0, 128, 128, 1)">count</span>(<span style="color: rgba(128, 0, 128, 1)">$chars</span>) - 1<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(128, 0, 128, 1)">$token</span> = ''<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Create random token at the specified length</span>

<span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(128, 0, 128, 1)">$i</span> = 0; <span style="color: rgba(128, 0, 128, 1)">$i</span> &lt; <span style="color: rgba(128, 0, 128, 1)">$len</span>; <span style="color: rgba(128, 0, 128, 1)">$i</span>++<span style="color: rgba(0, 0, 0, 1)">)

    </span><span style="color: rgba(128, 0, 128, 1)">$token</span> .= <span style="color: rgba(128, 0, 128, 1)">$chars</span>[<span style="color: rgba(0, 128, 128, 1)">mt_rand</span>(0, <span style="color: rgba(128, 0, 128, 1)">$numChars</span><span style="color: rgba(0, 0, 0, 1)">)];

</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Should token be run through md5?</span>

<span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$md5</span><span style="color: rgba(0, 0, 0, 1)">) {

    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Number of 32 char chunks</span>

    <span style="color: rgba(128, 0, 128, 1)">$chunks</span> = <span style="color: rgba(0, 128, 128, 1)">ceil</span>(<span style="color: rgba(0, 128, 128, 1)">strlen</span>(<span style="color: rgba(128, 0, 128, 1)">$token</span>) / 32<span style="color: rgba(0, 0, 0, 1)">);

    </span><span style="color: rgba(128, 0, 128, 1)">$md5token</span> = ''<span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Run each chunk through md5</span>

    <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(128, 0, 128, 1)">$i</span> = 1; <span style="color: rgba(128, 0, 128, 1)">$i</span> &lt;= <span style="color: rgba(128, 0, 128, 1)">$chunks</span>; <span style="color: rgba(128, 0, 128, 1)">$i</span>++<span style="color: rgba(0, 0, 0, 1)">)

      </span><span style="color: rgba(128, 0, 128, 1)">$md5token</span> .= <span style="color: rgba(0, 128, 128, 1)">md5</span>(<span style="color: rgba(0, 128, 128, 1)">substr</span>(<span style="color: rgba(128, 0, 128, 1)">$token</span>, <span style="color: rgba(128, 0, 128, 1)">$i</span> * 32 - 32, 32<span style="color: rgba(0, 0, 0, 1)">));

    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Trim the token</span>

    <span style="color: rgba(128, 0, 128, 1)">$token</span> = <span style="color: rgba(0, 128, 128, 1)">substr</span>(<span style="color: rgba(128, 0, 128, 1)">$md5token</span>, 0, <span style="color: rgba(128, 0, 128, 1)">$len</span><span style="color: rgba(0, 0, 0, 1)">);

}

</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$token</span><span style="color: rgba(0, 0, 0, 1)">;

}

</span>?&gt;</pre>
</div>
<p>form.php</p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 0, 255, 1)">include_once</span>("token.php"<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(128, 0, 128, 1)">$token</span> =<span style="color: rgba(0, 0, 0, 1)"> getToken();

</span><span style="color: rgba(0, 128, 128, 1)">session_start</span><span style="color: rgba(0, 0, 0, 1)">();

</span><span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['token'] = <span style="color: rgba(128, 0, 128, 1)">$token</span><span style="color: rgba(0, 0, 0, 1)">;

</span>?&gt;

&lt;form action="action.php" method="post"

&lt;input type="hidden" name="token" value="&lt;?=<span style="color: rgba(128, 0, 128, 1)">$token</span>?&gt;" /&gt;

&lt;!-- 其他input submit之类的 --&gt;

&lt;/form&gt;</pre>
</div>
<p>action.php</p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 128, 128, 1)">session_start</span><span style="color: rgba(0, 0, 0, 1)">();

</span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(128, 0, 128, 1)">$_POST</span>['token'] == <span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['token'<span style="color: rgba(0, 0, 0, 1)">]){

</span><span style="color: rgba(0, 0, 255, 1)">unset</span>(<span style="color: rgba(128, 0, 128, 1)">$_SESSION</span>['token'<span style="color: rgba(0, 0, 0, 1)">]);

</span><span style="color: rgba(0, 0, 255, 1)">echo</span> "这是一个正常的提交请求"<span style="color: rgba(0, 0, 0, 1)">;

}</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{

</span><span style="color: rgba(0, 0, 255, 1)">echo</span> "这是一个非法的提交请求"<span style="color: rgba(0, 0, 0, 1)">;

}

</span>?&gt;</pre>
</div>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/opensmarty/p/10893673.html
頁: [1]
查看完整版本: 【php】php表单防止重复提交(防csrf漏洞)