Web安全

XSS

XSS是一种代码注入方式的跨站脚本攻击,为了与层叠样式表CSS区分,取名为XSS。

起因通常是由于黑客往 HTML、DOM 中插入了网站没有校验的恶意脚本,当用户浏览时,浏览器无法确认这些脚本是正常的还是注入的页面内容,当执行到这些恶意脚本时就会对用户进行 cookie 窃取、监听用户行为收集信息发往黑客服务器、会话劫持和修改页面 DOM 恶意攻击等。

恶意脚本的注入方式,归纳起来有三种类型:存储型 XSS 攻击、反射型 XSS 攻击、基于 DOM XSS 攻击

存储型 XSS 攻击

存储型 XSS 攻击是将恶意代码存储到网站服务器,如果出现漏洞传播速度、影响范围较为广泛,常见于社区、论坛等带有内容保存的系统中

首先黑客利用网站漏洞将恶意代码发送至服务器,而服务器未经校验就保存了,其他用户通过浏览器向网站服务器请求内容,浏览器解析恶意代码并执行,黑客的意图就成功了,接下来黑客可以通过脚本读取用户信息上传到黑客自己的服务器,或做一些其它的非法操作。

反射型 XSS 攻击

反射型 XSS 攻击是将恶意代码拼接在 URL 处,常见于网站搜索、跳转等,一个特点是需要黑客诱导用户点击 URL 实现代码注入

不同于存储型,反射型是请求到达网站服务器后,不会存储到数据库。

DOM型XSS攻击

不同于存储型,反射型是请求到达网站服务器后,不会存储到数据库。

XSS防范

为了防止 XSS 攻击,我们需要验证用户的输入,做到有效检测。对输出的数据做编码,防止恶意脚本注入成功在浏览器端之行。

  • 确定性类型字段加强校验:当一些字段是确定性类型的,在输入内容时应按照相应规则加强校验,例如手机号、邮箱等信息。
  • HTML 模版转义:对于前后端一体的应用,拼接 HTML 模版这些也是必不可少的,应对 HTML 模版做好充分转义。一些常用的模版引擎:pug、ejs 这些默认都带有转义功能,来防止 XSS 攻击。
  • 设置 httpOnly:禁止用户读取 cookie,就算注入恶意代码,也无法读取 cookie 信息冒充用户提交信息。
  • 过滤和转码:关键字符做过滤或转码,如果是过滤,直接过滤掉指定的关键词,页面就不会展示了。如果是转码,不会过滤掉但也不会让其执行,例如 <script> 转义为 <script>推荐一个库 js-xss,用于对用户输入的内容进行过滤,以避免遭受 XSS 攻击
  • CSP:一种内容安全策略,需要在服务端设置

CSRF

跨站请求伪造(Cross-Site Request Forgery,简称 CSRF)是一种冒充受信任用户在当前已登陆 Web 应用程序上执行非本意操作的攻击方式。

CSRF 攻击简单来说,是黑客通过一种技术手段诱骗用户浏览器访问一个曾经受信任的网站去执行一些非法操作。这利用了 Web 中用户身份验证的一个漏洞:“简单的身份验证只保证了请求是发自用户的浏览器,却不能验证请求本身是否为用户自愿发出的”。

CSRF 攻击预防

// codingmay/app.js
app.post('/api/delete/article/:id', (req, res) => {
  if (req.get('Origin').indexOf('www.codingmay.com') < 0) {
    return res.json({ code: 'ERROR', message: 'origin error' })
  }
});

Cookie 的 SameSite 属性有三个值:

  • Strict:最严格,完全禁止第三方 Cookie。
  • Lax:相对宽松,第三方站点 Get 方式的 Form 表单(<form method="GET">)链接 (<a></a>)预加载(<link rel="prerender"/>) 这三种情况下会发送 Cookie 信息。
  • None:关闭 SameSite 属性,任何情况下都会发送 Cookie 信息。
// codingmay/app.js
app.get('/user/auth', (req, res) => {
  res.cookie('userId', 'user_001', {
    expires: new Date(Date.now() + 1000 * 60 * 10),
    sameSite: 'strict' // 设置为严格模式,完全禁止第三方 Cookie
  })
});

CSRF Token

解决 CSRF 攻击另一个常用的解决方案是 CSRF Token 验证,第一步是浏览器向服务器请求时,服务器返回一个随机 Token。

Node.js 中有一个中间件 expressjs/csurf 也可用来预防 CSRF 攻击。