Merging other languages
This commit is contained in:
190
en/09.1.md
190
en/09.1.md
@@ -1,93 +1,97 @@
|
||||
# 9.1 预防CSRF攻击
|
||||
|
||||
## 什么是CSRF
|
||||
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。
|
||||
|
||||
那么CSRF到底能够干嘛呢?你可以这样简单的理解:攻击者可以盗用你的登陆信息,以你的身份模拟发送各种请求。攻击者只要借助少许的社会工程学的诡计,例如通过QQ等聊天软件发送的链接(有些还伪装成短域名,用户无法分辨),攻击者就能迫使Web应用的用户去执行攻击者预设的操作。例如,当用户登录网络银行去查看其存款余额,在他没有退出时,就点击了一个QQ好友发来的链接,那么该用户银行帐户中的资金就有可能被转移到攻击者指定的帐户中。
|
||||
|
||||
所以遇到CSRF攻击时,将对终端用户的数据和操作指令构成严重的威胁;当受攻击的终端用户具有管理员帐户的时候,CSRF攻击将危及整个Web应用程序。
|
||||
|
||||
## CSRF的原理
|
||||
下图简单阐述了CSRF攻击的思想
|
||||
|
||||

|
||||
|
||||
图9.1 CSRF的攻击过程
|
||||
|
||||
从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤 :
|
||||
|
||||
- 1.登录受信任网站A,并在本地生成Cookie 。
|
||||
- 2.在不退出A的情况下,访问危险网站B。
|
||||
|
||||
看到这里,读者也许会问:“如果我不满足以上两个条件中的任意一个,就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:
|
||||
|
||||
- 你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站,特别现在浏览器都是支持多tab的。
|
||||
- 你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。
|
||||
- 上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。
|
||||
|
||||
因此对于用户来说很难避免在登陆一个网站之后不点击一些链接进行其他操作,所以随时可能成为CSRF的受害者。
|
||||
|
||||
CSRF攻击主要是因为Web的隐式身份验证机制,Web的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。
|
||||
|
||||
## 如何预防CSRF
|
||||
过上面的介绍,读者是否觉得这种攻击很恐怖,意识到恐怖是个好事情,这样会促使你接着往下看如何改进和防止类似的漏洞出现。
|
||||
|
||||
CSRF的防御可以从服务端和客户端两方面着手,防御效果是从服务端着手效果比较好,现在一般的CSRF防御也都在服务端进行。
|
||||
|
||||
服务端的预防CSRF攻击的方式方法有多种,但思想上都是差不多的,主要从以下2个方面入手:
|
||||
|
||||
- 1、正确使用GET,POST和Cookie;
|
||||
- 2、在非GET请求中增加伪随机数;
|
||||
|
||||
我们上一章介绍过REST方式的Web应用,一般而言,普通的Web应用都是以GET、POST为主,还有一种请求是Cookie方式。我们一般都是按照如下方式设计应用:
|
||||
|
||||
1、GET常用在查看,列举,展示等不需要改变资源属性的时候;
|
||||
|
||||
2、POST常用在下达订单,改变一个资源的属性或者做其他一些事情;
|
||||
|
||||
接下来我就以Go语言来举例说明,如何限制对资源的访问方法:
|
||||
|
||||
mux.Get("/user/:uid", getuser)
|
||||
mux.Post("/user/:uid", modifyuser)
|
||||
|
||||
这样处理后,因为我们限定了修改只能使用POST,当GET方式请求时就拒绝响应,所以上面图示中GET方式的CSRF攻击就可以防止了,但这样就能全部解决问题了吗?当然不是,因为POST也是可以模拟的。
|
||||
|
||||
因此我们需要实施第二步,在非GET方式的请求中增加随机数,这个大概有三种方式来进行:
|
||||
|
||||
- 为每个用户生成一个唯一的cookie token,所有表单都包含同一个伪随机值,这种方案最简单,因为攻击者不能获得第三方的Cookie(理论上),所以表单中的数据也就构造失败,但是由于用户的Cookie很容易由于网站的XSS漏洞而被盗取,所以这个方案必须要在没有XSS的情况下才安全。
|
||||
- 每个请求使用验证码,这个方案是完美的,因为要多次输入验证码,所以用户友好性很差,所以不适合实际运用。
|
||||
- 不同的表单包含一个不同的伪随机值,我们在4.4小节介绍“如何防止表单多次递交”时介绍过此方案,复用相关代码,实现如下:
|
||||
|
||||
生成随机数token
|
||||
|
||||
h := md5.New()
|
||||
io.WriteString(h, strconv.FormatInt(crutime, 10))
|
||||
io.WriteString(h, "ganraomaxxxxxxxxx")
|
||||
token := fmt.Sprintf("%x", h.Sum(nil))
|
||||
|
||||
t, _ := template.ParseFiles("login.gtpl")
|
||||
t.Execute(w, token)
|
||||
|
||||
输出token
|
||||
|
||||
<input type="hidden" name="token" value="{{.}}">
|
||||
|
||||
验证token
|
||||
|
||||
r.ParseForm()
|
||||
token := r.Form.Get("token")
|
||||
if token != "" {
|
||||
//验证token的合法性
|
||||
} else {
|
||||
//不存在token报错
|
||||
}
|
||||
|
||||
这样基本就实现了安全的POST,但是也许你会说如果破解了token的算法呢,按照理论上是,但是实际上破解是基本不可能的,因为有人曾计算过,暴力破解该串大概需要2的11次方时间。
|
||||
|
||||
## 总结
|
||||
跨站请求伪造,即CSRF,是一种非常危险的Web安全威胁,它被Web安全界称为“沉睡的巨人”,其威胁程度由此“美誉”便可见一斑。本小节不仅对跨站请求伪造本身进行了简单介绍,还详细说明造成这种漏洞的原因所在,然后以此提了一些防范该攻击的建议,希望对读者编写安全的Web应用能够有所启发。
|
||||
|
||||
## links
|
||||
* [目录](<preface.md>)
|
||||
* 上一节: [安全与加密](<09.0.md>)
|
||||
* 下一节: [确保输入过滤](<09.2.md>)
|
||||
# 9.1 CSRF attacks
|
||||
|
||||
## What is CSRF?
|
||||
|
||||
CSRF and XSRF both stand for "Cross-site request forgery". It's also known as a "one click attack" or "session riding".
|
||||
|
||||
So how does a CSRF attack work? A CSRF attack happens when an attacker tricks a trusted user into accessing a website or clicking a URL that transmits malicious requests (without the user’s consent) to a targeted website. Here's a simple example: using a few social engineering tricks, an attacker could use the QQ chat software to find and send malicious links to victims targeted at their user's online banking website. If the victim logs into their online bank account and does not exit, then clicking on a malicious link sent from the attacker could allow the attacker to steal all of the user's bank account funds.
|
||||
|
||||
When under a CSRF attack, a single end-user with an administrator account can threaten the integrity of the entire web application.
|
||||
|
||||
## CSRF principle
|
||||
|
||||
The following diagram provides a simple overview of a CSRF attack
|
||||
|
||||

