Nodejs安全的提醒

Nodejs安全的提醒

Node.js变得越来越成熟,毋庸置疑——尽管如此,仍然没有多少关于安全方面的知识。

在这篇文章中我会分享一些就Node.js安全方面的东西。

摒弃eval,或者友好相处 eval不是唯一一个你在服务端情况下要避免使用在以下每种表达式中的:

  • setInterval(String, 2)

  • setTimeout(String, 2)

  • new Function(String)

    但是为什么你需要避免eval呢?

    它会暴露你的代码从而被注入攻击(用户输入的eval-哇哦,连写下来都困难,请别这么干)同时这也是效率低下的(因为它会让解释器来执行)。

请使用严格模式!

通过这个标识你可以使用受限制的javascript变量。这样排除了一些未知错误。

不可删除的属性

‘use strict’;
delete Object.prototype; // 类型错误

对象名必须唯一

‘use strict’;
var obj = {
a: 1, a: 2 }; // syntax error 语法错误

不要使用 with 关键字

var obj = { x: 17 };
with (obj) // !!! syntax error 语法错误 {

}

想查看这些位置错误的完整列表,参照MDN

代码分析

可以借助JSLint, JSHint 或者 ESLint这几个工具。静态的代码分析可以在早期找出你代码中很多潜在的问题。

测试

更加不必说的,测试!测试!再多一点测试!

好吧,这不仅仅只是单元测试,你最好做一些循序渐进的测试。

向 sudo node app.js 说不!

我经常看到这个:人们总是用超级用户权限去运行他们的node应用。为什么?

因为他们想让他们的应用监听80端口和443端口。


这样是错误的!万一你的进程中发生一个错误或者bug可能会导致你整个系统崩溃,因为他被授权可以做任何事。


取而代之,你应该使用一个HTTP 服务器或者HTTP代理在传递请求。这可能是nginx、apache或者凡是你能说出的。

避免command注入

下面的这段代码有什么问题呢?

child_process.exec(‘ls’, function (err, data) {

console.log(data); });

首先,在最前面的 child_process.exec   产生了一个对 /bin/sh   的调用。它是个bash 解释器而不是个程序加载器。

这在传递用户输入到这个方法的时候是有问题的因为他可以使一个重音符或者$(),攻击者可以注入一条新的命令。

要搞定这个问题只要使用child_process.execFile  就可以了。

原始的博文处理了命令行注入,参阅LiftSecurity。

临时文件

在创建文件时要格外注意,比如上传文件。这些文件很容易就耗尽你的硬盘空间。

要搞定这种问题的话,你的解决办法就是用流(Streams)。

加固你的web应用

这个部分不仅仅是针对Node,包括在一般情况你怎么使你的web应用更安全。

跨站脚本攻击

当攻击者通过HTTP响应注入了一段可执行的代码。当一个应用很容易受这种类型的攻击时,它会回传一段非法输入到客户端(通常以javascript形式编写)。它使得攻击者可以偷取cookie,到剪贴板并自行修改页面。

举例

http://example.com/index.php?user=<script>alert(123)</script>

如果查询串在不通过校验的情况下回传到客户端,并添加到DOM中,它就会执行。

怎样预防呢?

  • 永远不要添加不可信的数据到DOM中
  • 在添加之前先编码

更多信息跨站脚本攻击和怎样避免。

阻止cookie窃取

在默认情况下,cookie是可以被相同域的javascript读取的。这样的话,在发生跨站脚本攻击时就非常危险了。但是还不止这点:一个三方的javascript类库也可以读取它们。

举例

