XSS
XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。 其实在web前端方面,可以简单的理解为一种javascript代码注入。举个例子,我们有个社交网站,允许大家相互访问空间,网站可能是这样做的:
For example:
<form action="" method="POST">
<input type="text" name="text">
<input type="submit" value="submit"></input>
</form>
<h2>你输入的内容: {{{text}}}</h2>
</form>
submit点一下:
GG ~
如何防范?
目前来讲,最简单的办法防治办法,还是将前端输出数据都进行转义最为稳妥。
比如,按照刚刚我们那个例子来说,其本质是,浏览器遇到script标签的话,则会执行其中的脚本。但是如果我们将script标签的进行转义,则浏览器便不会认为其是一个标签,但是显示的时候,还是会按照正常的方式去显示:
<form action="" method="POST">
<input type="text" name="text2">
<input type="submit" value="submit"></input>
</form>
<h2>你输入的内容: {{text2}}</h2>
</from>
再试一下:
特殊情况?
大部分情况下,我们靠对 html 实体字符的转义已经能够将 XSS 风险控制在门外。但是一些特殊情况我们不得不进行非转义字符串的输出时,隐患就会再次出现。
后端数据直出
当我们需要后端直接将数据打印在 script 标签中生成 js 变量的时候,我们通常不希望变量的值被转义从而避免前端渲染的时候值被二次转义的问题,所以我们的代码会被输出成:
<head>
<script>
window.message = { data: "</script><script>alert(0)</script>" };
</script>
</head>
试一下:
如何防范?
- 将特殊字符进行字符串转义 比如 ' " / 防止他们提前结束字符串、闭合 sciprt 标签。后端使用 json.dump() 实现。js 的 JSON.stringify 不会做这些操作
- 将输出变量字符进行 Unicode 编码
- 将输出变量字符进行 Hex 16进制编码 编码
<head>
<script>
window.message = {
data: "<\/script><script>alert(0)<\/script>",
data1: "\u003c\u002f\u0073\u0063\u0072\u0069\u0070\u0074\u003e\u003c\u0073\u0063\u0072\u0069\u0070\u0074\u003e\u0061\u006c\u0065\u0072\u0074\u0028\u0030\u0029\u003c\u002f\u0073\u0063\u0072\u0069\u0070\u0074\u003e",
data2: "\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x30\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e"
};
</script>
</head>
再试一下:
转义就万无一失了?
naive ! 点个链接一秒中招!
<a href="{{xss}}">点我</a>
链接中如果存在 javacript: 开头的协议,点击链接时浏览器会执行后面的代码,这个时候光转义是没有用的,需要对 url 协议进行白名控制,只允许 http, https, http, mailto 等安全协议
包括图片 src 属性img src="{{xss}}", iframe iframe src="{{xss}}" 都会存在这样的问题,都需要白名单处理。
处理方案总结
CSRF
CSRF(Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。 其实就是网站中的一些提交行为,被黑客利用,你在访问黑客的网站的时候,进行的操作,会被操作到其他网站上(如:你所使用的网络银行的网站)。
如何攻击?
要合理使用post与get, 为了省事儿,把应当提交的数据,做成get请求。 殊不知,这不仅仅是违反了http的标准而已,也同样会被黑客所利用。
比如,的网站中,有一个修改标题的操作使用的是 get 请求:
http://example.com/api?changetitle=CSRF
那么恶意攻击者可以使用:
<!DOCYTPE HTML>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<img src="http://example.com/api?changetitle=CSRF" />
</body>
</html>
这样的话,用户只需要访问一次黑客的网站,其实就相当于在你的网站中,操作了一次。然而用户却没有感知。
csrf攻击升级
如果你使用了post请求来处理关键业务,并且我们校验了请求中的 cookie 是否包含了当前用户的登录状态。是否就是万无一失了呢。我们来看下面这个真实的例子。
docs 中创建文档的 API 是 https://xxx.xxx/api/explorer/create/
需求通过 post 请求,带上 type 类型就可以创建一个新的文档。
攻击者在自己的网站中写下以下代码:
<!DOCTYPE html>
<html>
<body>
<form action="https://xxx.xxx.net/api/explorer/create/" method="POST">
<input type="text" hidden="true" name="type" value="2">
<input type="submit" value="我是渣渣辉! 点击领取屠龙宝刀!">
</form>
</body>
</html>
你当打开攻击者网站,并且通过诱导你成功点击按钮的时候,你就成功的发起了一次创建文档的请求。之前在我们的网站中就存在这样的问题,现在已经修复:
点击一下:
为什么这种攻击方法能生效?
浏览器表单发起 POST 的时候,会给这次请求自动带上所请求域名的 cookie,这个 cookie 中保存着我们在源站中的登录信息,(通常为 sessionid:123456qwertyu 这种)。
应对方法
- 每个 post 请求都带上一个与前用户 session 绑定的唯一 token,每次收到请求都去校验这个 token 是否合法。
- 每个 post 请求都去校验请求的 referer 是否来自于源站,否则就拒绝。但这种方法有弊端,因为浏览器可以设置 请求中不带 referer,并且低端浏览器可以伪造 referer
- 现代浏览器可以通过设置 cookie 的 SameSite属性,来防止跨域调用时带上包含 sessionid 的
cookie set-cookie: xxx-session=4050e145-043a-4ed0-977a-47d88cd4bbc7; SameSite=Lax
目前我们的文档 采用了 1、3两种方法。
光说不练假把式,攻击一个试试
你还别说,随便一找,真找到一个网站,没有校验 csrf token,我们去友情试探一下
严正声明,我是保持学习的态度,去试一试这个网站是否存在漏洞,所以找了一个提交工单的功能,来测试我们的跨站请求是否执行成功。请勿模仿
http://user.zhuolaoshi.com/m/...
为了防止装逼失败,先贴一张昨天实验成功的截图
攻击一下:
攻击成功。
一个被忽略了很久的漏洞
window.opener
带有 target="_blank"
跳转的网页拥有了浏览器 window.opener
对象赋予的对原网页的跳转权限,这可能会被恶意网站利用,例如一个恶意网站在某 UGC 网站 Po 了其恶意网址,该 UGC 网站用户在新窗口打开页面时,恶意网站利用该漏洞将原 UGC 网站跳转到伪造的钓鱼页面,用户返回到原窗口时可能会忽视浏览器 URL 已发生了变化,伪造页面即可进一步进行钓鱼或其他恶意行为:
代码如下
<script language="javascript">
window.opener.location = 'https://example.com'
</script>
想象一下,你在浏览淘宝的时候,点击了网页聊天窗口的一条外链,出去看了一眼,回来之后淘宝网已经变成了另一个域名的高仿网站,而你却未曾发掘,继续买东西把自己的钱直接打到骗子手里。
修复方法
为 target="_blank" 加上 rel="noopener noreferrer" 属性。
<a href="外跳的地址" rel="noopener noreferrer">外跳的地址</a>
缺点: 应为禁止了跳转带上 referrer,目标网址没办法检测来源地址。
还有一种方法是,所有的外部链接都替换为内部的跳转连接服务,点击连接时,先跳到内部地址,再由服务器 redirect 到外部网址。现在很多站点都是这么做的,不仅可以规避风险,还可以控制非法站点的打开
<a href="/redirect?target=http%3A%2F%2Fxxx.yyy.com">http://xxx.yyy.com</a>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。