去年 security course 上的是 React,然后学了一些 一些 React 项目中可能存在的安全隐患,今年看了一下列表,正好看到了前端也有更新,所以就把这个补上了。
一个非常好学习各种安全隐患的机构是 https://owasp.org/
其实很多情况下用户不妨问可疑网站就可以解决……但是吧,educate user 永远是一件非常困难的事情,多做做防护也是必要的。
这是一个对于很多用户来说非常容易踩雷的一个坑,展示效果如下:
其工作原理是通过 forward window.opener
这个对象实现的,其实这个漏洞真的挺容易被漏看的,因为 opener
以前是会随着 Target=”_blank”
一起被 forward 到新的网页,如果新的网页稍微有那么点心思不纯,就可以写一个克隆网页,然后比较轻松的盗取用户信息。
触发代码如下:
受到攻击的网页,假设有人留了个 post 之类的:
<html>
<body>
<li>
<a href="bad.example.com" target="_blank"
>Vulnerable target using html link to open the new pagea
>
li>
<button onclick="window.open('https://bad.example.com')">
Vulnerable target using javascript to open the new page
button>
body>
html>
可疑地网页
<html>
<body>
<script>
if (window.opener) {
window.opener.location = 'https://phish.example.com';
}
script>
body>
html>
根据 MDN 上说,目前使用 target="_blank"
的话,opener
是直接设置成 null
了,除此之外,两个可以将 window.opener
设置成 null
的方式有:
rel=noopener
Cross-Origin-Opener-Policy
设置为 same-origin
这是解决一种 Man-in-the-Middle attack 的解决方式,这种攻击方式的图示是这样的:
有些网站可能会有 HTTP first 的实现,加入网址为 https://www.example.com
,它会允许用户访问 http://www.example.com
,并且重定向到 https 加密的网站。对于用户来说,大多数情况下 TA 不会输入完整的网址,而是会直接输入 www.example.com
,这时,访问就会被定向到 HTTP 版本,再重定向到 HTTPS 的版本。
换言之,如果有黑客注册了 http://www.example.com
这一域名,那么用户的信息就会先访问到 HTTP 版本的网站,黑客没有用户信息,但是可以将用户重新定向到登录网址,获取用户信息。
如使用 301:
HTTP/1.1 301 Moved Permanently
Date: ____________________
Content-Length: 0
Connection: close
Location: http://www.example.com/login.html
这时候用户被被重定向到 http://www.example.com/login.html
进行下一步操作,用户一旦提供了登录信息,那么黑客也就获得了对应的登录信息,这时候黑客会将用户重新定向到 HTTPS 的网站。
降低风险的一种方法就是使用 HTTP Strict-Transport-Security (HSTS),也就是增加一条 header,大致如下:
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
preload
是针对从未访问过该网页,没有获取对应 header 的用户,更多信息可以在 reference 中看到。
这个还是比较好理解的,如果没有服务端验证,网站一旦收到了 XSS 攻击,那么就可以直接向服务器发送有问题的数据,又或者不通过浏览器发送有问题的信息到服务器去。
图示如下:
这里点击 action 的一个原因在于镶嵌了 iframe,通过修改 opacity 就可以看到逐渐显示的 iframe:
这也是另外一个用户很容易上当的事情,因为看不到,看到按钮(特别是关闭广告的那种)又都会点击……
这里预防的方法有两种,一个是使用 X-Frame-Options
去告知浏览器不要在嵌套 tag(如 iframe、frame、embed、object)中渲染页面,第二个是使用 Content Security Policy (CSP)
防止当前页面渲染嵌套 tag。
对于 legacy support,可以使用 下面的代码:
<style>
html {
display: none;
}
style>
<script>
if (top !== self) {
top.location = self.location;
} // Attempt to escape frame
else {
document.documentElement.style.display = 'block';
} // If not in frame, make html visible
script>
CSRF 也是一个比较常见的攻击,常见的一个方式是,用户打开了一个新的网站,这个网站可以获取浏览器中的 session id,然后将修改信息送到服务器,如果服务器不做任何的验证,或者是单纯的相信了这个 session id,那么服务器就会执行相应的操作。
解决方式一般用:
synchronizer token/anti-CSRF/CSRF token,一个加密并且半随机的 token
每次用户加载该页面,或是打开了一个新的 session,就可以生成一个新的 token,随后可以通过 HTML、json、http header 等方式传输给后端。当用户发送一个请求时,后端就可以验证对应的 token 是否和当前存储的一样,从而进行验证
double submit cookies
这是存在 session 中的 CSRF token 可以被还原的情况下使用,在这个情况下,token 会被加在 cookies 和 request body 中,如果两个 token 不 match,那么该请求就会被拒绝
将 token 存在 header 中
这个是依赖于 same origin policy 实现的,这样第三方无法使用该 token 完成请求
说起来我们项目最近就在用 sonatype 查 dependencies,然后 release team 会看 sonatype 的报告,发现有 outdated packages 的危险性比较高的话就没办法 release。
除此之外,也要申明接受和传输的数据类型,如:
$.get({
url: 'https://www.example.com',
dataType: 'application/json',
});
$.post({
url: 'https://www.example.com',
data: 'test',
dataType: 'application/json',
});
这里遭受的攻击是使用 URL Fragment,也就是不会传到 server 的那部分代码,比如说 query parameter,如:用户被钓鱼网站骗了,然后点击了 https://www.example.com/#filter=Products
这个网址
其中 filter=Products
这一段会被 URLParams,然后作为 parameter 对后端发送请求。换言之,如果代码没有处理过的话,这段代码 localhost:5500/#filter=
的执行结果为:
HTML 部分代码为:
<script>
const frag = window.location.hash.substr(1);
const filterval = frag.split('filter=')[1].split('&')[0];
filterval = decodeURI(filterval);
document.querySelector('.filter').innerHTML =
'Filtering by: "'
+ filterval + '"';
script>
两个解决方案为:
innerHTML
,而是使用 textContent
修改后的结果为:
修改后的代码:
const HtmlEncode = (s) => {
const HTMLCharMap = {
'&': '&',
"'": ''',
'"': '"',
'<': '<',
'>': '>',
'\\': '\',
'`': '`',
':': ':',
};
const encodeHTMLmapper = (ch) => {
return HTMLCharMap[ch];
};
return s.replace(/[&"'<>\\`:]/g, encodeHTMLmapper);
};
const frag = window.location.hash.substr(1);
const filterval = frag.split('filter=')[1].split('&')[0];
filterval = decodeURI(filterval);
document.querySelector('.filter').textContent =
'Filtering by: "'
+ HtmlEncode(filterval) + '"';
这一段其实和上面有点像,不过上面用的是 HTML fragment,这里可能用户在 input 或者 textarea 输入了一些信息,随着 AJAX 异步发送,前端没有清洗用户数据,后端也没有清晰数据,随后造成了恶意代码在网页中被加载
同上,eval 也是单独的作用域,可以用来执行 JS,除了清理用户数据之外,也避免使用 eval,而是换成 JSON.parse(json)
简单的说,就是 cookie 中没有 secure
这个 flag,这个是要结合 man-in-the-middle attack 一起去看的,MDN 中的解释为:
Indicates that the cookie is sent to the server only when a request is made with the https: scheme (except on localhost), and therefore, is more resistant to man-in-the-middle attacks.
换言之,当 secure
这个 flag 出现的时候,浏览器在和 HTTP 网站交流时不会带上 cookie,这样可以防止第三方在第一次访问 HTTP first 的网站时携带 cookie。
如果设置了 HttpOnly
,那么就无法在 JavaScript 中获取浏览器中的 cookie,这也是一个比较常见的 XSS 攻击,出于同样的考量,现在 JS 是无法设置 HttpOnly 这个 flag 的
这里指的是使用 JavaScript 从 fragment 那里获取 URL 进行重定向,如:https://www.example.com/signon?cust=returning#url=https://www.eaxmple.com/redirect
, 这里就会使用 #
去获取重定向的 URL,再使用 JS 中的 window.location = url
的方式进行重定向。
需要注意的是,原本的 domain 是 example,重定向中的 URL 的 domain 是 eaxmple,即
example
eaxmple
也就是说稍有不注意,用户就有可能被重定向到假的页面。
解决方式有:
signon
与 fragment 相比,这里用的是 query parameter,如 https://example.com?search=%3Cscript%3Ealert('This application is vulnerable!')%3C%2Fscript%3E
,用户有可能通过点击这样的网址,从而被获取到 cookie 等关键信息。
处理的方式,大多数上面也提过了:
简单的案例有,提供这样的代码 Steal Cred .
到一些用户输入框中,如果代码钱后端都没有被清理过的话,那么一旦这个代码被保存/持久化,任何看到这条这个评论/留言的用户的用户信息就会被窃取
处理的方式还是:
没有清洗用户输入的数据,其中可能包括 '
, "
,