php PHP中的CSRF(跨站点请求伪造)攻击示例和预防

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2526522/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 06:50:25  来源:igfitidea点击:

CSRF (Cross-site request forgery) attack example and prevention in PHP

phpcsrfowasp

提问by Saif Bechan

I have an website where people can place a vote like this:

我有一个网站,人们可以像这样投票:

http://mysite.com/vote/25

This will place a vote on item 25. I want to only make this available for registered users, and only if they want to do this. Now I know when someone is busy on the website, and someone gives them a link like this:

这将对第 25 项进行投票。我只想将其提供给注册用户,并且仅当他们想要这样做时。现在我知道有人在网站上忙时,有人给了他们这样的链接:

http://mysite.com/vote/30

then the vote will be places for him on the item without him wanting to do this.

那么投票将是他在该项目上的位置,而他不想这样做。

I have read the explanation on the OWASP website, but i don't really understand it

我已经阅读了OWASP 网站上解释,但我不太明白

Is this an example of CSRF, and how can I prevent this. The best thing i can think off is adding something to the link like a hash. But this will be quite irritating to put something on the end of all the links. Is there no other way of doing this.

这是CSRF的一个例子,我该如何防止这种情况。我能想到的最好的事情是在链接中添加一些东西,比如哈希。但是在所有链接的末尾放一些东西会很烦人。有没有其他方法可以做到这一点。

Another thing can someone maybe give me some other example of this, because the website seems fairly fugue to me.

另一件事可能有人可以给我一些其他的例子,因为该网站对我来说似乎相当赋格。

回答by Pascal MARTIN

This could become an example of CSRF if :

这可能成为 CSRF 的一个例子,如果:

  • that link is fetched (via an <img>tag, for example): forgery
  • from another site : cross-site
  • 获取该链接<img>例如,通过标签):伪造
  • 从另一个站点:跨站点


For example, if I could inject this <img>tag in the HTML source-code of stackoverflow (and I can, as stackoverflow allows one to use <img>tags in his posts):


例如,如果我可以<img>在 stackoverflow 的 HTML 源代码中注入这个标签(我可以,因为 stackoverflow 允许人们<img>在他的帖子中使用标签)

<img src="http://mysite.com/vote/30" />

You would just have voted for that item ;-)

你会投票给那个项目;-)


The solution that is generally used is to place a token, that has a limited life-time, in the URL, and, when the URL is fetched, check that this token is still valid.


通常使用的解决方案是在 URL 中放置一个生命周期有限的令牌,并在获取 URL 时检查该令牌是否仍然有效。

The basic idea would be :

基本思想是:

  • When generating the page :
    • generate a unique token
    • store it in the user's session
    • and place it in the links of the page -- which would look like this : http://mysite.com/vote/30?token=AZERTYUHQNWGST
  • When the voting page is called :
    • Check if the token is present in the URL
    • Check if it's present in the user's session
    • If not => do not register the vote
  • 生成页面时:
    • 生成唯一令牌
    • 将其存储在用户的会话中
    • 并将其放在页面的链接中——看起来像这样: http://mysite.com/vote/30?token=AZERTYUHQNWGST
  • 当投票页面被调用时:
    • 检查 URL 中是否存在令牌
    • 检查它是否存在于用户的会话中
    • 如果不是 => 不登记投票

The idea there is :

那里的想法是:

  • Tokens don't have a long life-time, and are hard to guess
  • Which means your attacker:
    • has only a window of a few minutes during which his injection will be valid
    • will have to be good at guessing ^^
    • will have to generate a different page for each user.
  • 令牌没有很长的生命周期,并且很难猜测
  • 这意味着您的攻击者
    • 只有几分钟的时间,他的注射是有效的
    • 必须善于猜测^^
    • 必须为每个用户生成不同的页面。


Also, note that the shorter the user's session remains active after he has left your site, the less risks there are that it's still valid when he visits the bad website.


另请注意,用户离开您的网站后会话保持活动的时间越短,访问不良网站时会话仍然有效的风险就越小。

But here, you have to choose between security and user-friendly...

但是在这里,您必须在安全性和用户友好性之间做出选择......


Another idea (that's not perfectly secure, but helps against guys would don't know how to force a POST request), would be to only accept POST requests when people are voting :


另一个想法(这不是完全安全,但有助于防止不知道如何强制 POST 请求的人),仅在人们投票时接受 POST 请求:

  • The browser is sending GET requests for injected tags
  • As this URL is modifying some data, anyway, it should not work with GET, but only with POST
  • 浏览器正在发送对注入标签的 GET 请求
  • 由于此 URL 正在修改一些数据,因此它不应该与 GET 一起使用,而只能与 POST 一起使用

But note that this is not perfectly safe : it's (probably ? )possible to force/forge a POST request, with some bit of Javascript.

但请注意,这并不是完全安全的:(可能?)可以使用一些 Javascript 来强制/伪造 POST 请求。

