确保快速申请安全
概述
本文的重点是了解如何保护 Node.js 和 Express 应用程序的安全。
介绍
在数字世界中,安全至关重要。如果你的应用程序安全措施不到位,那么被黑客攻击就不是会不会发生的问题,而是何时发生的问题。以下是一些保护应用程序免受攻击的有效方法。
安全最佳实践
首先,我们来了解一下Express的一些最佳实践。Express 建议禁用X-Powered-By标头,因为它会向攻击者泄露网站的工作原理。一个简单的解决方法是安装Helmet软件包。Helmet 为应用程序添加了一些开箱即用的安全更改,使其更不容易受到攻击。
const express = require("express");
const helmet = require("helmet");
const app = express();
app.use(helmet());
另一个建议是避免使用默认的`Set-Cookie`,而改用`cookie-session`。原因是 ` Set-Cookie`会存储整个会话对象,而`cookie -session`只会存储会话 ID。例如,我们可以在 Node.js 中使用 ` cookie-session`设置 cookie ,如下所示:
const express = require('express')
const cookieSession = require('cookie-session')
const app = express()
const expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(cookieSession({
name: 'trusted cookie', // Don't use Set-Cookie
path: '/',
expires: expiryDate,
keys: ['some random key']
}))
...
为了给 cookie 增加一层额外的安全保障,我们可以更改它的sameSite属性。默认情况下,sameSite 设置为lax ,如果我们将其更改为strict,则 cookie 的使用将仅限于颁发该 cookie 的域。
const express = require('express')
const cookieSession = require('cookie-session')
const app = express()
const expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(cookieSession({
name: 'trusted cookie', // Don't use Set-Cookie
path: '/',
expires: expiryDate,
keys: ['some random key'],
sameSite: 'strict'
}))
...
接下来,我们需要确保依赖项不存在安全问题。我们可以运行`npm audit` 命令,或者使用Snyk来检查依赖项中的安全问题。例如,使用 Snyk 测试依赖项将产生以下输出:
Testing /Users/meddy/projects/demo...
Organization: creativethoughtz.team
Package manager: npm
Target file: package-lock.json
Project name: demo
Open source: no
Project path: /Users/meddy/projects/demo
Licenses: enabled
✓ Tested 56 dependencies for known issues, no vulnerable paths found.
Synk 是一个选择,但我们也可以直接使用 npm。使用 npm,我们可以运行npm audit fix来扫描项目中的漏洞,并自动安装所有兼容的更新来修复存在漏洞的依赖项。要查看 Express 团队的完整建议列表,请访问安全最佳实践页面。目前,该应用程序的防御措施非常有限。接下来,我们来看看如何提升应用程序的安全性。
跨站请求伪造 (CSRF)
跨站请求伪造 (CSRF)是 Web 应用程序上最常见的攻击之一。当 Web 服务器向用户提供某种类型的访问密钥(例如 cookie 或令牌)时,攻击就会发生,从而使用户能够绕过重新身份验证。一旦用户访问另一个已设置 CSRF 攻击的网站,恶意网站就能以用户的身份向服务器发出请求。为了防止 CSRF 攻击,请使用csurf软件包。csurf 软件包确保所有发送到服务器的请求都来自您的网站。csurf 软件包允许您在网站表单中存储加密令牌。当向服务器发出请求时,有效负载必须包含存储在表单中的令牌。
示例服务器
const express = require('express')
const cookieSession = require('cookie-session');
const csrf = require('csurf')
const expressHandlebars = require('express-handlebars');
const bodyParser = require('body-parser')
// setup csrf Protection middleware
const csrfProtection = csrf();
const parseForm = bodyParser.urlencoded({ extended: false })
const app = express()
app.engine('handlebars', expressHandlebars({ defaultLayout: 'main' }));
app.set('view engine', 'handlebars')
const expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(cookieSession({
name: 'session',
path: '/',
expires: expiryDate,
sameSite: 'strict',
keys: ['some random key']
}))
app.get('/form', csrfProtection, function (req, res) {
// pass the csrfToken to the view
res.render('send', { csrfToken: req.csrfToken() })
})
// when a post is made verify the token
app.post('/process', parseForm, csrfProtection, function (req, res) {
res.send('data is being processed')
})
带有 _csrf 令牌的基本形式
<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>
另一种提供 CSRF 防护的方法是检查源地址和引用地址 标头,它们被称为禁止标头。禁止标头无法通过编程方式修改,因为用户代理对其拥有完全控制权。这些标头包含请求发起的主机信息,我们可以利用这些信息将其与我们应用程序的主机进行比较。 这将有助于为抵御 CSRF 攻击提供额外的安全保障。接下来,我们将继续探索应用程序的其他安全选项。
跨站脚本攻击(XSS)
跨站脚本攻击(XSS)是指攻击者能够将恶意代码注入到您的应用程序中。好消息是,如果您使用的是 Angular、React 或 Pug 等前端框架,框架本身会对数据进行清理,从而保护您免受 XSS 攻击。然而,确保数据库中的数据也经过清理的唯一方法是在服务器端进行数据清理。我们可以使用sanitize-html包来进行数据清理。
const sanitizeHtml = require('sanitize-html');
const dirty = 'some really tacky <script>alert("Hi")</script>';
const clean = sanitizeHtml(dirty);
如果您未使用框架渲染前端,或者希望将任何 HTML 标签存储在数据库中,则可以采用这种方法。要了解有关不同类型 XSS 攻击以及如何预防它们的更多信息,请查看OWASP 速查表。
速率限制
速率限制是另一种我们可以部署的防御机制,用于保护我们的资源免受攻击。速率限制会限制可以发送到服务器的请求数量。当达到最大请求数量时,服务器将限制来自该来源的请求。
授权
授权代表用户在我们系统中的权限。这些权限与特定资源相关,并以CRUD缩写词表示,分别代表创建 (Create)、读取 (Read)、更新 (Update) 和删除 (Delete)。确定用户权限时,应遵循最小权限原则。这意味着您应该仅根据系统用户的实际需要授予权限。
验证
密码认证协议 (PAP)是最薄弱的认证方案之一,但却是最常用的。密码很容易被破解,更糟糕的是,它们还会被串联起来。问题在于,普通用户平均拥有超过 90 个在线账户。因此,如果我们的应用程序需要密码进行认证,那么它就应该强制执行强密码要求。这将有助于确保我们的认证方案不会成为整个认证链中最薄弱的环节。此外,我们还应该考虑密码的加密算法,以防止密码被破解。在选择哈希算法时,我们应该避免使用SHA2哈希算法,而应该使用更安全的argon2哈希算法。
哈希代码片段
const argon2 = require('argon2');
try {
const hash = await argon2.hash("password");
} catch (err) {
//...
}
验证密码代码片段
try {
if (await argon2.verify("<big long hash>", "password")) {
// password match
} else {
// password did not match
}
} catch (err) {
// internal failure
}
如果可能,我们应该避免自行构建身份验证系统,而是考虑利用现有的身份验证系统。Passport 就是一种常用的身份验证系统。它提供了多种身份验证选项。我们可以将整个身份验证过程委托给OAuth或SAML标准,或者如果我们想自行管理身份验证,也可以使用本地策略。
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
另一个日益流行的概念是无密码认证。无密码认证允许用户无需记住密码即可登录。用户只需输入手机号码或电子邮件地址,即可收到一次性验证码或链接,然后使用该验证码或链接登录。现在我们有了几种在网络上验证用户身份的方法,让我们继续提升应用程序的安全性。
HTTPS(数据传输)
HTTPS可能是保护数据完整性的最简单的安全机制之一。HTTPS会在数据传输过程中对其进行加密,使得黑客极难获取客户端和服务器之间交换的信息。
AES-256(静态数据)
保护应用程序和资源的另一种安全措施是加密存储在数据库或处于静态状态的数据。可以使用诸如AES-256之类的强加密算法来加密静态数据。一种常用的静态数据加密方法是AWS KMS 信封加密策略,它使用 AES-256 算法。该方案使用主密钥加密数据密钥,然后使用该数据密钥对静态数据进行加密。解密数据时,必须使用与加密静态数据时相同的数据密钥。
制定计划
制定安全计划是保障您的安全举措和应用程序生存的最终决定因素。安全计划会详细说明应对措施、通知对象、攻击类型以及响应方式。安全计划通常由安全团队制定,但这超出了本文的讨论范围。不过,AWS 安全白皮书概述了一些业内最佳安全实践,其中许多实践也被 AWS 应用于他们自己的软件项目中。
结论
一如既往,祝您一切顺利,感谢您阅读本文。如果您觉得本文对您有所帮助,请留下评分或评论;如果您有任何疑问,欢迎随时提问。
文章来源:https://dev.to/meddy672/securing-an-express-application-43m1