var cookies = document.cookie.split(’; ');

怎么防止这种情况呢?

你可以看看cookie中的HttpOnly标识,可以让javascript不能读取它。

** 内容安全政策**

Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. CSP可以通过Content-Security-Policy 这个HTTP 头信息来生效。

Content-Security-Policy: default-src 'self' *.mydomain.com

这个会允许可信的域以及它的子域的内容。

伪造跨站请求

CSRF is an attack which forces an end user to execute unwanted actions on a web application in which he/she is currently authenticated. 这会发生是因为每个到一个网站的请求都附带着cookie,甚至包括那些其他网站的请求也是。

举例

上面的片段很容易导致结果是你的用户信息被删除。

怎样避免它呢?

为了防止跨站请求伪装,你需要实现同步的token,幸运的是在Node.js已经为你实现了。下面是他怎么工作的:

  1. 当一个GET请求开始接受服务端的CSRF检测的,如果不存在,则创建一个。
  2. 当一个显示用户输入的时候,保证添加一个含有CSRF token值的隐藏输入。
  3. 当表单提交的时候,保证数据是表单提交的,而且session是匹配的。

哪里翻译的不对或者有问题,欢迎指出。英文不溜求不拍砖。

作者:Gergely Nemeth 翻译:luoyjx 译文地址:Node.js安全的提醒 英文原文: Node.js Security Tips


3 回复

Node.js 安全的提醒

Node.js 越来越成熟,但有关安全方面的知识却相对较少。在这篇文章中,我会分享一些有关 Node.js 安全的知识点。

摒弃 eval,或者友好相处

eval 不是唯一需要避免使用的表达式,如 setInterval(String, 2)setTimeout(String, 2) 也应避免使用。new Function(String) 同样需要注意。

为什么需要避免 eval

eval 会使代码暴露于注入攻击,并且效率低下,因为它会让解释器来执行字符串代码。

// 避免这样做
const userInput = "1 + 2";
eval(userInput); // 3

使用严格模式

使用 'use strict'; 可以帮助你排除一些未知错误。

'use strict';

// 示例代码
delete Object.prototype; // 类型错误

对象名必须唯一

'use strict';

// 示例代码
var obj = {
  a: 1,
  a: 2 // 语法错误
};

不要使用 with 关键字

var obj = { x: 17 };

// 错误示例
with (obj) { // 语法错误
  console.log(x); // 17
}

代码分析

可以使用 JSLint, JSHint 或 ESLint 进行静态代码分析。

// 示例代码
const jshint = require('jshint').JSHINT;
const code = 'function test() { return 0; }';
jshint(code);

测试

测试是必不可少的,不仅仅是单元测试,还要进行集成测试和端到端测试。

// 示例代码
const assert = require('assert');
const myFunction = require('./myModule');

assert.strictEqual(myFunction(1, 2), 3);

不要用 sudo 运行 Node 应用

使用 sudo node app.js 运行应用是错误的做法,因为这样会使应用拥有超级用户权限,一旦发生错误可能导致系统崩溃。

// 正确做法
const http = require('http');
http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(8080);

避免命令注入

// 错误示例
const { exec } = require('child_process');
exec('ls', (err, data) => {
  console.log(data);
});

// 正确做法
const { execFile } = require('child_process');
execFile('ls', [], (err, data) => {
  console.log(data);
});

临时文件

在创建文件时要格外注意,例如上传文件。使用流(Streams)可以避免硬盘空间耗尽。

const fs = require('fs');
const fileStream = fs.createWriteStream('file.txt');
fileStream.write('Hello World\n');
fileStream.end();

加固你的 Web 应用

跨站脚本攻击(XSS)

防止 XSS 攻击的一种方法是在将数据添加到 DOM 之前进行编码。

// 示例代码
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  const user = req.query.user;
  res.send(`<div>${user}</div>`); // 危险
  res.send(`<div>${encodeURIComponent(user)}</div>`); // 安全
});

阻止 cookie 窃取

使用 HttpOnly 标识可以防止 JavaScript 读取 cookie。

res.cookie('sessionId', sessionId, { httpOnly: true });

内容安全策略(CSP)

使用 CSP 可以增强安全性,防止 XSS 和数据注入攻击。

// 示例代码
res.setHeader('Content-Security-Policy', "default-src 'self' *.mydomain.com");

防止跨站请求伪造(CSRF)

使用同步的 CSRF token 来防止 CSRF 攻击。

// 示例代码
const csrf = require('csurf');
const cookieParser = require('cookie-parser');

app.use(cookieParser());
app.use(csrf({ cookie: true }));

app.get('/form', (req, res) => {
  res.send(`
    <form method="POST">
      <input type="hidden" name="_csrf" value="${req.csrfToken()}">
      <button type="submit">Submit</button>
    </form>
  `);
});

希望这些提示能够帮助你更好地保护你的 Node.js 应用。


好多内容好像被过滤了,可能是 markdown pre 标签使用的问题。

在讨论Node.js安全时,有几点需要注意:

1. 避免使用 eval 及其类似函数

// 不推荐
eval('console.log("Hello")');

// 推荐
const func = new Function('console.log("Hello")');
func();

2. 使用严格模式

'use strict';

严格模式有助于减少一些意外的错误。

3. 避免 with 关键字

var obj = { x: 17 };
with (obj) { // 语法错误
  console.log(x); 
}

4. 静态代码分析工具

使用如 JSLint、JSHint 或 ESLint 进行代码检查。

npm install -g eslint
eslint yourfile.js

5. 避免以 root 权限运行应用

# 不推荐
sudo node app.js

# 推荐
sudo iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3000

6. 防止命令注入

// 不推荐
child_process.exec(`ls ${userInput}`, (err, data) => {
  console.log(data);
});

// 推荐
child_process.execFile('ls', [userInput], (err, data) => {
  console.log(data);
});

7. 使用流处理文件上传

const fs = require('fs');
const fileStream = fs.createReadStream('path/to/file');
fileStream.pipe(fs.createWriteStream('destination/path'));

8. 加固 Web 应用

  • 跨站脚本攻击 (XSS):始终对用户输入进行转义。

    const escapedInput = he.encode(userInput);
    
  • 跨站请求伪造 (CSRF):使用 CSRF token。

    app.use(express.csrf());
    app.use((req, res, next) => {
      res.locals.csrfToken = req.csrfToken();
      next();
    });
    

以上建议可以帮助你在开发 Node.js 应用时提高安全性。

回到顶部