网络安全。我是不是正确保护了我的 REST api? (node.js 快递)

Posted

技术标签:

【中文标题】网络安全。我是不是正确保护了我的 REST api? (node.js 快递)【英文标题】:Web security. Am I protecting my REST api properly? (node.js express)网络安全。我是否正确保护了我的 REST api? (node.js 快递) 【发布时间】:2018-06-25 03:58:55 【问题描述】:

我有一个带有 REST 之类的 API 的 MEAN 堆栈应用程序。我有两种用户类型:用户和管理员。为了让用户登录并保持会话,我像这样使用 jsonwebtoken jwt(简化):

const jwt = require("jsonwebtoken");

//example user, normally compare pass, find user in db and return user
let user =  username: user.username, userType: user.userType ;

const token = jwt.sign( data: user , secret, 
    expiresIn: 604800 // 1 week
);

为了保护我的特快路线,我这样做:

在这个例子中,它是一个“获取用户”路由,管理员可以获取任何给定用户的信息。 “普通”用户只被允许获取关于他/她自己的信息,为什么我将请求的用户名与从令牌解码的用户名进行比较。

let decodeToken = function (token) 
    let decoded;
    try 
        decoded = jwt.verify(token, secret);
     catch (e) 
        console.log(e);
    
    return decoded;


// Get one user - admin full access, user self-access
router.get('/getUser/:username', (req, res, next) => 
    let username = req.params.username;
    if (req.headers.authorization) 
        let token = req.headers.authorization.replace(/^Bearer\s/, '');
        decoded = decodeToken(token);

        if (decoded.data.userType == 'admin') 
            //do something admin only
         else if (decoded.data.username == username) 
            //do something user (self) only
         else
            res.json( success: false, msg: 'not authorized' );
        
     else 
        res.json( success: false, msg: 'You are not logged in.' );
    
)

所以我的问题是,这有多安全?有人可以操纵会话令牌将用户名交换为其他人的用户名吗?甚至将用户类型从用户更改为管理员?

我的猜测是。只有当他们知道“秘密”,但这是否足够安全?秘密毕竟就像存储在代码中的纯文本密码一样。最佳做法是什么?

【问题讨论】:

你跑到let user = username: user.username, userType: user.userType ;了吗? IIUC,这定义了一个变量user,它最初是undefined。然后是评估... 的时候了,这涉及访问user.username。在 undefined 上查找 username 属性失败并出现 TypeError 我不明白我是怎么遇到这个问题的,或者我不完全理解你的意思。正如我在代码中的注释所解释的,用户对象只是一个示例。通常会从数据库返回一个用户,如果不是,则会处理错误。 请忽略。您的示例代码令人困惑,但我想我理解您的意思。如果jwt.verify 失败并在decodeToken 中出现异常,则if (decoded.data.userType == 'admin') 中确实存在问题,因为decoded === undefined。也许考虑将decoded 初始化为结构上有效的值,这意味着catch 块中没有权限。此外,decoded 可能应该在您的处理程序中使用 let 声明。 啊,是的。我想我明白你的意思。通常当 jwt.verify 失败时,它会生成一个响应,说“缓冲区错误,机密必须是字符串类型”......类似的东西。但是,然后它失败了,并且没有达到代码的“脆弱”部分,这是主要问题。不过,感谢您指出,我会尝试引发一些 jwt 错误并寻找解决方案。 【参考方案1】:

这是非常安全的,特别是如果通过 HTTPS 发送 - 那么攻击者将不知道您的请求负载是什么样的。

唯一真正的危险是确保您保持秘密的安全,不要保存在公共 git 存储库中,锁定对任何存储秘密的盒子的访问。使用编码的秘密。

还有其他方法可以强化您的服务器。考虑使用 npm 流行的 helmet 模块。

【讨论】:

谢谢,这正是我需要知道的。我是 ssl 加密的,所以我想这个应用程序对于我的目的来说已经足够好了。我以前用过头盔,但这次我只用了cors。如果以后遇到威胁,也许我应该考虑添加头盔中间件。【参考方案2】:

如果您的数据包通过 HTTPS 发送,这是安全的。如果您想添加另一层安全性,您可以首先在iron 的帮助下加密用户详细信息,iron 使用“aes-256-cbc”进行加密,然后使用该加密文本并通过生成令牌JWT。这样,如果用户以某种方式访问​​您的令牌并访问JWT 的网站。他将无法识别此令牌中的内容。

再次强调,这只是增加了一层额外的安全性,从技术上讲,人们无法提取信息,因为这非常耗时,但为我们的应用程序增加了一些额外的安全性。

还要确保您的所有秘密(密钥)都是私密的。

【讨论】:

非常感谢您的建议!高度赞赏:) 随时:) 如果您需要任何帮助,请告诉我。【参考方案3】:

