我设计了一个“安全”的登录系统。让我们专注于暴力预防。在我的 Android 客户端中,我有一个计数器,用于计算用户尝试使用错误凭据登录的次数。当它达到 3 次尝试失败时,我禁用登录按钮和密码字段,强制用户重新启动应用程序,从而阻止暴力攻击。这是正确的做法吗?
或者我应该在服务器中放置一个计数器?但是在服务器中它可能导致 DoS,如果我同时收到很多暴力尝试,对吗?
这个想法是停止无限数量的输入,所以我认为这可以在应用程序端完成,在我的例子中是在 Android 客户端。
我愿意接受建议。
我设计了一个“安全”的登录系统。让我们专注于暴力预防。在我的 Android 客户端中,我有一个计数器,用于计算用户尝试使用错误凭据登录的次数。当它达到 3 次尝试失败时,我禁用登录按钮和密码字段,强制用户重新启动应用程序,从而阻止暴力攻击。这是正确的做法吗?
或者我应该在服务器中放置一个计数器?但是在服务器中它可能导致 DoS,如果我同时收到很多暴力尝试,对吗?
这个想法是停止无限数量的输入,所以我认为这可以在应用程序端完成,在我的例子中是在 Android 客户端。
我愿意接受建议。
客户端措施只是部分(并且主要是装饰性的)解决方案,这只能限制非严肃的尝试。任何严重的尝试都会因为检测到登录 URL/API 而直接攻击您的服务器,或者将通过拦截代理运行您的客户端以捕获创建蛮力运行所需的详细信息。
强大的防御通常要求您假设攻击者知道系统的所有信息,除了密钥(Kerckhoff 原则),因此您应该从该位置开始。
缓解措施包括:
来自CAPEC-112蛮力:
这种攻击的关键因素是攻击者快速探索可能的秘密空间的能力。虽然防御者无法控制攻击者可用的资源,但他们可以控制秘密空间的大小。防御者必须依靠确保这样做所需的时间和资源将超过信息的价值。
另请参阅CAPEC-49密码暴力破解
防止暴力攻击的惯用方法是在服务器上增加一点延迟。
正如用户 immibis在评论中指出的那样,攻击者不需要使用您的应用程序来执行 DoS 或 DDoS。攻击者所需要的只是端点,然后他们可以淹没它。他们可以通过对 .apk 文件进行逆向工程或仅通过监控传出流量来找到端点。
所以防御必须在服务器端。通过在每次失败的登录尝试后添加一点延迟(例如 1 秒),攻击者尝试数百万个可能的用户名/密码组合变得不切实际。
如果您希望保护您的服务器免受一般 DDoS 攻击,您需要一个强大的服务器场,或者使用像 CloudFlare 这样的服务。
一个重要方面是设备上是否存储有任何类型的密钥材料可用于验证设备,或者用户是否可以简单地使用全新的设备,下载应用程序并登录。
如果设备上有这样的密钥材料,则服务器可以对每个设备应用速率限制。服务器端应用速率限制的其他方法(例如每个 IP 或每个用户名的速率限制)是潜在的 DoS 攻击向量。无论速率限制是通过让服务器执行 CPU 繁重的计算作为密码验证的一部分还是通过临时锁定来实现的,都是如此。
当您同时控制客户端和服务器代码时,您可以设计一个协议,其中 CPU 的主要验证部分在客户端完成,但服务器仍然执行加盐哈希以确保您仍然拥有服务器端验证的好处。显然,如果您从头开始实施这样的事情,则存在由设计或实施缺陷引入的漏洞风险。
做 CPU 密集型部分客户端的好处是它主要消除了 DoS 攻击向量。做 CPU 密集部分客户端的一个缺点是客户端可能在 CPU 资源方面受到限制。
结合上述方法是可能的。在第一次登录时,您可以让客户端进行几秒钟的散列,例如通过向客户端发送盐并让它进行多轮散列,最后服务器使用不同的盐进行最后一轮散列。如果密码被接受,设备将发送一个令牌,该令牌将允许客户端在未来使用更便宜的二级哈希对同一帐户进行身份验证。在令牌过期并且客户端返回到较慢的哈希之前,服务器可以强制限制允许使用令牌的失败登录尝试次数。
我必须再次警告说,有很多方法可以在这样的设计中引入安全漏洞,因此您必须采取这种方式来应对暴力攻击的风险。
可以将设备缓送和工作证明结合起来:
如果每个设备都必须使用自签名密钥(约 4096 位)作为设备标识进行连接,并且用于速率限制:
任何一个
或者只是让它成为别人的问题 - 使用公共的 2 因素身份验证系统。