|
||||
|
||||
Figure 9.1 CSRF attack process.
|
||||
|
||||
As can be seen from the figure, to complete a CSRF attack, the victim must complete the following two steps:
|
||||
|
||||
-1. Log into trusted site A, and store a local Cookie.
|
||||
-2. Without going through existing site A, access the dangerous link to site B.
|
||||
|
||||
As a reader you may be asking: "If I do not meet the above two conditions, I will will not be subjected to CSRF attacks." Yes this is true, however you cannot guarantee that the following does not occur:
|
||||
|
||||
- You cannot guarantee that when you are logged into a site, the site didn't launch any hidden tabs.
|
||||
- You cannot guarantee that when you close your browser, your cookies will immediately expire and your last session will have ended.
|
||||
- Trusted, high traffic websites will likely not have hidden vulnerabilities easily exploitable by CSRF based attacks.
|
||||
|
||||
Thus, it can be difficult for users to visit a website through a link and know that it will not carry out unknown operations in the form of a CSRF attack.
|
||||
|
||||
CSRF attacks work mostly because of the process through which users are authenticated. Although you can reasonably guarantee that a request originates from a user's browser, there is no guarantee that the user granted approval for the request.
|
||||
|
||||
## How to prevent CSRF attacks
|
||||
|
||||
You might be a little scared after reading the section above. But fear is a good thing. It will force you educate yourself on how to prevent vulnerabilities like this from happening to you.
|
||||
|
||||
Preventative measures against CSRF attacks can be taken on both the server and client sides of a web application. However, CSRF attacks are most effectively thwarted on the server side.
|
||||
|
||||
There are many ways of preventing CSRF attacks are the server side. Most approaches stem from from the following two aspects:
|
||||
|
||||
1. Maintaining proper use of GET, POST and cookies.
|
||||
2. Including a pseudo-random number with non-GET requests.
|
||||
|
||||
In the previous chapter on REST, we saw how most web applications are based on GET and POST HTTP requests, and that cookies were included along with these requests. We generally design application as according to the following principles:
|
||||
|
||||
1. GET is commonly used to view information without altering any data.
|
||||
|
||||
2. POST is used in placing orders, changing the properties of a resource or performing other tasks.
|
||||
|
||||
I'm now going to use the Go language to illustrate how to restrict access to resources methods:
|
||||
|
||||
mux.Get("/user/:uid", getuser)
|
||||
mux.Post("/user/:uid", modifyuser)
|
||||
|
||||
Since we've stipulated that modifications can only use POST, when a GET method is issued instead of a POST, we can refuse to respond to the request. According to the figure above, attacks utilizing GET as a CSRF exploit can be prevented. Is this enough to prevent all possible CSRF attacks? Of course not, because POSTs can also be forged.
|
||||
|
||||
We need to implement a second step, which is (in the case of non-GET requests) to increase the length of the pseudo-random number included with the request. This usually involves steps:
|
||||
|
||||
- For each user, generate a unique cookie token with a pseudo-random value. All forms must contain the same pseudo-random value. This proposal is the simplest one because in theory, an attacker cannot read third party cookies. Any form that an attacker may submit will fail the validation process without knowing what the random value is.
|
||||
- Different forms contain different pseudo-random values, as we've introduced in section 4.4, "How to prevent multiple form submission". We can reuse the relevant code from that section suit our needs:
|
||||
|
||||
Generating a random number token:
|
||||
|
||||
h := md5.New()
|
||||
io.WriteString(h, strconv.FormatInt(crutime, 10))
|
||||
io.WriteString(h, "ganraomaxxxxxxxxx")
|
||||
token := fmt.Sprintf("%x", h.Sum(nil))
|
||||
|
||||
t, _ := template.ParseFiles("login.gtpl")
|
||||
t.Execute(w, token)
|
||||
|
||||
|
||||
Output token:
|
||||
|
||||
<input type="hidden" name="token" value="{{.}}">
|
||||
|
||||
Authentication token:
|
||||
|
||||
r.ParseForm()
|
||||
token := r.Form.Get("token")
|
||||
if token! = "" {
|
||||
// Verification token of legitimacy
|
||||
} Else {
|
||||
// Error token does not exist
|
||||
}
|
||||
|
||||
We can use the preceding code to secure our POSTs. You might be wondering, in accordance with our theory, whether there could be some way for a malicious third party to somehow figure out our secret token value? In fact, cracking it is basically impossible -successfully calculating the correct string value using brute force methods needs about 2 to the 11th time.
|
||||
|
||||
## Summary
|
||||
|
||||
Cross-site request forgery, otherwise known as CSRF, is a very dangerous web security threat. It is known in web security circles as a "sleeping giant" security issue; as you can tell, CSRF attacks have quite the reputation. This section not only introduced cross-site request forgery itself, but factors underlying this vulnerability. It concludes with some suggestions and methods for preventing such attacks. I hope this section will have inspired you, as a reader, to write better and more secure web applications.
|
||||
|
||||
## Links
|
||||
- [Directory](preface.md)
|
||||
- Previous section: [Security and encryption](09.0.md)
|
||||
- Next section: [Filter inputs](09.2.md)
|
||||
|
||||
Reference in New Issue
Block a user