回答您最初的问题“有人可以操纵会话令牌以将用户名交换为其他人的用户名吗?甚至将 userType 从用户更改为管理员?”

JWT 令牌从服务器端加密并以响应的形式发送到客户端。使用该令牌,需要牢记两点:

    令牌使用只有服务器知道的私钥加密 令牌包含所谓的signature,用于验证 JWT 有效负载的内容(例如 userType 等)

关于第一点的更多信息,请参考以下answer。

为了更好地展示 JWT 令牌和签名,请查看以下 URL - https://jwt.io/


考虑到这一点,您必须确保:

您的密钥在任何时候都不会暴露给外部服务/客户端。理想情况下,该密钥甚至没有硬编码在您的代码库中(请问您是否希望我详细说明)。

您的端点没有任何逻辑缺陷。

从设计的角度来看,我更愿意将与管理空间相关的任何端点隔离到它们自己的端点中,但是快速浏览一下,您的代码似乎很好。 :-)


附带说明一下,如果您在 Web 应用程序安全方面没有太多经验,我建议您查看一些自动扫描程序,例如 Acunetix、Burp(混合)等。虽然无论如何都不完美,但它们非常有能力检测大量的行为漏洞(例如,恶意行为者通常会利用的漏洞)。

【讨论】:

请注意,jwt 令牌加密,仅签名。内容对用户可见,只有其完整性(真实性)受到保护。【参考方案4】:

秘密毕竟就像存储在代码中的纯文本密码。

没错。如果secret 没有保密,那么攻击者可以伪造用户对象并使用该秘密对其进行签名,而verify 将无法区分。

将秘密放入代码中可能会导致秘密泄露。它可能通过您的代码存储库泄漏,因为配置错误的服务器将 JS 源文件作为静态文件提供,或者因为攻击者找到了一种方法来利用 child_process 调用来回显服务器上的源文件。您的用户的安全不应依赖于没有人犯此类常见错误。

什么是最佳实践?

使用密钥管理系统 (KMS),而不是自行滚动或在代码中嵌入秘密。 Wikipedia 说

密钥管理系统 (KMS),也称为加密密钥管理系统 (CKMS),是一种用于为设备和应用程序生成、分发和管理加密密钥的集成方法。

通常,您如何执行此操作取决于您的主机。比如Google Cloud和AWS都提供密钥管理服务。

https://www.npmjs.com/browse/keyword/kms 可能会帮助您找到适合您的堆栈的东西。

【讨论】:

这确实是密钥管理问题的答案。然而,对于较小的项目,这通常是不可行的,这是非常不幸的,因为将秘密存储在 Web 服务器上是一个真正的风险。 @GaborLengyel,当您说“较小的项目”时,您指的是那些不在 gCloud 或 AWS 等容器中运行的项目吗? Kubernetes 可能是这些人的选择吗? 不幸的是,我对 Kubernetes 不够熟悉,但可能是的,这听起来是一个合理的想法!谢谢:)【参考方案5】:

以上代码中的一些潜在漏洞:

变量 username 来自 url,但 usertype 是从路由器操作中的令牌断言的。例如,如果审计日志从 username 变量中获取用户 ID,则它可能会损坏,因为这不是真实的并且可以是用户发送的任何内容。

重播是个问题。由于令牌有效期为一周,因此在令牌过期之前无法撤销管理员权限。 (恶意用户可以重放之前的管理员令牌。)

同样,在令牌过期之前,您不能终止(强制注销)任何会话。这是此类无状态设计中的常见问题。

其他人指出的令牌解码后的空引用。在 node.js 中,这更像是一个弱点而不是一个漏洞,我认为它是不可利用的。

服务器上的秘密非常有价值,可以用来冒充任何用户。在安全性要求高的系统中很难保护这样的秘密。

因此,与另一个答案相反,这远非“非常安全”,但对于许多目的来说是相当安全的。

【讨论】:

第一点。我不这么认为,或者我错过了一些东西。是的,“任何人”可以在 url 参数中发送他/她想要的任何用户名,但请注意,它与从令牌解码的用户名进行比较。即用户将永远无法访问隐藏在令牌中的任何其他用户。忘记用户ID,它们不用于任何比较。 ;) 我知道会话到期,这实际上只是出于开发原因,将在发布时缩短为 1 天。您的最后一点,是的,这是我发布此 q 时的担忧之一。我希望多保护一​​点。谢谢你的回复

以上是关于网络安全。我是不是正确保护了我的 REST api? (node.js 快递)的主要内容,如果未能解决你的问题,请参考以下文章

使用 OAuth2.0 或 Azure Active Directory 保护 REST API

如何通过查询身份验证保护 REST API

什么是 OAuth,它如何保护 REST API 调用? [关闭]

需要使用Keycloack和oAuth2保护普通Spring REST API的示例

使用 Spring Security 保护 REST 端点

需要使用 Keycloak 和 oAuth2 保护普通 Spring REST API 的示例