如何安全地存储 Discord(OAuth2) 用户的访问令牌?

Posted

技术标签:

【中文标题】如何安全地存储 Discord(OAuth2) 用户的访问令牌?【英文标题】:How to securely store the Access-Token of a Discord(OAuth2) User? 【发布时间】:2019-06-13 23:38:26 【问题描述】:

我正在努力寻找一种安全保存访问令牌的方法,我的网络应用程序在用户授权应用程序后从 DiscordAPI 检索到该访问令牌。

我正在为 Discord Bot 创建一个 Web 界面。这里很重要,不是每个人都可以使用它。只有特定 Discord 服务器上的服务器版主等才能访问网站的大部分内容。为此,我使用 Discord 中的 OAuth2 东西来检索访问令牌,我可以使用它获取用户信息,例如他们的唯一 ID。 然后使用 ID 来检查他们在 Discord 服务器上的角色类型。

现在获取 Access-Token 的部分已经编写好了脚本,并且似乎可以正常工作。我可以使用令牌从 Discord API 等中查询数据。

我主要关心的是这里的安全性。我已经阅读了几篇不同的帖子,但似乎每个人对此都有不同的看法。

我经常读到(甚至在 Auth2 网站上)的一个“解决方案”是将令牌保存在 cookie 中。

我不确定这是否安全。我不能简单地将令牌仅存储在服务器上,因为用户必须在令牌生命周期内保持登录状态。但是将其存储为 cookie 会使其容易受到攻击(我对此还不够熟悉,无法防御)。

我目前收到token时有这行:

res.cookie('authToken', response.access_token);

我还将该行调整为以下内容,因为这是为了删除通过脚本读取 cookie 的所有方式(够了吗?):

res.cookie('authToken', response.access_token, httpOnly: true );

当访问 Web 界面的其他部分时,我会检查 cookie 是否存在并尝试向 Discord 询问用户信息。如果用户信息正确返回,我假设用户已正确验证:

router.get('/', catchAsync(async (req, res, next) =>   
    if(req.cookies.authToken === undefined) 

       // Render index view
       res.render('index',  
           authenticated: false
       );
   
   else 
        // Grab the token
        const localToken = req.cookies.authToken;
        // Use the token to query user information
        const response = await discordapi.GetDiscordUserDetails(localToken);
        if(response.id) 
            // Open the Index page and pass the username over
            res.render('index', 
                authenticated: true,
                username: response.username,
                userid: response.id,
                avatarid: response.avatar
            );
         else 
            res.render('index',  
                authenticated: false
            );
        
   
));

(我传递的“经过身份验证的”布尔值仅更改 html(快速车把)文档中登录按钮的可见性。您不能用它做任何其他事情。)

现在,以我对此的有限了解,我几乎认为这是最糟糕的做法,我想改进(这个)。

另一种解决方案是将访问令牌存储在 Web 服务器本身的数据库中。所以基本上从不以任何方式向用户展示它。 但是,我需要一种将这个令牌与用户匹配的方法,所以他们仍然需要某种带有我可以用来获取令牌的信息的 cookie。 我认为如果令牌在数据库中被加密并且用户有一个密钥来解密它,那将是最佳的。 但是,我不确定这是否再次安全,因为如果您读出 cookie,您仍然可以访问令牌,或者至少表现得像您是那个特定用户。

那么保持 cookie 100% 安全是唯一的方法吗?我对此很陌生,尽管这很可能只是一个简单的不和谐服务器的小型 Web 界面,但我仍然希望正确地做到这一点。

我还读到将令牌作为某种密码处理并将其包装到另一层“会话”中是一种方法,但这听起来很复杂,我实际上不知道从哪里开始。

我希望有人能对此有所了解,因为安全是我真正害怕的事情。

感谢您的宝贵时间!

【问题讨论】:

进一步考虑,我通常也不反对只使用我通过护照等管理的帐户进行正常的注册/登录过程,然后简单地让用户将他们的 Discord 帐户链接到该帐户.然后我可以将令牌存储在数据库中,并将其与我可以管理的用户 ID 链接起来。我认为这将是一种“更简单”和“更好”的方式,但是我仍然对跳过额外的用户名/密码登录层非常感兴趣。 是的,我还在为一个不和谐的机器人(第一次)开发一个 Web 仪表板,想知道是否有更安全的方法。顺便联系我(EukalyptenHD#2183),我很想看看你的面板是怎么做的。此外,关于 oauth2 框架之上的正常注册/登录过程,我觉得它违背了能够按“授权”并直接登录而无需输入用户名或密码的整个目的,但是是的我猜也可以。 【参考方案1】: 无聊的故事:

我想先假设我已经看到网上银行应用程序以纯文本形式发送身份验证令牌,并在 POST 中附加为查询字符串。

话虽如此,开发人员必须做出的大多数安全考虑必须旨在保护后端结构,而不是防止用户在客户端被黑客入侵。 (显然,你会尽一切可能让最后一个假设变得不那么可信,但在道德和技术上都有一个限制。我的意思是,如果我连接到一个不安全的网络并且有人拦截了我的通信并设法对其进行解码,我认为这将是我的错而不是其他人。)

无论如何,我将停止哲学化,但最后一个已知的观察是,永远不会有任何 100% 安全的解决方案。

真正的答案:

使用 httpOnly cookie 是传输和存储身份验证令牌的最简单、最安全的方法,但如果这还不够,还可以实现一些其他安全层。这只是一些想法,可能还有更多!

    在一段时间不活动后减少令牌的生命周期并关闭会话服务器端。您必须记录活动会话,每个会话都有其开始时间和活动令牌等。

    IP 检查。如果会话以来自美国的 IP 地址开始,五分钟后 IP 地址似乎来自菲律宾,那么您可能需要采取一些措施。

    使用 AWS Cognito 等外部身份验证服务。但它不会做任何你自己做不到的事情。

    实施多重身份验证。

    与 IP 检查类似,您可以使用用户代理字符串作为种子计算哈希,并将其存储。当您对客户身份有疑问时,请检查它。

    谈到哈希,您可以存储令牌并将哈希发送给用户。唯一的改进是它可以防止有人直接调用 Discord API(如果 API 没有过滤器)。例如,密码始终存储为哈希值。

    前一个的任意组合。

这个列表可以无限长下去,在我看来,在某个时间点之后,你只会浪费不必要的时间和资源。只要记住银行在 URL 中发送令牌,您就会感觉好多了。

【讨论】:

非常感谢。我已经假设所有这些模糊的其他答案背后的原因是根本没有“完全安全”的方式。但是,我确实期望有一种“首选”方式来处理这些事情。令人惊讶的是,例如银行不遵循“99.99%”的安全方式。很多其他的东西都是标准化的,但不能保证这些东西的安全。

以上是关于如何安全地存储 Discord(OAuth2) 用户的访问令牌?的主要内容,如果未能解决你的问题,请参考以下文章

如何撤销 Discord OAuth2.0 中的令牌?

Discord OAuth2:'请求中缺少“代码”'

使用 discord Oauth2 时,如何在我的网站上显示用户名和头像? [关闭]

discord Oauth2 访问令牌未定义

Discord 使用 url-query 中的“代码”发送 Oauth2 重定向 url。如何在我的谷歌脚本中获取该代码

用角度理解 Oauth2 流