目录

NodeJS 安全

NodeJS 安全

1 NodeJS 基础

1.1 定义与原理

是一个开源的、跨平台的运行时环境,有了它,开发人员可以使用 JavaScript 创建各种服务器端工具和应用程序。此运行时要用于浏览器上下文之外(即可以直接运行于计算机或服务器操作系统上)。

1.2 常见框架

Express 是最流行的 Node 框架,是许多其它流行 Node 框架的底层库。虽然 Express 本身是极简风格的,但是开发人员通过创建各类兼容的中间件包解决了几乎所有的 web 开发问题。这些库可以实现 cookie、会话、用户登录、URL 参数、POST 数据、安全头等功能。

特点:

  • 为不同 URL 路径中使用不同 HTTP 动词的请求(路由)编写处理程序。

  • 集成了「视图」渲染引擎,以便通过将数据插入模板来生成响应。

  • 设置常见 Web 应用设置,比如用于连接的端口,以及渲染响应模板的位置。

  • 在请求处理管道的任何位置添加额外的请求处理「中间件」。

1.3 Node 环境

1.3.1 基础环境

下载地址:https://nodejs.org/en/

从 HelloWorld 程序开始

1
2
3
4
5
const http = require('http');
http.createServer((req, res)=>{
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end("Hello World\n");
}).listen(80);

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20201227172203.png-water_print

1.3.2 Express 环境简介

安装

1
npm install express

从 HelloWorld 程序开始

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const express = require('express');
const app = express();

app.get('/hello', (req, res) => {
  res.send("Hello World");
});

app.listen(80, () => {
  console.log("listen on 80");
});

2 Express 及其组件

2.1 路由模块

路由的作用就是解析 URL,调用对应的控制器(的方法,并传递参数)。

客户端的请求是以 URL 的形式传递给服务器的。传统 WEB 开发中,URL 对应服务器上某个目录下的某个文件。MVC 开发则改变了这种对应关系,WEB 服务器会截获所有请求,不做资源存在性检查,直接转发给网站的路由程序。

路由器再调用相关的控制器。控制器调用相关的服务,并返回视图对象。路由器再从视图对象中提取生成好的网页代码返回给 Web 服务器,最终返回给客户端。

举例:

download.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {
  res.send('download page');
});

router.get('/docs', (req, res) => {
  res.send('download page docs');
});
module.exports = router;

main.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const express = require('express');
const app = express();
const download = require(__dirname + "/download.js");

app.use('/download', download);
app.get('/', (req, res) => {
    res.send("Hello World");
});
app.get('/news', (req, res) => {
    res.send("news page");
});
app.listen(80, () => {
    console.log('listen on 80');
});

app.use 表示对所有路由,和所有方法都调用中间函数

app.get ('/')表示对 get 方法开头的 URL 调用中间函数

app.use('/download') 表示对 /download 头的所有,调用中间函数。

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20201227200630.png-water_print

2.1 中间件

中间件在 Express 应用中得到了广泛使用。大多数应用会使用第三方中间件来简化常见的 web 开发任务,比如 cookie、会话、用户身份验证、访问请求 POST 和 JSON 数据,日志记录、压缩 HTTP 响应等等。

一些中间件的引入顺序很重要(例如,如果会话中间件依赖于 cookie 中间件,则必须先添加 cookie 处理器)。绝大多数情况下要先调用中间件后设置路由,否则路由处理器将无法访问中间件的功能。morgan 是一个日志记录的中间件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const express = require('express');
const app = express();
const download = require(__dirname + "/download.js");
const logger = require("morgan");
app.use(logger('dev'));

app.listen(80, () => {
    console.log('listen on 80');
});

app.use('/download', download);

app.get('/', (req, res) => {
    res.send("Hello World");
});
app.get('/news', (req, res) => {
    res.send("news page");
});

中间件和路由函数是按照声明顺序调用的

2.2 渲染

PUG 模板引擎是一款健壮、灵活、功能丰富的 HTML 模板引擎专门为 Node 平台开发。 PUG 是由 Jade 改名而来。

Pug 通过缩进(表示标签间的嵌套关系)的方式来编写代码的过程,在编译的过程中,不需要考虑标签是否闭合的问题。

pugDemo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const express = require('express');
const app = express();
const path = require("path");

app.listen(80, () => {
    console.log("listening on 80");
});

app.set("views", path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.get('/', function(req, res) {
    res.render('index', {title: 'Hello', message: 'hello world pug engine'})
})

views/index.pug

1
2
3
4
5
html
    head
        title=title
    body
        h1=message

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20201227202805.png-water_print

3 威胁分析

3.1 常见漏洞

代码执行,命令执行,XSS,SQL 注入,SSRF,文件上传

nodejs 对一些经典 web 漏洞,有一定防护,有的漏洞在其他语言中不明显。但在 nodejs 中比较突出。

require 链劫持(软件投毒),正则表达式拒绝服务(ReDoS),不安全的包。

3.2 代码审计

3.2.1 项目结构

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20201227204456.png-water_print

3.2.2 审计依赖

nsp 是检测包依赖的工具,被收购后,在 npm >= 6.0.0,使用如下指令:

1
2
npm audit
npm audit fix

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20201227205007.png-water_print

3.2.3 不安全的对象直接引用

1
2
3
4
5
6
7
8
9
function isAdmin(req, res, next) {
  if(req.user.role == "admin") // 检测账号用户是否是 admin
    return next();
  // 如果不是,报错
  res.redirect('/403');
}
app.get('/admin', isAdmin, function(req, res) {
  res.send('secret');
});

3.2.4 敏感信息泄露

1
2
3
app.use(function(err, req, res, next) {
  res.status(500).send(err.stack);
})

3.2.5 未经验证的重定向和转发

SSRF

1
2
3
4
5
6
7
app.use('/redirect', function(req, res) {
  request(req.query.url, function(error, response, body){
    if(err) {
      return res.send(body);
    }
  })
})

3.3 安全配置

Helmet

  • Strict-Transport-Security 强制实施与服务器的安全 (http over SSL / TLS)连接

  • X-Frame-Options 提供点击劫持保护

  • XSS-Protection 支持在最新的 Web 浏览器中内置的跨站点脚本(XSS)过滤器

  • X-Content-Type-Options 可防止浏览器从声明的内容类型中嗅探响应

  • Content-Security-Policy 可防止各种攻击,包括跨站点脚本和其他跨站点注入

4 常见问题

4.1 XSS

1
2
3
sudo docker pull registry.cn-shanghai.aliyuncs.com/yhskc/chatsys:latest

sudo docker run -d -p 0.0.0.0:32888:80 registry.cn-shanghai.aliyuncs.com/yhskc/chatsys

Pug XSS

练习一:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210111114309.png-water_print

采用了转义的方式进行输出,因此无法绕过

练习二:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210111114357.png-water_print

采用未转义的方式,因此直接采用常规 payload 即可

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210111114440.png-water_print

练习三:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210111114516.png-water_print

<script> 标签中,因此直接采用行内闭合后拼接的方式可以 XSS,payload:1;alert(1)

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210111114657.png-water_print

练习四:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210111114739.png-water_print

仍然是未转义模式,直接拼接即可

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210111114850.png-water_print

练习五:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210111114915.png-water_print

发现其过滤了尖括号、alert、分号等敏感字符

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210111115024.png-water_print

利用 JSFuck 绕过滤

1
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210111115357.png-water_print