将用户名和密码从移动/网络应用程序的每个请求发送到后端 api 是否一个坏主意

Posted

技术标签:

【中文标题】将用户名和密码从移动/网络应用程序的每个请求发送到后端 api 是否一个坏主意【英文标题】:Is it a bad idea to send username and password with every request from a mobile/web app to the backend api将用户名和密码从移动/网络应用程序的每个请求发送到后端 api 是不是一个坏主意 【发布时间】:2018-06-26 14:30:52 【问题描述】:

我觉得将用户名和密码从移动/网络应用程序的每个 https 请求发送到后端 api 是不是一个坏主意,但我无法提出太多好的论据来解释原因。

在服务器中,我们将密码的哈希值存储在用户记录中。在任何情况下,对于每个请求,我们都需要从数据库中加载此用户记录,例如检查用户是否被列入黑名单。由于我们需要在每个请求上加载用户记录,因此比较密码不需要任何成本,因此无需管理另一个 sessionID 或令牌数据库。

由于密码是在 https 连接中发送的,所以中间人无法捕捉到密码。而且即使中间有人可以做到,那么当用户需要第一次登录时他也可以做​​到(因为无论如何他都需要在开始时发送他的密码)

那么有什么好的方法呢?

【问题讨论】:

我猜您必须将密码和用户名保存在应用中的某个位置以便在后续请求中重复使用? @ThankgodOssaija 是的,无论如何我会把它保存在我的应用程序的某个地方,我真的很讨厌一些应用程序在 2 或 3 个月后因为我不知道什么再次要求你提供我的凭据(你忘记了因为你从来没有真正使用过它,除非是第一次) 与其将用户名和密码保存在您的应用程序上,不如在每次登录时生成并保存一个唯一标识符,并将其作为登录响应负载的一部分发回。您可以使用此唯一标识符在后续请求中从数据库中获取用户。你可以使用一些 UUID 包。或者最好还是考虑 JSON 网络令牌。 【参考方案1】:

回答您的问题,为什么不应在每个请求和响应中都包含凭据。这将确保恶意应用无法拦截数据并破坏用户凭据。

一旦您在每个请求中包含凭据,这意味着您显然将它们存储在应用程序中的某个位置,如果用户丢失他或她的设备,这些凭据可能会被逆向工程和破坏。

【讨论】:

【参考方案2】:

这将是一个很好的安全面试问题,因为它很好地衡量了对安全领域的理解程度。

不建议在每个请求中发送用户名/密码,但其他答案甚至没有触及真正的原因。

从技术上讲,用户名/密码与 API 密钥之类的东西几乎相同,它们在有效期间是等效的。从某种意义上说,API 密钥甚至更糟,因为根据实现,您通常会将 API 密钥明文存储在数据库中,而不是正确散列的密码(旁注,但请在散列时使用 bcrypt 或 pbkdf2 之类的东西)。你是完全正确的,可以在每个请求上以与 API 密钥相同的方式发送密码,您可以以相同的方式撤销它,等等。API 密钥 密码。在这方面绝对没有区别。

但是,在大多数情况下,您可能仍然不应该这样做,至少有以下原因。

密码很弱如果由用户选择。如果您有很多用户,许多用户将使用 123456 等密码。很容易猜到其中的一些。您不需要在 API 应用程序中运行此风险,但是,在每个请求中发送它或不发送它几乎不会修改此风险,但由于下一点,它会产生一点点。

有时 SSL/TLS 存在弱点。 确实会不时披露针对 TLS 的新攻击,然后最终对其进行修补。但在一段时间内,攻击者可能会从加密流中推断出比特,如果部分已知加密内容(即常见密码),这可能会更容易。机会不是很高,但问题是,对于许多 TLS 密码套件,攻击者现在可以记录流量,并在以后解密(因为它需要很长时间,或者该方法尚未发明)。如果他只解密一个已经过时 3 年的 API 密钥,那很好,但如果是用户密码……那就不太好。

服务器漏洞也可能是一个问题。前段时间,openssl 实现中存在一个漏洞,您实际上可以读取服务器内存的一部分(网络服务器进程)。如果网络服务器一直收到用户密码,它可能会在内存中多次。如果它只是一个临时 API 密钥(或会话 ID 或其他),那么对攻击者来说价值较小。还是不错的,但至少不是密码。

内部攻击者也是需要考虑的因素。如果您的服务器上已经有攻击者,他可能无法访问数据库以直接读取/修改凭据,但他可能能够在有限的时间内观察 Web 服务器进程。和上面一样,如果他只能观察临时id而不是实际密码,那就更好了。