回答by MyGGaN

First, GET request shouldn't be used to alter states on the server, so for your vote service I would recommend POST/PUT. This is only a guideline, but a clever one.

首先,不应使用 GET 请求来更改服务器上的状态,因此对于您的投票服务,我建议使用 POST/PUT。这只是一个指导方针,但很聪明。

So to your question, CSRF is a client issue so it doesn't matter what kind of server language you use (PHP in your case). The standard fix is the same and goes like this: Have a random value in the URI/POST-data and the same value in the Cookie header. If those matches you could be sure there is no CSRF. There are a lot of info about how this could be done here on StackOverflow eg. this one.
Good luck!

因此,对于您的问题,CSRF 是一个客户端问题,因此您使用哪种服务器语言(在您的情况下为 PHP)并不重要。标准修复是相同的,如下所示:在 URI/POST-data 中有一个随机值,在 Cookie 标头中有相同的值。如果这些匹配,您可以确定没有 CSRF。在 StackOverflow 上有很多关于如何做到这一点的信息,例如。这个
祝你好运!

回答by vanderaj

OWASP has a CSRFGuard for PHP, and ESAPI for PHP that I wrote a long time ago for XMB -> UltimaBB -> GaiaBB.

OWASP 有一个用于 PHP 的 CSRFGuard,以及我很久以前为 XMB -> UltimaBB -> GaiaBB 编写的用于 PHP 的 ESAPI。

http://code.google.com/p/gaiabb-olpc/source/search?q=function+get_new_token&origq=function+get_new_token&btnG=Search+Trunk

http://code.google.com/p/gaiabb-olpc/source/search?q=function+get_new_token&origq=function+get_new_token&btnG=Search+Trunk

It seems some others have cleaned up that code and allowed for stronger tokens:

似乎其他一些人已经清理了该代码并允许使用更强大的令牌:

https://www.owasp.org/index.php/PHP_CSRF_Guard

https://www.owasp.org/index.php/PHP_CSRF_Guard

thanks, Andrew

谢谢,安德鲁

回答by Accountant ?

There are 3 players in a CSRF attack

CSRF 攻击中有 3 个玩家

  1. the victim website (your voting website in your example) [knows his logged in users cookies]
  2. your client's browser(while he is logged in) [knows his cookies]
  3. an attacker website [Doesn't know the logged-in users cookies]
  1. 受害者网站(在您的示例中是您的投票网站)[知道他登录的用户 cookie]
  2. 您客户的浏览器(当他登录时)[知道他的 cookie]
  3. 攻击者网站 [不知道登录用户的 cookie]

CSRF attacks depend on 2 facts

CSRF 攻击取决于 2 个事实

  1. browsers send cookies automatically with every request
  2. we depend on cookies to identify our logged-in users (e.g: setcookie("sessionID", "0123456789ABCDEF", time()+3600);)
  1. 浏览器在每次请求时自动发送 cookie
  2. 我们依赖cookie来确定我们的注册用户(如:setcookie("sessionID", "0123456789ABCDEF", time()+3600);

If an attacker could by away or another make a logged-in user requests this

如果攻击者可以通过或其他方式使登录用户请求此

// http://victim.website/vote/30

for example by putting the link on the attacker website or send it in an email, the logged in client browser will send the identifying cookies(sessionID) along with this request, which will make the victim website think that his logged-in user really wants to vote!

例如,通过将链接放在攻击者网站上或通过电子邮件发送,登录的客户端浏览器将与此请求一起发送识别 cookie(sessionID),这将使受害者网站认为他的登录用户确实想要投票!

But if the victim's website more clever and verifies the requests of his logged-in users with additional GET or POST parameter (not cookies), the attacker now is in a problem because GET and POST parameters are not sent automatically by browsers, and he has to guess it.

但是,如果受害者的网站更聪明,并使用额外的 GET 或 POST 参数(不是 cookie)验证其登录用户的请求,那么攻击者现在就遇到了问题,因为浏览器不会自动发送 GET 和 POST 参数,并且他已经猜一猜。

// http://victim.website/vote/30?csrfSecret=0123456789ABCDEF

The attacker doesn't know the csrfSecretparameter which is a secret between the victim website and his client (just like the session token), so the attacker has no way to build the URL that he wants forge a request by.

攻击者不知道csrfSecret受害者网站和他的客户端之间的秘密参数(就像会话令牌一样),因此攻击者无法构建他想要伪造请求的 URL。

Similarly, if the vote is done by POST requests, the attacker will not be able to make the form on his website(or third party website) because he doesn't know the secret between the victim's website and his users.

同样,如果投票是通过 POST 请求完成的,攻击者将无法在他的网站(或第三方网站)上制作表单,因为他不知道受害者网站和他的用户之间的秘密。

<form method="post" action="http://victim.website/vote" >
    <input type="hidden" name="vote" value="30">
    <input type="hidden" name="csrfSecret" value="????? I don't know it :(">
</form>