安全性,老是一个不可忽视的问题。许多人都认可这点,可是却不多有人真的认真地对待它。因此咱们列出了这个清单,让你在将你的应用部署到生产环境来给千万用户使用以前,作一个安全检查。javascript
如下列出的安全项,大多都具备普适性,适用于除了Node.js
外的各类语言和框架。可是,其中也包含一些用Node.js
写的小工具。php
如下是一些安全性相关的HTTP头,你的站点应该设置它们:前端
Strict-Transport-Security
:强制使用安全链接(SSL/TLS之上的HTTPS)来链接到服务器。java
X-Frame-Options
:提供对于“点击劫持”的保护。node
X-XSS-Protection
:开启大多现代浏览器内建的对于跨站脚本攻击(XSS)的过滤功能。nginx
X-Content-Type-Options
: 防止浏览器使用MIME-sniffing
来肯定响应的类型,转而使用明确的content-type
来肯定。git
Content-Security-Policy
:防止受到跨站脚本攻击以及其余跨站注入攻击。github
在Node.js
中,这些均可以经过使用Helmet模块轻松设置完毕:web
var express = require('express'); var helmet = require('helmet'); var app = express(); app.use(helmet());
Helmet
在Koa中也能使用:koa-helmet。正则表达式
固然,在许多的架构中,这些头会在Web服务器(Apache,nginx)的配置中设置,而不是在应用的代码中。若是是经过nginx配置,配置文件会相似于以下例子:
# nginx.conf add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Content-Security-Policy "default-src 'self'";
完整的例子能够参考这个nginx配置。
若是你想快速确认你的网站是否都设置这些HTTP头,你能够经过这个网站在线检查:http://cyh.herokuapp.com/cyh 。
当部署前端应用时,确保不要在代码中暴露如密钥这样的敏感数据,这将能够被全部人看到。
现今并无什么自动化检测它们的办法,可是仍是有一些手段能够用来减小不当心将敏感数据暴露在客户端的几率:
使用pull request
更新代码
创建起code review
机制
暴力破解即系统地列举全部可能的结果,并逐一尝试,来找到正确答案。在web应用中,用户登录就特别适合它发挥。
你能够经过限制用户的链接频率来防止这类的攻击。在Node.js
中,你可使用ratelimiter包。
var email = req.body.email; var limit = new Limiter({ id: email, db: db }); limit.get(function(err, limit) { });
固然,你能够将它封装成一个中间件以供你的应用使用。Express
和Koa
都已经有现成的不错的中间件:
var ratelimit = require('koa-ratelimit'); var redis = require('redis'); var koa = require('koa'); var app = koa(); var emailBasedRatelimit = ratelimit({ db: redis.createClient(), duration: 60000, max: 10, id: function (context) { return context.body.email; } }); var ipBasedRatelimit = ratelimit({ db: redis.createClient(), duration: 60000, max: 10, id: function (context) { return context.ip; } }); app.post('/login', ipBasedRatelimit, emailBasedRatelimit, handleLogin);
这里咱们所作的,就是限制了在一段给定时间内,用户能够尝试登录的次数 -- 这减小用户密码被暴力破解的风险。以上例子中的选项都是能够根据你的实际情景所改变的,因此不要简单的复制粘贴它们。。
若是你想要测试你的服务在这些场景下的表现,你可使用hydra。
对于cookie的安全使用,其重要性是不言而喻的。特别是对于动态的web应用,在如HTTP这样的无状态协议的之上,它们须要使用cookie来维持状态。
如下是一个每一个cookie能够设置的属性的列表,以及它们的含义:
secure - 这个属性告诉浏览器,仅在请求是经过HTTPS传输时,才传递cookie。
HttpOnly - 设置这个属性将禁止javascript
脚本获取到这个cookie,这能够用来帮助防止跨站脚本攻击。
domain - 这个属性用来比较请求URL中服务端的域名。若是域名匹配成功,或这是其子域名,则继续检查path
属性。
path - 除了域名,cookie可用的URL路径也能够被指定。当域名和路径都匹配时,cookie才会随请求发送。
expires - 这个属性用来设置持久化的cookie,当设置了它以后,cookie在指定的时间到达以前都不会过时。
在Node.js
中,你可使用cookies包来轻松建立cookie。可是,它是较底层的。在建立应用时,你可能更像使用它的一些封装,如cookie-session 。
var cookieSession = require('cookie-session'); var express = require('express'); var app = express(); app.use(cookieSession({ name: 'session', keys: [ process.env.COOKIE_KEY1, process.env.COOKIE_KEY2 ] })); app.use(function (req, res, next) { var n = req.session.views || 0; req.session.views = n++; res.end(n + ' views'); }); app.listen(3000);
(以上例子取自cookie-session模块的文档)
跨站请求伪造(CSRF)是一种迫使用户在他们已登陆的web应用中,执行一个并不是他们原意的操做的攻击手段。这种攻击经常用于那些会改变用户的状态的请求,一般它们并不窃取数据,由于攻击者并不能看到响应的内容。
在Node.js
中,你可使用csrf模块来缓和这种攻击。它一样是很是底层的,你可能更喜欢使用如csurf这样的Express
中间件。
在路由层,能够会有以下代码:
var cookieParser = require('cookie-parser'); var csrf = require('csurf'); var bodyParser = require('body-parser'); var express = require('express'); // setup route middlewares var csrfProtection = csrf({ cookie: true }); var parseForm = bodyParser.urlencoded({ extended: false }); // create express app var app = express(); // we need this because "cookie" is true in csrfProtection app.use(cookieParser()); app.get('/form', csrfProtection, function(req, res) { // pass the csrfToken to the view res.render('send', { csrfToken: req.csrfToken() }); }); app.post('/process', parseForm, csrfProtection, function(req, res) { res.send('data is being processed'); });
在展现层,你须要使用CSRF token
:
<form action="/process" method="POST"> <input type="hidden" name="_csrf" value="{{csrfToken}}"> Favorite color: <input type="text" name="favoriteColor"> <button type="submit">Submit</button> </form>
(以上例子取自csurf模块的文档)
如下是两种相似的,可是略有不一样的攻击方式,一种关于跨站脚本,而另外一种则关于存储。
非持久化的XSS攻击 在攻击者向指定的URL的响应HTML中注入可执行的JavaScript
代码时发生。
持久化的XSS攻击 在应用存储未通过滤的用户输入时发生。用户输入的代码会在你的应用环境下执行。
为了防护这类攻击,请确保你老是检查并过滤了用户的输入内容。
在用户的输入中包含部分或完整的SQL查询语句时,SQL注入就有可能发生。它可能会读取敏感数据,或是直接删除数据。
例如:
select title, author from books where id=$id
以上这个例子中,$id
来自于用户输入。用户输入2 or 1=1
也能够。这个查询可能会变成:
select title, author from books where id=2 or 1=1
最简单的预防方法则是使用参数化查询(parameterized queries)或预处理语句(prepared statements)。
若是你正在经过Node.js
使用PostgreSQL
。那么你可使用node-postgres模块,来建立参数化查询:
var q = 'SELECT name FROM books WHERE id = $1'; client.query(q, ['3'], function(err, result) {});
攻击者使用命令注入来在远程web服务器中运行系统命令。经过命令注入,攻击者甚至能够取得系统的密码。
实践中,若是你有一个URL:
https://example.com/downloads?file=user1.txt
它能够变成:
https://example.com/downloads?file=%3Bcat%20/etc/passwd
在这个例子中,%3B
会变成一个分号。因此将会运行多条系统命令。
为了预防这类攻击,请确保老是检查过滤了用户的输入内容。
咱们也能够以Node.js
的角度来讲:
child_process.exec('ls', function (err, data) { console.log(data); });
在child_process.exec
的底层,它调用了/bin/sh
,因此它是一个bash
解释器,而不只仅是只能执行用户程序。
当用户的输入是一个反引号或$()
时,将它们传入这个方法就很危险了。
能够经过使用child_process.execFile
来解决上面这个问题。
因为HTTP是明文传输的,因此咱们须要经过一个SSL/TLS通道来加密,即HTTPS。现在高级别的加密方式已被广泛使用,可是,若是在服务端缺少配置,也可能会致使服务端使用低级别的加密,或不加密。
你须要测试:
密码,密钥和重协商(renegotiation)都已经合法妥善得配置完毕。
证书的合法性。
使用如nmap
和sslyze
这样的工具可使这项工做很是简单。
nmap --script ssl-cert,ssl-enum-ciphers -p 443,465,993,995 www.example.com
使用sslyze
来检查SSL/TSL:
./sslyze.py --regular example.com:443
在上文的配置管理章节咱们已经对其有了接触 - Strict-Transport-Security
头会强制使用HTTPS来链接服务器。如下是一个Twitter的例子:
strict-transport-security:max-age=631138519
这里的max-age
定义了浏览器须要自动将全部HTTP请求转换成HTTPS的秒数。
对于它的测试是很是简单的:
curl -s -D- https://twitter.com/ | grep -i Strict
帐号锁定用于缓和暴力破解带来的拒绝服务方面的影响。实践中,它意味着,当用户尝试了几回登录并失败后,将在其后的一段内,禁止他的登录操做。
可使用以前提到的rate-limiter
来阻止这类攻击。
这类攻击主要是因为一些正则表达式,在极端状况下,会变得性能及其糟糕。这些正则被称为恶魔正则(Evil Regexes):
对于重复文本进行分组
在重复的分组内又有重复内容
([a-zA-Z]+)*
, (a+)+
或 (a|a?)+
在如aaaaaaaaaaaaaaaaaaaaaaaa!
这样的输入面前,都是脆弱的。这会引发大量的计算。更多详情能够参考ReDos。
可使用Node.js
工具safe-regex这检测你的正则:
$ node safe.js '(beep|boop)*' true $ node safe.js '(a+){10}' false
一些错误场景可能会致使应用泄露底层的应用架构信息,如:like: X-Powered-By:Express
。
堆栈信息可能本身自己并无什么用,但它常常能泄露一些攻击者很是感兴趣的信息。将堆栈信息返回出来是很是很差的实践。你须要将它们记录在日志中,而不是展现给用户。
更强的能力意味着更大的责任 - NPM有这许多能够现成使用的包,可是代价是:你须要检查这些包自己是否存在安全问题。
幸运的是Node Security project
(nsp)是一个很是棒的工具,来检查你使用的模块是不是易被一些已知的手段攻击的。
npm i nsp -g # either audit the shrinkwrap nsp audit-shrinkwrap # or the package.json nsp audit-package
这个清单主要根据OWASP
维护的Web Application Security Testing Cheat Sheet所列。