如果您想在每个请求中发送密码,您需要将密码存储在客户端。在客户端存储敏感内容通常不是一个好主意,这为攻击者打开了可能性。如果用户输入凭据,接收临时令牌并且客户端忘记实际密码,直到令牌过期并且用户必须再次提供他的密码,通常会更好。从用户体验的角度来看,这当然可能接受也可能不接受——但安全始终是一种平衡。 :)

如您所见,在每个请求中发送用户名/密码并不是直接漏洞。事实上,在很多情况下,它甚至可能是可以接受的。但是您可以通过使用临时凭证使其更安全,如果成本合理,您为什么不这样做?

【讨论】:

感谢 gabor,现在至少我有一个严肃的论点:关键字永远不会改变,并且在会话 id 暂时有效时永远有效。但无论如何这不是一个杀手级的论点,因为如果我们让会话 id 的有效性假设为 2 小时(我理解的规范),攻击者只需要等待我们再次发送密码以获得新的会话 id :( 【参考方案3】:

是的,是的

这通常是一个糟糕的主意。 WEP 是一种旧的网络安全协议,由于其内在的弱点而最终被取代,因为它无法从数据包(信标)中获取密码,甚至无需通过网络进行身份验证。这就是我们不在传输过程中存储此类凭据的原因。

我应该怎么做?

我建议使用 API 密钥。这是一个私钥,您可以随程序可以在数据库中查找并与特定用户相关联的每个请求一起发送该私钥。例如,如果用户(我们称他为 Bob)注册并请求 API 密钥,您将给他一个密钥(例如 asdf12345),相当于一张票。他可以使用该票来执行某些操作,只要他每次做某事时出示他的票即可。与您在酒店的酒吧/餐厅订购饮料和食物并只需出示您的房卡类似,您每次订购饮料时都需要出示它。每次 Bob 向您的服务器执行请求时,他都需要出示票号 asdf12345,这将让您看到 Bob 被允许访问这些方法。 Mary 没有票,所以当她请求一些数据时,她会被拒绝,因为她没有令牌。

我该怎么做?

每次用户在您的网站上注册一个帐户并请求一个令牌(并且可能同意您拥有的某些 T&C)时,您都会生成一个 GUID/随机哈希并将其存储在应包含两者的 dbo.ApiKeys 表中API 密钥和用户 ID。用户的应用程序应添加此密钥,以便在执行 API 请求时,它们可以包含此令牌。这可以防止您在每个请求中放弃用户的凭据。这也意味着,如果用户设法通过任何方式破坏他们的 API 密钥,这通常是他们的错(他们在星巴克的连接不安全,他们将其保存在桌面上的 .txt 文件中,他们将其写在他们桌子上的便签等)

编辑:OWASP 标准

如果您有兴趣进一步阅读并想了解这方面的行业标准,OWASP 有a page dedicated to this 特定任务。它们是计算机安全最佳实践的通用 wiki。

【讨论】:

感谢 horkrine,但是您的方式要复杂得多,并且没有说明为什么在每个请求中发送密码的方式更糟(除非我们说 https 很弱)。因为理论上没有人可以访问通过 https 发送的密码,即使我们在每个请求中都发送它 虽然这是真的,但您随后依靠 HTTPS 来保护您的用户。并非所有用户都知道 HTTPS 是什么,更不用说他们当前是否通过它进行连接。通过允许您的应用程序改用私钥,这意味着您可以轻松管理这些密钥。如果客户端违反使用协议,您也可以撤销 API 密钥,如果请求来得太快,则返回 Http 响应 429 “Too Many Requests” HTTP 响应代码。说真的,阅读这篇文章 - 里面有很多有用的信息可能会对你有所帮助 因为它是一个移动应用程序,所有的连接都是在后台进行的,用户对此一无所知。通过在每个请求上发送登录名/密码,如果 DOS 攻击,我也可以像使用 api 密钥一样阻止客户端(只需将用户记录置于非活动状态)...

以上是关于将用户名和密码从移动/网络应用程序的每个请求发送到后端 api 是否一个坏主意的主要内容,如果未能解决你的问题,请参考以下文章

密码加密和通过网络发送的最佳方法[重复]

如何使用用户名和密码登录?

如何将 JWT 令牌从 PHP 发送到 Angular?

Servlet

使用 oAuth2 验证 WPF 和 PHP 之间的请求?

需要搞清楚的关于抓包的问题