Cross-site request forgery (CSRF) is a common and serious exploit where a user is tricked into performing an action he didn’t explicitly intend to do. This can happen when, for example, the user is logged in to one of his favorite websites and proceeds to click a seemingly harmless link. In the background, his profile information is silently updated with an attacker’s e-mail address. The attacker can then use the website’s password reset feature to e-mail herself a new password and she’s just successfully stolen the account. Any action that a user is allowed to perform while logged in to a website, an attacker can perform on his/her behalf, whether it’s updating a profile, adding items to a shopping cart, posting messages on a forum, or practically anything else.
跨站点请求伪造(CSRF)是一种常见且严重的攻击,利用这种欺骗用户可以诱使用户执行他未明确打算执行的操作。 例如,当用户登录到他最喜欢的网站之一并继续单击看似无害的链接时,可能会发生这种情况。 在后台,他的个人资料信息将以攻击者的电子邮件地址进行无提示更新。 然后,攻击者可以使用网站的密码重置功能通过电子邮件向自己发送新密码,而她刚刚成功盗取了该帐户。 允许用户登录到网站时执行的任何操作,无论是更新个人资料,向购物车中添加商品,在论坛上发布消息,还是实际上其他任何操作,攻击者都可以代表他/她执行。
If you’ve never heard of CSRF before or you haven’t written your code with prevention in mind, then I hate to break it to you but more than likely you’re vulnerable. In this guide I will show you exactly how CSRF attacks work and what you can do to protect your users.
如果您以前从未听说过CSRF,或者您在编写代码时没有考虑到预防措施,那么我不愿意将它泄露给您,但是您很容易受到攻击。 在本指南中,我将向您确切介绍CSRF攻击的工作原理以及如何保护用户。
To understand how a CSRF attack works, it’s best to see it in action. To illustrate an attack, I’d like to create a simple example that has the ability to logout an active session. We will need a login page (login.php), a processing script to handle logging in and logging out of the session (process.php), and finally an example attack (harmless.html).
要了解CSRF攻击的工作原理,最好是将其付诸实践。 为了说明攻击,我想创建一个简单的示例,该示例具有注销活动会话的功能。 我们将需要一个登录页面( login.php ),一个处理脚本来处理会话的登录和注销( process.php ),最后是一个示例攻击( harmless.html )。
First, here’s the code for login.php:
首先,这是login.php的代码:
<?php session_start(); ?> <html> <body> <?php if (isset($_SESSION["user"])) { echo "<p>Welcome back, " . $_SESSION["user"] . "!<br>"; echo '<a href="process.php?action=logout">Logout</a></p>'; } else { ?> <form action="process.php?action=login" method="post"> <p>The username is: admin</p> <input type="text" name="user" size="20"> <p>The password is: test</p> <input type="password" name="pass" size="20"> <input type="submit" value="Login"> </form> <?php } ?> </body> </html>The login.php script begins by initializing the session data. It then checks to see if $_SESSION["user"] has been set, and if so displays a welcome message along with a link to logout. Otherwise it displays the login form.
login.php脚本首先初始化会话数据。 然后,它检查是否已设置$_SESSION["user"] ,如果已设置,则显示欢迎消息以及注销链接。 否则,它将显示登录表单。
This is the processing script, process.php:
这是处理脚本process.php :
<?php session_start(); switch($_GET["action"]) { case "login": if ($_SERVER["REQUEST_METHOD"] == "POST") { $user = (isset($_POST["user"]) && ctype_alnum($_POST["user"]) ? $_POST["user"] : null; $pass = (isset($_POST["pass"])) ? $_POST["pass"] : null; $salt = '$2a$07$my.s3cr3t.SalTY.str1nG$'; if (isset($user, $pass) && (crypt($user . $pass, $salt) == crypt("admintest", $salt))) { $_SESSION["user"] = $_POST["user"]; } } break; case "logout": $_SESSION = array(); session_destroy(); break; } header("Location: login.php"); ?>The process.php script also begins by initializing the session data, and then checks to see if there is an action to work with. We perform some basic input validation using PHP’s ternary operator along with the ctype_alnum() and crypt() functions, and then set or destroy the session variable accordingly. The user is redirected back to login.php at the end of the script.
process.php脚本还从初始化会话数据开始,然后检查是否有可操作的动作。 我们使用PHP的三元运算符以及ctype_alnum()和crypt()函数执行一些基本的输入验证,然后相应地设置或销毁会话变量。 在脚本末尾,用户将被重定向回login.php 。
Now let’s focus on the file an attacker might create to exploit the code in our previous examples. This is the exploit code, harmless.html:
现在,让我们集中讨论攻击者可能创建的文件,以利用我们先前示例中的代码。 这是利用代码harmless.html :
<html> <body> <p>This page is harmless... Or is it?</p> <!-- Address to target website --> <img src="process.php?action=logout" style="display: none;"> </body> </html>If you visit login.php and log in to your account, and then while logged in you proceed to visit the attacker’s page, you will be automatically logged out even though you didn’t click the logout link. The browser sends a request to the server to access the process.php script, expecting it to be an image file. The processing script has no way of differentiating between a valid request initiated by a user clicking on the logout link and a cleverly-crafted request the browser was tricked into sending.
如果您访问login.php并登录到您的帐户,然后在登录时继续访问攻击者的页面,即使您未单击注销链接也将自动注销。 浏览器将请求发送到服务器以访问process.php脚本,并希望该请求是图像文件。 处理脚本无法区分用户单击注销链接发起的有效请求和欺骗浏览器发送的精心设计的请求。
The harmless.html file could be hosted on an entirely different server than the one you’re logged into, and it would still work because the attacker’s page is making a request on your behalf using the session you have open in the background. It doesn’t even matter if the website you’re logged into is on a private network, the request will be submitted from your IP address as if you made the request yourself, making a trace to the source of the attack nearly impossible.
harmless.html文件可以托管在与您登录的服务器完全不同的服务器上,并且仍然可以正常工作,因为攻击者的页面正在使用您在后台打开的会话代表您发出请求。 即使您登录的网站是否在专用网络上也没关系,就像您自己提出请求一样,请求将从您的IP地址提交,从而几乎不可能追踪到攻击源。
Additionally, if you allow your users to link to images as a profile avatar or the like, without proper escaping and sanitizing of the user supplied data the attack may even be possible within your own web domain.
此外,如果您允许用户作为个人资料的头像或类似内容链接到图像,而没有对用户提供的数据进行适当的转义和清理,则甚至可能在您自己的Web域中进行攻击。
While logging someone out of a website isn’t that impressive, harmless.html could have just as easily contained a hidden inline frame (as opposed to an image tag) with a form that automatically submits when the page is loaded, which would make any of the attacks mentioned at the beginning of this guide fair game.
虽然使某人退出网站并不令人印象深刻,但harmless.html可以很容易地包含一个隐藏的嵌入式框架(与image标签相对),该框架具有在页面加载时自动提交的表单,这将使本指南竞赛开始时提到的攻击。
Hopefully now you understand just how serious CSRF attacks can be, so let’s take a look at how you can prevent them.
希望现在您了解CSRF攻击有多严重,因此让我们看一下如何预防它们。
In order to ensure that an action is actually being performed by the user rather than a third party, you need to associate it with some sort of unique identifier which can then be verified. To prevent the attack, we can modify login.php as follows:
为了确保操作实际上是由用户而不是第三方执行的,您需要将其与某种唯一的标识符相关联,然后可以对其进行验证。 为了防止攻击,我们可以如下修改login.php :
<?php // make a random id $_SESSION["token"] = md5(uniqid(mt_rand(), true)); echo '<a href="process.php?action=logout&csrf=' . $_SESSION["token"] . '">Logout</a></p>';Then to verify the identifier, we can modify process.php as follows:
然后,为了验证标识符,我们可以如下修改process.php :
case "logout": if (isset($_GET["csrf"]) && $_GET["csrf"] == $_SESSION["token"]) { $_SESSION = array(); session_destroy(); } break;With these simple modifications, harmless.html will no longer work because the attacker has been given the additional task of having to guess an additional random token value. To protect forms, you can also include the identifier inside of a hidden field as follows so it is submitted along with the rest of the form data.
通过这些简单的修改, harmless.html将不再起作用,因为已为攻击者提供了额外的任务,必须猜测额外的随机令牌值。 为了保护表单,您还可以按如下所示将标识符包括在隐藏字段中,以便将其与表单数据的其余部分一起提交。
<input type="hidden" name="csrf" value="<?php echo $_SESSION["token"]; ?>">In my own opinion, despite the best intentioned harassing of my esteemed friends and colleagues, I prefer to use PHP’s session_id() rather than generating a random token since I’m not particularly fond of the “security through obscurity” approach. In addition to using session_id(), I also use session_regenerate_id() whenever logging in or updating sensitive information in order to mitigate the risk of any session fixation attacks, and I never append the id to a URL that will be stored in the browsers history. Arbitrarily exposing the session id more than necessary is never a good idea, but so long as you’re careful I think it’s a more elegant approach. Of course, if your website uses some type of authentication that doesn’t use sessions, then you’d need to generate your own id anyway.
我个人认为,尽管最好地骚扰了我尊敬的朋友和同事,但我更喜欢使用PHP的session_id()而不是生成随机令牌,因为我并不特别喜欢“默默无闻的安全性”方法。 除了使用session_id() ,每当登录或更新敏感信息时,我也会使用session_regenerate_id()来减轻任何会话固定攻击的风险,而且我从未将ID附加到将存储在浏览器历史记录中的URL上。 。 过度暴露会话ID绝不是一个好主意,但是只要您小心一点,我认为这是一种更优雅的方法。 当然,如果您的网站使用的某种身份验证类型不使用会话,那么无论如何您都需要生成自己的ID。
By now you should understand the basic principles underlying a CSRF attack and what steps you can take to protect your site and your users. As Ben Franklin said, “an ounce of prevention is worth a pound of cure.” I’m sure all of us would rather take the time to make sure the code we write is secure than deal with the stress, headaches and possible lawsuits surrounding a compromise.
到目前为止,您应该了解CSRF攻击的基本原理以及可以采取哪些步骤来保护您的站点和用户。 正如本·富兰克林所说: “一分预防胜过一磅治疗。” 我敢肯定,我们所有人都宁愿花些时间来确保我们编写的代码是安全的,而不是去处理与折衷有关的压力,头痛和可能的诉讼。
Image via Blazej Lyjak / Shutterstock
图片来自Blazej Lyjak / Shutterstock
翻译自: https://www.sitepoint.com/preventing-cross-site-request-forgeries/