关于安全性:什么是最好的分布式蛮力对策?

What is the best Distributed Brute Force countermeasure?

首先,有一点背景:我正在为CodeIgniter实现一个auth+auth系统并不是什么秘密,到目前为止,我正在赢得(可以这么说)。但是我遇到了一个非常重要的挑战(大多数认证库完全忽略了这个挑战,但我坚持要正确处理):如何智能地处理大规模、分布式、可变用户名暴力攻击。

我知道所有常见的技巧:

  • 限制每个IP/主机的失败尝试次数,并拒绝罪犯访问(例如fail2ban),因为僵尸网络变得更智能,这不再有效。
  • 将上述内容与已知"坏"IP/主机(如denyhosts)的黑名单结合起来,后者依赖的僵尸网络越来越不喜欢1。
  • IP/主机白名单与传统认证相结合(不幸的是,对于动态IP用户和大多数网站上的高流失率来说,这是无用的)
  • 在n分钟/小时内设置网站范围内失败尝试次数的限制,并限制(挂起)之后的所有登录尝试数分钟/小时(由于DoS攻击问题,您将成为僵尸网络儿童的游戏)
  • 对于没有登录/密码选项的所有用户,强制数字签名(公钥证书)或RSA硬件令牌(毫无疑问,这是一个可靠的解决方案,但仅适用于封闭的专用服务)
  • 强制的超强密码方案(例如超过25个带有符号的无意义字符-同样,对于临时用户来说太不实用)
  • 最后,captchas(在大多数情况下都可以使用,但对用户来说很烦人,对坚决的、足智多谋的攻击者来说几乎毫无用处)
  • 现在,这些只是理论上可行的想法。有很多垃圾的想法会把网站搞得一团糟(例如,对DoS的小攻击)。我想要更好的东西。更好的是,我的意思是:

    • 它必须是安全的(+)以防DoS和暴力攻击,并且不会引入任何新的漏洞,这些漏洞可能会使稍微狡猾的机器人继续在雷达下工作。

    • 它必须是自动化的。如果它需要一个人工操作人员来验证每个登录或监视可疑的活动,那么它将无法在真实的场景中工作。

    • 它必须对主流网络使用是可行的(即,可以由非程序员执行的高流量、高容量和开放注册)

    • 它不能妨碍用户体验,以至于临时用户会感到恼火或沮丧(并可能放弃网站)

    • 它不能牵扯到小猫,除非它们真的很安全

    (+)所谓"安全",我的意思是至少和一个偏执的用户保持密码秘密的能力一样安全。

    所以-让我们听听!你会怎么做?你知道我没有提到的最佳实践吗(哦,请说你知道)?我承认我确实有自己的想法(结合3和4的想法),但我会让真正的专家在让自己难堪之前发表意见;—)


    好吧,拖延够了,这是我到目前为止想出来的好的。

    (不好意思,前面有一个很长的帖子。勇敢点,朋友,这次旅行值得一试)好的。

    将原始帖子中的方法3和4组合成一种"模糊"或动态白名单,然后-这里是诀窍-不要阻止非白名单IP,只需将它们限制到地狱和地狱。好的。

    Note that this measure is only meant to thwart this very specific type of attack. In practice, of course, it would work in combination with other best-practices approaches to auth: fixed-username throttling, per-IP throttling, code-enforced strong password policy, unthrottled cookie login, hashing all password equivalents before saving them, never using security questions, etc.

    Ok.

    关于攻击场景的假设好的。

    如果攻击者以变量用户名为目标,则不会触发用户名限制。如果攻击者正在使用僵尸网络或有权访问较大的IP范围,我们的IP限制将无能为力。如果攻击者预先抓取了我们的用户列表(通常在开放注册Web服务上可能),我们无法根据"找不到用户"错误的数量检测正在进行的攻击。如果我们强制一个限制性的系统范围(所有用户名、所有IP)限制,那么任何这样的攻击都将在攻击期间以及限制期间对我们的整个站点造成影响。好的。

    所以我们需要做点别的。好的。

    对策的第一部分:白名单好的。

    我们可以相当肯定的是,攻击者无法检测并动态欺骗我们数千个用户的IP地址(+)。这使得白名单的可行性。换句话说:对于每个用户,我们存储一个(散列的)IP列表,用户以前(最近)登录的IP列表。好的。

    因此,我们的白名单方案将作为一个锁定的"前门",用户必须从他认可的"良好"IP之一连接才能登录。对这个"前门"的暴力攻击几乎是不可能的。好的。

    (+)除非攻击者"拥有"服务器、所有用户的盒子或连接本身——在这种情况下,我们不再有"身份验证"问题,我们有真正的特许权大小的"拉即插即用"Fubar情况好的。

    对策的第二部分:系统范围内无法识别IP的节流好的。

    为了使开放式注册Web服务的白名单有效,在该服务中,用户经常切换计算机和/或从动态IP地址连接,我们需要为从无法识别的IP连接的用户打开"cat门"。诀窍是设计这扇门,这样僵尸网络就会被卡住,合法的用户就不会受到任何干扰。好的。

    在我的方案中,这是通过设置一个非常限制性的最大数量的未经批准的IP登录尝试失败,例如,在3小时的时间段内(根据服务类型,使用更短或更长的时间段可能更明智),并使该限制成为全局的,即对所有用户帐户。好的。

    即使是一个缓慢(1-2分钟之间的尝试)蛮力将被检测和挫败迅速有效地使用这种方法。当然,一个真正缓慢的蛮力可能仍然不被注意,但太慢的速度击败了蛮力攻击的目的。好的。

    我希望通过这种节流机制实现的是,如果达到最大限值,我们的"猫门"会砰地关上一段时间,但我们的前门仍然对通过常规方式连接的合法用户开放:好的。

    • 或者从他们的一个识别的IP连接
    • 或者使用持久登录cookie(从任何地方)

    唯一在攻击期间受到影响的合法用户(即在激活限制时)将是没有从未知位置登录或使用动态IP登录的持久登录cookie的用户。这些用户将无法登录,直到节流功能失效(如果攻击者在节流功能失效的情况下继续运行僵尸网络,这可能需要一段时间)。好的。

    为了让这一小部分用户能够挤过原本密封的cat门,即使bots仍在不断地攻击它,我将使用一个带有captcha的"备份"登录表单。因此,当您显示"抱歉,但此时无法从该IP地址登录"消息时,包含一个链接,其中显示"安全备份登录-仅限人类(bots:无谎言)"。开个玩笑吧,当他们点击那个链接时,给他们一个recaptcha认证的登录表单,绕过站点范围的限制。这样,如果他们是人类并且知道正确的登录+密码(并且能够读取captchas),那么他们将永远不会被拒绝服务,即使他们是从未知主机连接的,并且不使用自动登录cookie。好的。

    噢,我只是想澄清一下:因为我确实认为captchas是一种邪恶的东西,所以"备份"登录选项只会在限制活动时出现。好的。

    不可否认,像这样的持续攻击仍然会构成DoS攻击的一种形式,但随着所描述的系统就位,它只会影响我所怀疑的用户的一小部分,即那些不使用"记住我"cookie的人,并且在攻击发生时正好登录,并且没有从任何他们通常的身份证和谁不能读Captchas。只有那些能够拒绝所有这些标准的人,特别是机器人和真正不幸的残疾人,才会在机器人攻击中被拒绝。好的。

    EDIT: Actully, I thought of a way to let even CAPTCHA-challenged users pass through during a 'lockdown': instead of, or as a supplement to, the backup CAPTCHA login, provide the user with an option to have a single-use, user-specific lockdown code sent to his email, that he can then use to bypass the throttling. This definitely crosses over my 'annoyance' threshold, but since it's only used as a last resort for a tiny subset of users, and since it still beats being locked out of your account, it would be acceptable.

    Ok.

    (另外,请注意,如果攻击比我在这里描述的讨厌的分布式版本复杂的多,就不会发生这种情况。如果攻击只来自几个IP或者只来自几个用户名,那么它将在更早的时候被阻止,并且不会在整个站点造成任何后果)好的。

    所以,这就是我将在我的auth库中实现的对策,一旦我确信它是合理的,并且没有一个我错过的更简单的解决方案。事实是,在安全性方面有很多微妙的方法可以做错事,我不超过做出错误的假设或毫无希望的逻辑错误。所以,请,任何和所有的反馈,批评和改进,微妙之处等都非常感谢。好的。好啊。


    几个简单的步骤:

    黑名单某些常见的用户名,并将其用作蜜罐。管理员、客人等…不要让任何人用这些名字创建帐户,所以如果有人试图登录,你知道是有人在做他们不应该做的事情。

    确保在网站上拥有真正权力的人都有一个安全的密码。要求管理员/版主使用较长的密码,密码中混合了字母、数字和符号。拒绝普通用户的简单密码,并给出解释。

    你能做的最简单的事情之一就是告诉人们当有人试图登录他们的帐户时,如果不是他们,给他们一个链接来报告事件。一条简单的消息,当他们登录时,就像"有人试图在星期三凌晨4:20登录您的帐户,废话连篇。如果不是你,点击这里。"它可以让你保留一些关于攻击的统计数据。如果您看到欺诈访问量突然增加,则可以加强监控和安全措施。


    如果我正确理解蛮力攻击的mo,那么一个或多个用户名将被不断尝试。

    这里有两个我认为还没有看到的建议:

    • 我一直认为,标准的做法是在每个用户每次错误登录之后都有一个短暂的延迟(大约一秒钟)。这威慑了蛮力,但我不知道一秒钟的延迟能阻止字典攻击多久。(10000字字典==10000秒==3小时左右。嗯,还不够好。)
    • 与其全站慢下来,不如让一个用户名慢下来。每一次错误的尝试,油门都会变得越来越猛(我想达到一个极限,这样真正的用户仍然可以登录)

    编辑:响应用户名限制的评论:这是一个特定于用户名的限制,不考虑攻击源。

    如果用户名受到限制,那么即使是协调的用户名攻击(多个IP,每个IP一次猜测,相同的用户名)也会被捕获。个别用户名受限制保护,即使攻击者在超时期间可以自由尝试其他用户/密码。

    从攻击者的角度来看,在超时期间,您可以第一次猜测100个密码,并快速发现每个帐户一个错误的密码。您可能只能对同一时间段进行50秒的猜测。

    从用户帐户的角度来看,即使猜测来自多个来源,破解密码仍然需要相同的平均猜测次数。

    对于攻击者来说,最多只需要破坏100个帐户,就像破坏1个帐户一样,但是由于您没有在整个站点范围内进行限制,所以可以很快地增加限制。

    额外改进:

    • 检测猜测多个帐户的IP-408请求超时
    • 检测猜测相同帐户的IP—408在大量(比如100次)猜测之后请求超时。

    用户界面想法(在此上下文中可能不适用),这也可以改进上述内容:

    • 如果您控制着密码设置,那么向用户展示他们的密码有多强会鼓励他们选择一个更好的密码。
    • 如果您控制着登录页面,在对一个用户名进行少量(比如10次)猜测之后,提供一个验证码。


    认证有三个因素:

  • 用户知道一些事情(如密码)
  • 用户有一些东西(例如,遥控钥匙)
  • 用户就是某种东西(如视网膜扫描)
  • 通常,网站只执行政策1。即使大多数银行也只执行政策1。相反,它们依赖于"知道其他事情"的双因素身份验证方法。(例如:用户知道他们的密码和他们母亲的婚前姓。)如果您能够,添加第二个身份验证因素的方法并不太困难。

    如果您可以生成大约256个随机字符,那么您可以在16×;16表中构造它,然后让用户给出单元格A-14表中的值。当用户注册或更改密码时,给他们表并告诉他们打印并保存它。

    这种方法的困难在于,当用户忘记了他们的密码,就像他们会忘记的那样,你不能只提供标准的"回答这个问题并输入一个新密码",因为这也容易受到暴力的攻击。另外,你不能重置它,然后给他们发一封新的邮件,因为他们的邮件也可能会受到影响。(请参见:makeuseof.com及其被盗域名。)

    另一个想法(包括小猫),是博亚所谓的Sitekey(我相信他们注册了这个名字)。简单地说,您让用户在注册时上载图像,当他们尝试登录时,让他们从8个或15个(或更多)随机图像中选择图像。因此,如果用户上传了他们的小猫的照片,理论上只有他们知道在所有其他的小猫(或花或其他什么)中哪一张是他们的。这种方法唯一真正的易变性是中间人攻击。

    还有一个想法(尽管没有Kittens),就是跟踪用户访问系统时使用的IP,并要求他们在从以前没有的地址登录时执行额外的身份验证(captcha、pick a kitty、pick a key from this table)。此外,与Gmail类似,允许用户查看最近登录的位置。

    编辑,新想法:

    验证登录尝试的另一种方法是检查用户是否来自您的登录页面。你不能检查推荐人,因为他们很容易被伪造。您需要的是在用户查看登录页面时在会话var中设置一个键,然后在提交登录信息时检查以确保该键存在。如果bot不从登录页面提交,它将无法登录。您还可以在这个过程中引入JavaScript,通过使用它来设置一个cookie,或者在表单加载后向表单添加一些信息来促进这一点。或者,您可以将表单分为两个不同的提交(即,用户输入用户名、提交,然后在新页面上输入密码并再次提交)。

    在这种情况下,关键是最重要的方面。生成它们的一种常见方法是用户数据、IP和提交时间的某种组合。


    我不得不问你是否已经对这个问题做了成本效益分析;听起来你在试图保护自己免受一个攻击者的攻击,他有足够的网络存在来猜测一些密码,每个IP可能发送3-5个请求(因为你已经取消了IP限制)。这种攻击大约要花多少钱?它是否比您试图保护的帐户的价值更贵?有多少庞大的僵尸网络想要你拥有的东西?

    答案可能是否定的——但如果是这样,我希望您能从某种类型的安全专业人员那里得到帮助;编程技能(和stackoverflow分数)与安全技术没有很强的关联。


    我之前回答过一个非常类似的问题,关于如何在PHP中限制用户登录尝试。我将在这里重申这个建议的解决方案,因为我相信你们中的许多人会发现它是信息性的,并且可以看到一些实际的代码。请牢记,使用验证码可能不是最佳解决方案,因为目前验证码终结者使用的算法越来越精确:

    您不能简单地通过将限制链接到单个IP或用户名来防止DOS攻击。该死的,你甚至不能真正阻止使用这种方法的快速登录尝试。

    为什么?因为攻击可以跨越多个IP和用户帐户,以绕过限制尝试。

    我在其他地方看到过,理想情况下,您应该跟踪整个站点中所有失败的登录尝试,并将它们关联到时间戳,也许:

    1
    2
    3
    4
    5
    6
    CREATE TABLE failed_logins(
        id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
        username VARCHAR(16) NOT NULL,
        ip_address INT(11) UNSIGNED NOT NULL,
        attempted DATETIME NOT NULL
    ) engine=InnoDB charset=UTF8;

    根据给定时间内登录失败的总次数决定某些延迟。您应该基于从您的failed_logins表中提取的统计数据,因为它会随着时间的推移而变化,这取决于用户的数量以及他们中有多少人可以调用(和键入)他们的密码。

    1
    2
    3
    10 failed attempts = 1 second
    20 failed attempts = 2 seconds
    30 failed attempts = reCaptcha

    查询每个登录失败尝试的表,以查找给定时间段内失败登录的次数,例如15分钟:

    1
    SELECT COUNT(1) AS failed FROM failed_logins WHERE attempted > DATE_SUB(NOW(), INTERVAL 15 minute);

    如果给定时间段内的尝试次数超过了您的限制,则强制限制或强制所有用户使用验证码(即recaptcha),直到给定时间段内失败的尝试次数小于阈值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // array of throttling
    $throttle = array(10 => 1, 20 => 2, 30 => 'recaptcha');

    // assume query result of $sql is stored in $row
    $sql = 'SELECT MAX(attempted) AS attempted FROM failed_logins';
    $latest_attempt = (int) date('U', strtotime($row['attempted']));
    // get the number of failed attempts
    $sql = 'SELECT COUNT(1) AS failed FROM failed_logins WHERE attempted > DATE_SUB(NOW(), INTERVAL 15 minute)';
    // assume the number of failed attempts was stored in $failed_attempts
    krsort($throttle);
    foreach ($throttle as $attempts => $delay) {
        if ($failed_attempts > $attempts) {
            // we need to throttle based on delay
            if (is_numeric($delay)) {
                $remaining_delay = time() - $latest_attempt - $delay;
                // output remaining delay
                echo 'You must wait ' . $remaining_delay . ' seconds before your next login attempt';
            } else {
                // code to display recaptcha on login form goes here
            }
            break;
        }
    }

    在某个阈值上使用recaptcha可以确保最小化来自多个前端的攻击,并且正常的站点用户不会因合法的失败登录尝试而经历显著的延迟。我不能预防高rantee,因为它已经扩展到captcha可以被破解。还有其他的解决方案,也许是"给这只动物命名"的变体,它可以作为一个很好的替代品。


    要将Jens的方案总结为伪状态转换图/规则库:

  • 用户+密码->输入
  • 用户+!密码->拒绝
  • 用户+已知IP(用户)->前门,// never throttle
  • 用户+未知的_ip(用户)->catflap
  • (拒绝>n)通过CatFlaps(位置)->节流CatFlaps(位置)// slow the bots
  • catflap+throttle+password+captcha->entry // humans still welcome
  • catflap+throttle+password+!captcha->denied // a correct guess from a bot
  • 观察:

    • 不要给前门加油门。埃尔博尼亚州警察把你的电脑放在你家里,但不能审问你。蛮力是一个可行的方法从你的电脑。
    • 如果您提供"忘记密码?"链接,然后您的电子邮件帐户成为攻击面的一部分。

    这些观察涵盖了不同类型的攻击,你正试图反击。


    看起来你在试图防御缓慢分布的蛮力。你不能做那么多。我们使用的是PKI,没有密码登录。这是有帮助的,但是如果您的客户每隔一段时间就有机会访问一次工作站,这就不太适用了。


    免责声明:我为一家双因素公司工作,但我不是来插电的。以下是一些观察结果。

    使用XSS和浏览器vuln可以窃取cookie。用户通常会更改浏览器或清除cookie。

    源IP地址同时是动态变量和伪造的。

    验证码很有用,但不能验证特定的人。

    多种方法可以成功地结合在一起,但好的品味肯定是有序的。

    密码的复杂性是好的,任何基于密码的关键依赖于密码具有足够的熵。imho,在安全的物理位置写下的强密码比内存中的弱密码要好。人们知道如何评估纸质文档的安全性,比他们知道如何在三个不同的网站上作为密码时计算狗的名字中的有效熵要好得多。考虑让用户能够打印出一个大的或小的满是一次性使用密码的页面。

    像"你高中的吉祥物是什么"这样的安全问题大多是"你知道的东西"的另一种糟糕的形式,大多数问题在公共领域很容易被猜测或直接暴露出来。

    正如您所指出的,限制失败的登录尝试是防止暴力攻击和轻松地给帐户加药之间的权衡。激进的锁定策略可能反映出对密码熵缺乏信心。

    我个人并不认为在网站上强制密码过期有什么好处。攻击者得到您的密码一次,然后他就可以更改密码,并尽可能轻松地遵守该策略。也许一个好处是,如果攻击者更改帐户密码,用户可能会更快地注意到。更好的情况是,在攻击者获得访问权之前,不知何故地通知了用户。在这方面,"自上次登录以来尝试失败n次"之类的消息很有用。

    最好的安全性来自第二个身份验证因素,该因素相对于第一个因素是带外的。正如您所说,"您拥有的东西"中的硬件令牌非常好,但许多(不是全部)都有与其分发相关的实际管理开销。我不知道有什么生物识别"你是什么"的解决方案适合网站。一些双因素解决方案与OpenID提供程序一起工作,一些具有php/perl/python sdk。


    我的最高建议是,只要确保让用户知道他们的帐户登录尝试有问题就行了。--如果有证据表明有人试图进入他们的帐户,用户可能会更加认真地对待他们的密码强度。

    实际上,我发现有人入侵了我哥哥的MySpace帐户,因为他们试图进入我为他设置的Gmail帐户,并使用了"通过电子邮件重置密码"功能…它进入了我的收件箱。


  • 在输入正常密码之前需要一次性密码怎么样?这就很明显有人在有很多机会猜测主密码之前就开始攻击了?

  • 保持一个全局的登录失败计数/比率-这是一个攻击的指示器-在攻击期间,对登录失败更严格,例如更快地禁止IP。


  • 由于有几个人将captcha作为回退的人工机制,所以我添加了一个关于captcha有效性的早期stackoverflow问题和线程。

    Recaptcha是否被破解/黑客攻击/OCR攻击/击败/破坏?

    使用captcha并不能限制限制限制和其他建议的改进,但是我认为将captcha作为回退的答案的数量应该考虑到寻求破坏安全性的人可以使用的基于人的方法。


    这里有点晚,但我在想,假设是一个很难的情况-攻击者使用大量随机IP、随机用户名和随机密码,从10000个最流行的列表中选择。

    你可以做的一件事是,特别是当系统受到攻击时,在系统上有很多错误的密码尝试,尤其是当密码的熵很低时,你可以问一个次要的问题,例如,你父母的名字是什么。如果攻击者使用密码"password1"攻击了一百万个帐户,他们很可能会得到很多,但他们获得正确名称的几率会大大降低成功率。


    我不相信有一个完美的答案,但我会倾向于接近它的基础上,试图混淆机器人,如果一个攻击被察觉。

    在我的头脑中:

    切换到备用登录屏幕。它有多个用户名和密码空白,确实会出现,但只有一个在正确的位置。字段名是随机的——会话密钥与登录屏幕一起发送,然后服务器可以找出哪些字段是什么。不管成功与否,它都会被丢弃,这样你就不能尝试重播攻击——如果你拒绝密码,他们会得到一个新的会话ID。

    任何与错误字段中的数据一起提交的表单都假定来自机器人——登录失败、句点和IP被限制。确保随机字段名与合法字段名不匹配,这样,使用记住密码的东西的人就不会被误导。

    接下来,换一种验证码怎么样:你有一系列不会给人类带来问题的问题。但是,它们不是随机的。当攻击开始时,每个人都会得到问题1。一小时后,问题1被丢弃,不再使用,每个人都会得到问题2等等。

    由于问题的可处理性,攻击者无法探测下载数据库并将其放入机器人中。他必须在一小时内向僵尸网络发送新的指令,才能做任何事情。


    您还可以根据用户密码的强度进行节流。

    当用户注册或更改其密码时,您将计算其密码的强度等级,例如1到10之间。

    类似"password"的分数是1,而"c6eqaprepe7et*awr@ch"的分数可能是9或10,分数越高,节流所需的时间越长。


    当我问这个问题时,我通常听到的第一个答案是更改端口,但请忘记这一点,只需禁用IPv4即可。如果您只允许来自IPv6网络的客户机,您将不再祈祷简单的网络扫描,攻击者将求助于DNS查找。不要在与Apache(aaaa)/sendmail(mx->aaaaa)相同的地址上运行,也不要在给每个人的地址上运行(aaaa)。确保你的区域不能被xferd,等等,你允许任何人下载你的区域?

    如果机器人程序发现你的服务器设置了新的主机名,只需在主机名前加一些胡言乱语,然后更改你的地址。保留旧名称,甚至为bot net设置**蜜罐名称以超时。

    **测试您的反向(ptr)记录(在ip6.arpa下),看看它们是否可以在有记录的/4和没有记录的/4中归零,也就是说,通常ip6.arpa将有大约32个"."在地址中,但尝试最后几个丢失的记录可能会避开有记录的网络块和其他没有记录的网络块。如果您再进一步,就有可能跳过大部分地址空间。

    在最坏的情况下,用户将不得不设置一个IPv6隧道,这不像他们必须去到vpning到一个dmz…尽管有人想知道为什么这不是第一个选择。

    同样,kerberos很酷,但是imho ldap失败了(nisplus在技术上有什么问题?我已经读到Sun决定用户需要LDAP,因此他们放弃了NIS+)。如果没有LDAP或NIS,kerberos就可以正常工作,只需要逐个主机地管理用户。使用Kerberos可以使您获得一个易于使用的(如果不是自动的)PKI。