关于Web安全的问题,是一个老生常谈的问题,做为离用户最近的一层,咱们大前端应该把手伸的更远一点。javascript
咱们最多见的Web安全攻击有如下几种:css
XSS
跨站脚本攻击CSRF
跨站请求伪造URL
跳转漏洞ClickJacking
点击劫持/UI-覆盖攻击SQL Injection
SQL注入OS Command Injection
OS命令注入XSS (Cross Site Script),中文是跨站脚本攻击;其本来缩写是 CSS,但为了和层叠样式表(Cascading Style Sheet)有所区分,于是在安全领域叫作 XSS。html
恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。前端
XSS攻击能够分为3类:java
Reflected XSS
Stored XSS
DOM-based or local XSS
反射型 XSS 只是简单地把用户输入的数据 “反射” 给浏览器,这种攻击方式每每须要攻击者诱使用户点击一个恶意连接,或者提交一个表单,或者进入一个恶意网站时,注入脚本进入被攻击者的网站。程序员
伪装我是一个恶意连接(Click Me~)
web
const http = require('http');
// 启http服务
const server = http.createServer(function (req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'});
res.write('<script>while(true)alert("反射型 XSS 攻击")</script>');
res.end();
});
server.listen('8000');
复制代码
这样就产生了反射型 XSS 攻击。攻击者能够注入任意的恶意脚本进行攻击,可能注入恶做剧脚本,或者注入能获取用户隐私数据(如cookie)的脚本,这取决于攻击者的目的。正则表达式
存储型 XSS 会把用户输入的数据 "存储" 在服务器端,当浏览器请求数据时,脚本从服务器上传回并执行。这种 XSS 攻击具备很强的稳定性。
sql
比较常见的一个场景是攻击者在社区或论坛上写下一篇包含恶意 JavaScript 代码的文章或评论,文章或评论发表后,全部访问该文章或评论的用户,都会在他们的浏览器中执行这段恶意的 JavaScript 代码。shell
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>存储型 XSS</title>
</head>
<body>
<div>Try Me:<input type="text" id="input"></div>
<button id="btn">Submit</button>
<script> const input = document.getElementById('input'); const btn = document.getElementById('btn'); let val; input.addEventListener('change', e => { val = e.target.value; }, false); btn.addEventListener('click', e => { fetch('http://localhost:8000/save', { method: 'POST', body: val }); }, false); </script>
</body>
</html>
复制代码
const http = require('http');
let userInput = '';
function handleReequest (req, res) {
const method = req.method;
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
if (method === 'POST' && req.url === '/save') {
let body = '';
req.on('data', chunk => {
body += chunk;
});
req.on('end', () => {
if (body) {
userInput = body;
}
res.end();
});
} else {
res.writeHead(200, { 'Content-Type': 'text/html; charset=UTF-8' });
res.write(userInput);
res.end();
}
}
// 启http服务
const server = http.createServer((req, res)=> {
handleReequest(req, res)
});
server.listen('8000');
复制代码
基于DOM或本地的XSS实际上是一种特殊类型的反射型XSS,它是基于DOM文档对象模型的一种漏洞。能够经过DOM来动态修改页面内容,从客户端获取DOM中的数据并在本地执行。基于这个特性,就能够利用JS脚原本实现XSS漏洞的利用。
<body>
<input type="text" id="input">
<button id="btn">Submit</button>
<div id="div"></div>
<script> const input = document.getElementById('input'); const btn = document.getElementById('btn'); const div = document.getElementById('div'); let val; input.addEventListener('change', (e) => { val = e.target.value; }, false); btn.addEventListener('click', () => { div.innerHTML = `<a href=${val}>Try Me~</a>` }, false); </script>
</body>
复制代码
总结: XSS攻击的本质就是,利用一切手段在目标用户的浏览器中执行攻击脚本。
防范: 对于一切用户的输入、输出、客户端的输出内容视为不可信,在数据添加到DOM或者执行了DOM API的时候,咱们须要对内容进行HtmlEncode或JavaScriptEncode,以预防XSS攻击。
如今主流的浏览器内置了防范 XSS 的措施,例如 内容安全策略(CSP)。但对于开发者来讲,也应该寻找可靠的解决方案来防止 XSS 攻击。
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,一般缩写为CSRF或者XSRF,是一种对网站的恶意利用。
一般状况下,CSRF 攻击是攻击者借助受害者的 Cookie 骗取服务器的信任,能够在受害者绝不知情的状况下以受害者名义伪造请求发送给受攻击服务器,从而在并未受权的状况下执行在权限保护之下的操做。
关于浏览器的Cookie策略请参考HTTP Cookie。
假设有一个BBS站点http://www.a.com
:
当用户登陆以后,会设置以下 cookie: res.setHeader('Set-Cookie', ['user=william; expires=Fri, 23 Mar 2019 00:00:00 GMT;'])
当登陆后的用户发起 http://www.a.com/delete?id=666666
请求时,会删除 id 为 666666 的帖子。
CSRF攻击者准备的网站B:<img src="http://www.a.com/delete?id=666666">
当登陆用户访问攻击者的网站B时,会向 www.a.com
发起一个删除用户帖子的请求。此时若用户在切换到 www.a.com
的帖子页面刷新,会发现ID 为 666666 的帖子已经被删除。
要完成一次CSRF攻击,受害者必须依次完成两个步骤:
1.验证 HTTP Referer 字段
2.添加 token 验证
3.验证码
复制代码
尽管CSRF听起来像跨站脚本(XSS),但它与XSS很是不一样,XSS利用站点内的信任用户,而CSRF则经过假装来自受信任用户的请求来利用受信任的网站。
与XSS攻击相比,CSRF攻击每每不大流行(所以对其进行防范的资源也至关稀少)和难以防范,因此被认为比XSS更具危险性,每每同XSS一同做案!
借助未验证的URL跳转,将应用程序引导到不安全的第三方区域,从而致使的安全问题。
当用户点击后,通过服务器或者浏览器解析后,将会跳到恶意的网站中。
http://a.baidu.com/index?act=go&url=http://evil.cn/
http://b.baidu.com/safecheck.html?id=1&url=http://evil.cn/
http://c.baidu.com/f/user/passport?jumpUrl=http://evil.cn/
复制代码
经过以GET或者POST的方式接收将要跳转的URL,而后经过上面的几种方式的其中一种来跳转到目标URL。一方面,因为用户的输入会进入Meta,javascript,http头因此均可能发生相应上下文的漏洞,如xss等等,可是同时,即便只是对于URL跳转自己功能方面就存在一个缺陷,由于会将用户浏览器从可信的站点导向到不可信的站点,同时若是跳转的时候带有敏感数据同样可能将敏感数据泄漏给不可信的第三方。
① referer的限制
若是肯定传递URL参数进入的来源,咱们能够经过该方式实现安全限制,保证该URL的有效性,避免恶意用户本身生成跳转连接
② 加入有效性验证Token
咱们保证全部生成的连接都是来自于咱们可信域的,经过在生成的连接里加入用户不可控的Token对生成的连接进行校验,能够避免用户生成本身的恶意连接从而被利用,可是若是功能自己要求比较开放,可能致使有必定的限制。
ClickJacking点击劫持,也叫UI覆盖攻击,攻击者会利用一个或多个透明或不透明的层来诱骗用户支持点击按钮的操做,而实际的点击确实用户看不到的一个按钮,从而达到在用户不知情的状况下实施攻击。
这种攻击方式的关键在于能够实现页中页的<iframe>
标签,而且可使用css样式表将他不可见。
防止点击劫持有两种主要方法:
X-FRAME-OPTIONS
X-FRAME-OPTIONS是微软提出的一个http响应首部,指示浏览器不容许从其余域进行取景,专门用来防护利用iframe嵌套的点击劫持攻击。而且在IE八、Firefox3.六、Chrome4以上的版本均能很好的支持。
DENY
: 拒绝任何域加载
SAMEORIGIN
: 容许同源域下加载
ALLOW-FROM
: 能够定义容许frame加载的页面地址
顶层判断
在UI中采用防护性代码,以确保当前帧是最顶层的窗口,如: top != self || top.location != self.location || top.location != location
图片覆盖攻击(Cross Site Image Overlaying),攻击者使用一张或多张图片,利用图片的style或者可以控制的CSS,将图片覆盖在网页上,造成点击劫持。固然图片自己所带的信息可能就带有欺骗的含义,这样不须要用户点击,也能达到欺骗的目的。
<a href="http://www.a.com/delete?id=666666">
<img src="~~~" style="~~~" />
</a>
复制代码
解决方案: 在防护图片覆盖攻击时,须要检查用户提交的HTML代码中,img标签的style属性是否可能致使浮出。
SQL 注入漏洞(SQL Injection)是 Web 开发中最多见的一种安全漏洞。能够用它来从数据库获取敏感信息,或者利用数据库的特性执行添加用户,导出文件等一系列恶意操做,甚至有可能获取数据库乃至系统用户最高权限。
SQL注入攻击指的是经过构建特殊的输入做为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,经过执行SQL语句进而执行攻击者所要的操做,其主要缘由是程序没有细致地过滤用户输入的数据,导致非法数据侵入系统。
根据相关技术原理,SQL注入能够分为平台层注入和代码层注入。前者由不安全的数据库配置或数据库平台的漏洞所致;后者主要是因为程序员对输入未进行细致地过滤,从而执行了非法的数据查询。基于此,SQL注入的产生缘由一般表如今如下几方面:
① 不当的类型处理;
② 不安全的数据库配置;
③ 不合理的查询集处理;
④ 不当的错误处理;
⑤ 转义字符处理不合适;
⑥ 多个提交处理不当。
当应用程序使用输入内容来构造动态sql语句以访问数据库时,会发生sql注入攻击。若是代码使用存储过程,而这些存储过程做为包含未筛选的用户输入的字符串来传递,也会发生sql注入。sql注入可能致使攻击者使用应用程序登录在数据库中执行命令。相关的SQL注入能够经过测试工具pangolin进行。
① 永远不要信任用户的输入。对用户的输入进行校验,能够经过正则表达式,或限制长度;对单引号和双"-"进行转换等。
② 永远不要使用动态拼装sql,可使用参数化的sql或者直接使用存询存取。
③ 永远不要使用管理员权限的数据库链接,为每一个应用使用单独的权限有限的数据库链接。
④ 不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。
⑤ 应用的异常信息应该给出尽量少的提示,最好使用自定义的错误信息对原始错误信息进行包装。
⑥ sql注入的检测方法通常采起辅助软件或网站平台来检测,软件通常采用sql注入检测工具jsky,网站平台就有亿思网站安全平台检测工具MDCSOFT SCAN等。采用MDCSOFT-IPS能够有效的防护SQL注入,XSS攻击等。
OS 注入攻击是指程序提供了直接执行 Shell 命令的函数的场景,当攻击者不合理使用,且开发者对用户参数未考虑安全因素的话,就会执行恶意的命令调用,被攻击者利用。
OS 命令注入其实原理和 SQL 注入是相似的,只是场景不同而已。 在 Node.js 中可使用 exec()
执行命令经过传入一段字符串命令,并把一个错误或命令处理结果回传至回调函数中。
let userInput = "user input";
child_process.exec('ls -l ' + userInput, (err, data) => {
console.log(data);
});
复制代码
攻击者可使用一个分号";"来结束命令,并开始一个新的调用,他们可使用反引号或$()来运行子命令。还有不少潜在的滥用。
在child_process.exec引擎下,将调用执行"/bin/sh"。而不是目标程序。已发送的命令只是被传递给一个新的"/bin/ sh'进程来执行shell。 child_process.exec的名字有必定误导性 - 这是一个bash的解释器,而不是启动一个程序。这意味着,若是直接执行用户输入的参数,全部的shell字符可能会产生毁灭性的后果。
在 Node.js 中除了 exec() 以外,还有 execFile() 和 spawn() 两个方法也能够用来执行系统命令。它们和 exec() 的区别是后者是直接将一个命令字符串传给 /bin/sh 执行,而前者是提供了一个数组做为参数容器,最后参数会被直接传到 C 的命令执行方法 execve() 中,不容易执行额外的参数。
<!--child_process.execFile -->
let path = "user input";
child_process.execFile('/bin/ls', ['-l', path], (err, result) => {
console.log(result)
});
复制代码
<!--child_process.spawn-->
let path = "user input";
let ls = child_process.spawn('/bin/ls', ['-l', path])
ls.stdout.on('data', data => {
console.log(data.toString());
});
复制代码
注意:
使用spawn或execFile并不老是安全的。例如,运行/bin/find,并传入用户输入参数仍有可能致使系统被攻陷。 find命令有一些选项,容许读/写任意文件。
参考文章: