本章内容
- 解析cookie、请求主体和查询字符串的中间件
- 实现Web程序核心功能的中间件
- 处理Web程序安全的中间件
- 提供静态文件服务的中间件
npm package: https://www.npmjs.com/package/package
解析cookie、请求主体和查询字符串的中间件
常用中间件
注意:All node patches will be removed - all middleware should work without Connect and with similar frameworks like restify(connect内置中间件已经从connect中分离处理,使用时需要单独安装、引入)
cookie-parser
- 未签名cookie
1) install
npm install cookie-parser
2) 常规(未签名)cookie测试
var connect = require('connect');
var app = connect();
var cookieParser = require('cookie-parser')
app.use(cookieParser('sysuygm is cool'));
app.use(function(req, res) {
console.log(req.cookies);
console.log(req.signedCookies);
"cookie":req.cookies,
"signedCookies":req.signedCookies
}));
});
3)客户端结果
发送
accept: application/json
accept-encoding: gzip, deflate
accept-language: en-US,en;q=0.8
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Authorization: Basic ZmFmYTpmYWZh
Cookie: foo=bar;bar=baz
收到
{
"cookie": {
"foo": "bar",
"bar": "baz",
"io": "GXHOFF8zxkVibldwAAAO"
},
"signedCookies": {}
}
- 签名cookie
签名cookie更适合敏感数据,因为用它可以验证cookie数据的完整性,有助于防止中间人攻击。有效的签名cookie放在req.signedCookies
对象中。把两个对象分开是为了体现开发者的意图。如果把签名的和未签名的cookie放到同一个对象中,常规cookie可能就会被改造,仿冒签名的cookie。
签名cookie
看起来像tobi.DDm3AcVxE9oneYnbmpqxoyhyKsk
一样,点号(.)左边的是cookie
的值,右边是在服务器上用SHA-1 HMAC
生成的加密哈希值(基于哈希的消息认证码)。如果cookie
的值或者HMAC
被改变的话,Connect
的解签会失败
1) 使用签名的cookie
cookieParser()中间件没有提供任何通过Set-Cookie响应头向HTTP客户端写出站cookie的功能。但Connect可以通过res.setHeader()函数写入多个Set-Cookie响应头
app.use(function(req, res) {
console.log(req.cookies);
// console.log(req.signedCookies);
res.setHeader('Set-Cookie',
'foo-bar;Expires=True,08 Jun 2021 10:18 GMT');
res.end(JSON.stringify( {'seccuss':true}));
});
2) curl 查看
>curl http://localhost:3000/ -H "Cookie:foo-bar" --head
HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: foo-bar;Expires=True,08 Jun 2021 10:18 GMT
Date: Sun, 15 Apr 2018 15:16:58 GMT
Connection: keep-alive
bodyParser():解析请求主体
- 假设你要用HTML标签接受用户上传的文件。用一行代码添加bodyParser()中间件就全齐了
- bodyParser()组件为你提供了req.body属性,可以用来解析JSON、x-www-form-urlencoded和multipart/form-data请求
- 如果是multipart/form-data请求,比如文件上传,则还有req.files 对象
- 这是个非常有用的组件,实际上它整合了其他三个更小的组件:json(), urlencoded(), 和multipart()
1) 先安装
npm install body-parser
2) 使用
var cookieParser = require('cookie-parser')
// app.use(bodyParser()); 最好是像下面那样显示指定子组件,否则会提示冗余
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
app.use(bodyParser.json());
app.use(function(req, res) {
console.log(req.body.username);
// console.log(req.signedCookies);
res.end(JSON.stringify( {
'username':req.username,
'password':req.pass
}));
});
POST数据
{
"username": "caicai",
"pass":"sysuygm"
}
如果bodyParser()在内存中缓存json和x-www-form-urlencoded请求主体,产生一个大字符串,那攻击者会不会做一个超级大的JSON请求主体对服务器做拒绝服务攻击呢?答案基本上是肯定的,所以Connect提供了limit()中间件组件。
limit():请求主体的限制
- limit()中间件组件的目的是帮助过滤巨型的请求
- 防止脚本攻击
(好像已经被丢弃了)
query():查询字符串解析
- 它解析查询字符串,为程序提供req.query对象
- 它会将请求发送过来的查询字符串以JSON格式作为响应返回去
(似乎已经被废弃?)
favicon():提供favicon
(似乎已经被废弃?)
使用 serve-favicon
代替,用static-favicon
会提示:
(node:3324)
DeprecationWarning: static-icon deprecated; switch to module serve-favicon
使用firefox才能看到favicon!
::1 - - [Sun, 15 Apr 2018 16:55:37 GMT] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
undefined
::1 - - [Sun, 15 Apr 2018 16:59:42 GMT] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0"
undefined
::1 - - [Sun, 15 Apr 2018 16:59:54 GMT] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0"
logger():记录请求
已被废弃,现在用morgan
代替, 直接安装morgan
body-parser deprecated bodyParser: use individual json/urlencoded middlewares connect.js:27:9
body-parser deprecated undefined extended: provide extended option ..\node_modules\body-parser\index.js:105:29
morgan deprecated undefined format: specify a format connect.js:30:9
morgan deprecated default format: use combined format connect.js:30:9
listening...
undefined
::1 - fafa [Sun, 15 Apr 2018 16:48:14 GMT] "POST /?username=pppp HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
vhost():虚拟主机
- vhost()(虚拟主机)中间件是一种通过请求头Host路由请求的简单、轻量的办法
- 这项任务通常是由反向代理完成的,可以把请求转发到运行在不同端口上的本地服务器那里
- vhost()组件在同一个Node进程中完成这一操作,它将控制权交给跟vhost实例关联的Node HTTP服务器
- 可使子域名在Express中更容易管理。
- 缺点:如果一个网站引发了崩溃,你的所有网站都会宕掉(因为它们都运行在同一个进程中)
session():会话管理
需要安装 session-parser
,且在cookie-parser
前引入。
安全相关部分
basicAuth():HTTP基本认证
基本认证是非常简单的HTTP认证机制,并且在使用时应该小心,因为如果不是通过HTTPS进行认证,用户凭证很可能会被攻击者截获
basicAuth()在最新的connect中已无法使用,且无法单独使用,似乎只有Express中才能使用。
app.use(basicAuth(connect.basicAuth(auth)));
^
TypeError: connect.basicAuth is not a function
at Object.<anonymous> (F:\后台开发\wechat\lib\connect.js:30:27)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Function.Module.runMain (module.js:693:10)
at startup (bootstrap_node.js:188:16)
at bootstrap_node.js:609:3
csrf():跨站请求伪造防护
- 跨站请求伪造(CSRF)利用站点对浏览器的信任漏洞进行攻击
- csrf()会生成一个包含24个字符的唯一ID,认证令牌,作为req.session._csrf附到用户的会话上
- 这个令牌会作为隐藏的输入控件_csrf出现在表单中,CSRF在提交时会验证这个令牌
>npm install csrf
使用:
为了确保csrf()可以访问req.body._csrf(隐藏输入控件的值)和req.session._csrf,你要确保csrf()添加在了bodyParser()和session()的下面
app.use(favicon(__dirname + '/timg.ico'));
// app.use(basicAuth(connect.basicAuth(auth)));
app.use(cookieParser('private-key'));
app.use(session({
keys:['aaa','bbb','ccc'],
maxAge:360000*24,
name:'session'
}));
app.use(bodyParser());
//添加在bodyParser()和session()的下面
app.use(csrf());
app.use(morgan());
errorHandler():开发错误处理
- 可以基于请求头域Accept提供详尽的HTML、JSON和普通文本错误响应
- errorHandler()是要用在开发过程中的,不应该出现在生产环境中
安装:
npm install errorhandler
example:
var connect = require('connect')
var errorhandler = require('errorhandler')
var app = connect()
if (process.env.NODE_ENV === 'development') {
// only use in development
app.use(errorhandler())
}
static():静态文件服务
- static()中间件实现了一个高性能的、灵活的、功能丰富的静态文件服务器,支持HTTP缓存机制、范围请
- 有对恶意路径的安全检查,默认不允许访问隐藏文件(文件名以.开头),会拒绝有害的null字节
- static()本质上是一个非常安全的、完全能胜任的静态文件服务中间件,可以保证跟目前各种HTTP客户端的兼容
- 假定你的程序遵循典型的场景,要返回./public目录下的静态资源文件