Skip to content

XSS

XSS(跨网站指令码 Cross-site scripting) 是一种网站应用程式的安全漏洞攻击,是代码注入的一种。它允许恶意使用者将程式码注入到网页上,其他使用者在观看网页时就会受到影响。这类攻击通常包含了 HTML 以及使用者端脚本语言。

XSS 分为三种:反射型存储型DOM-based

如何攻击

XSS 通过修改 HTML 节点或者执行 JS 代码来攻击网站。例如,通过 URL 获取某些参数:

html
<!-- http://www.domain.com?name=<script>alert(1)</script> -->
<div>{{name}}</div>

上述 URL 输入可能会将 HTML 改为 <div><script>alert(1)</script></div>,这样页面中就凭空多了一段可执行脚本。这种攻击类型是反射型攻击,也可以说是 DOM-based 攻击

也有另一种场景,比如写了一篇包含攻击代码 <script>alert(1)</script> 的文章,那么可能浏览文章的用户都会被攻击到。这种攻击类型是存储型攻击,也可以说是 DOM-based 攻击,并且这种攻击打击面更广。

如何防御

字符转义

最普遍的做法是转义输入输出的内容,对于引号,尖括号,斜杠进行转义:

js
function escape(str) {
    str = str.replace(/&/g, '&amp;')
    str = str.replace(/</g, '&lt;')
    str = str.replace(/>/g, '&gt;')
    str = str.replace(/"/g, '&quto;')
    str = str.replace(/'/g, '&#39;')
    str = str.replace(/`/g, '&#96;')
    str = str.replace(/\//g, '&#x2F;')
    return str
}

通过转义可以将攻击代码 <script>alert(1)</script> 变成:

js
// -> &lt;script&gt;alert(1)&lt;&#x2F;script&gt;
escape('<script>alert(1)</script>');

对于显示富文本来说,不能通过上面的办法来转义所有字符,因为这样会把需要的格式也过滤掉。这种情况通常采用白名单过滤的办法,当然也可以通过黑名单过滤,但是考虑到需要过滤的标签和标签属性实在太多,更加推荐使用白名单的方式。

js
var xss = require('xss');
var html = xss('<h1 id="title">XSS Demo</h1><script>alert("xss");</script>');
// -> <h1>XSS Demo</h1>&lt;script&gt;alert("xss");&lt;/script&gt;
console.log(html);

以上示例使用了 js-xss 来实现。可以看到在输出中保留了 h1 标签且过滤了 script 标签。

CSP

另外,我们还可以通过 CSP 来尽量减少 XSS 攻击。CSP 本质上也是建立白名单,规定了浏览器只能够执行特定来源的代码。

CSP(内容安全策略 Content Security Policy) 是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻击都是主要的手段。

通常可以通过 HTTP Header 中的 Content-Security-Policy 来开启 CSP:

  • 只允许加载本站资源:

    http
    Content-Security-Policy: default-src 'self'
  • 只允许加载 HTTPS 协议图片:

    http
    Content-Security-Policy: img-src https://*
  • 允许加载任何来源框架:

    http
    Content-Security-Policy: child-src 'none'

除此之外,<meta> 元素也可以被用来配置该策略,例如:

html
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

更多属性可以查看这里

CSRF

CSRF(跨站请求伪造 Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF,是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。跟 XSS 相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

简单点说,CSRF 就是利用用户的登录态发起恶意请求。

如何攻击

假设网站中有一个通过 GET 请求提交用户评论的接口,那么攻击者就可以在钓鱼网站中加入一个图片,图片的地址就是评论接口:

html
<img src="http://www.domain.com/xxx?comment='attack'" />

如果接口是 POST 提交的,就相对麻烦点,需要用表单来提交接口:

html
<form action="http://www.domain.com/xxx" id="CSRF" method="post">
    <input name="comment" value="attack" type="hidden" />
</form>

如何防御

防范 CSRF 可以遵循以下几种规则:

  • GET 请求不对数据进行修改
  • 阻止不明外域的访问
    • 同源检测,阻止第三方网站请求接口
    • Samesite Cookie,不让第三方网站访问到用户 Cookie
  • 提交时要求附加本域才能获取的信息
    • CSRF Token
    • 双重 Cookie 验证

SameSite

可以对 Cookie 设置 SameSite 属性。该属性设置 Cookie 不随着跨域请求发送,该属性可以很大程度减少 CSRF 的攻击,但是该属性目前并不是所有浏览器都兼容。

验证 Referer

对于需要防范 CSRF 的请求,我们可以通过验证 Referer 来判断该请求是否为第三方网站发起的。

Token

服务器下发一个随机 Token(算法不能复杂),每次发起请求时将 Token 携带上,服务器验证 Token 是否有效。

点击劫持

如何攻击

点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过 iframe 嵌入自己的网页中,并将 iframe 设置为透明,然后诱使用户在该页面上进行操作,此时用户将在不知情的情况下点击透明的 iframe 页面。

防御方法

还是让后端大佬解决,使用一个 HTTP 响应头 —— X-Frame-OptionsX-Frame-Options 可以说是为了解决点击劫持而生的,它有三个可选的值:

  1. DENY: 浏览器会拒绝当前页面加载任何 frame 页面
  2. SAMEORIGIN: frame 页面的地址只能为同源域名下的页面
  3. ALLOW-FROM origin: 允许 frame加载的页面地址(这是一个被弃用的指令,不再适用于现代浏览器 - MDN

window.opener

如何攻击

window.opener 返回打开当前窗口的那个窗口的引用,例如:在 A 页面中打开了 B 页面,B 页面的 window.opener 返回 A 页面的 window

一般来说,打开同源的页面,不会有什么问题。但对于跨域的外部链接来说,存在一个被钓鱼的风险。比如你正在浏览购物网站,从当前网页打开了某个外部链接,在打开的外部页面,可以通过 window.opener.location 改写来源站点的地址。利用这一点,将来源站点改写到钓鱼站点页面上,例如跳转到伪造的高仿购物页面,当再回到购物页面的时候,是很难发现购物网站的地址已经被修改了的,这个时候你的账号就存在被钓鱼的可能了。

通过以下方式跳转链接,新页面的 window.opener 会保留原父页面 window 对象的引用:

  • 通过 window.open 打开新页面,并且该 API 第 3 个参数未设置 noopener参考文档
  • 通过 a 标签打开页面,并且该 a 标签设置了 target="_blank"rel="opener" 属性

如何防御

  1. 通过 a 标签跳转链接,不同时设置 target="_blank"rel="opener" 属性
  2. 通过 window.open 跳转链接,第 3 个参数 windowFeatures 设置 noopener 属性
  3. HTTP 响应头 Cross-Origin-Opener-Policy 的值设置为 same-origin
  4. 将外链替换为内部的跳转连接服务,跳转时先跳到内部地址,再由服务器 redirect 到外链

中间人攻击

如何攻击

中间人攻击是攻击方同时与服务端和客户端建立起了连接,并让对方认为连接是安全的,但是实际上整个通信过程都被攻击者控制了。攻击者不仅能获得双方的通信信息,还能修改通信信息。中间人攻击的本质是客户端和服务端之间的认证和信任问题。

如何防御

对称加密、非对称加密、混合加密技术都没有有效防止中间人攻击,因为中间人可以截取首次传输的密钥并偷天换日,而客户端或服务端并无法得知。HTTPS 作为防止中间人攻击的终极手段,引入证书机制解决了客户端和服务端的信任问题,从而较为有效的防止了中间人攻击。

密码安全

密码安全虽然大多是后端的事情,但是作为一名优秀的前端程序员也需要熟悉这方面的知识。

加盐

对于密码存储来说,必然是不能明文存储在数据库中的,否则一旦数据库泄露,会对用户造成很大的损失。并且不建议只对密码单纯通过加密算法加密,因为存在彩虹表的关系。

通常需要对密码加盐,然后进行几次不同加密算法的加密。

js
// 加盐也就是给原密码添加字符串,增加原密码长度
sha256(sha1(md5(salt + password + salt)));

但是加盐并不能阻止别人盗取账号,只能确保即使数据库泄露,也不会暴露用户的真实密码。一旦攻击者得到了用户的账号,可以通过暴力破解的方式破解密码。对于这种情况,通常使用验证码增加延时或者限制尝试次数的方式。并且一旦用户输入了错误的密码,也不能直接提示用户输错密码,而应该提示账号或密码错误。

SQL 注入

如何攻击

拼接 SQL 时未仔细过滤,黑客可提交畸形数据改变语义。比如查某个文章,提交了这样的数据 id=-1 or 1=1 等。1=1 永远是 true,导致 where 语句永远是 ture。那么查询的结果相当于整张表的内容,攻击者就达到了目的。或者,通过屏幕上的报错提示推测 SQL 语句等。

如何防御

  1. 禁止目标网站利用动态拼接字符串的方式访问数据库
  2. 减少不必要的数据库抛出的错误信息
  3. 对数据库的操作赋予严格的权限控制
  4. 净化和过滤掉不必要的 SQL 保留字,比如:where, or, exec

参考资料