【问题标题】:How can I protect user pages from brute-force attack如何保护用户页面免受暴力攻击
【发布时间】:2016-03-13 03:20:00
【问题描述】:

我想听听您对以下方面的建议:

我在 Tomcat 上使用 jspweb-application,用户在输入登录名和密码后(两者都是自动定义的,在创建帐户后不会更改)能够进入他们的个人页面。

我想在具有用户登录 ID 的服务器上使用 ArrayList 对用户帐户进行一些保护,其中某些登录 ID 的未成功登录数量将被保留(会有线程制作金额值一段时间后为零)。

如果金额大于某个定义的值 - 阻止登录(直到清除金额)并发送给用户电子邮件链接,点击后服务器内部的金额值将设置为 0。我会努力解决这个问题,但我的问题是这种方法是否正确,这样的ArrayList 将满足需求:

List<User> users = Collections.synchronizedList(userList);

并使用同步的setget 方法访问它。
目的是防止暴力攻击(手动甚至服务器驱动)。

有没有办法防御访问攻击(在短时间内进行多次登录尝试)?

提前致谢。

【问题讨论】:

  • @nfechner 这是可能的,谢谢你的想法。但我也在寻找其他解决方案。
  • 使用synchronized 不会为您提供任何程度的针对暴力攻击的保护。完全没有。因为那甚至没有任何意义。您需要将 HTTP 请求/秒/IP 限制在合理的数量 - 如果达到阈值,则停止返回数据。这与重新验证码相结合将在一定程度上保护您的网页
  • @specializt synchronized 是必需的,因为会从List&lt;User&gt; users 读取数据并在那里添加新值。

标签: java jsp security


【解决方案1】:

我对 java 的了解不够,无法向您推荐代码。

但是,如果您可以使用 IP 地址存储请求,则可以对其设置特殊规则(如防火墙)。

比如说,当同一个 IP 在一分钟内发送 100 个请求时,你可以禁止它访问你的网站。

已经给出的一个好建议是在回答之前增加延迟。如果必须测试数百万个密码才能找到有效密码,那么您添加的每一秒都会使蛮力攻击非常耗时。

【讨论】:

    【解决方案2】:

    并使用同步的 set 和 get 方法访问它。

    您究竟想如何从此类列表中获取用户?在每次登录尝试期间,您必须遍历整个列表以找到该用户,或者您始终知道数组中的确切位置,或者可能列表已排序,因此您可以使用二进制搜索,但在这种情况下,插入在复杂性方面效率低下. 此外,必须同步用户结构或要存储最新失败登录的任何位置。

    恕我直言,就实现而言,最简单的解决方案之一可能是以下。请注意,在一段时间后不需要额外的线程来重置尝试次数,但是您必须在一段时间内清除最近失败登录时间超出可观察时间范围的对。以其他方式导致结构的大小增长。

    ConcurrentHashMap<String, List<Long>> loginFails = new ConcurrentHashMap<>();
    int ATTEMPTS_TO_FREEZE = 5;
    int TIME_FRAME_IN_MINUTES = 5;
    

    以登录为键,最后一次失败的登录列表为值。在我们的例子中,让阈值为 5。 登录前检查权限。如果 5 在最后 5 分钟内失败 -> 拒绝

    List<Long> attempts = loginFails.get(login);
    if (attempts != null) {
        synchronized(attempts) {
            if (attempts.size() == ATTEMPTS_TO_FREEZE 
                && attempts.peek() > System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(TIME_FRAME_IN_MINUTES)) {
            //return some warning to user, that he exceeded number of attempts
            }
        }
    }
    

    登录失败后

    Queue<Long> attempts = loginFails.get(login);
    if (attempts == null) {
        attempts = loginFails.putIfAbsent(login, new LinkedList<Long>());
        if (attempts == null) {
            attempts = loginFails.get(login);
        }
    }
    synchronized (attempts) {
        attempts.add(System.currentTimeMillis());
        if (attempts.size() > ATTEMPTS_TO_FREEZE) {
            attempts.remove();
        }
    }
    

    在重置时(单击电子邮件链接后),您可以简单地使用此类登录删除条目。除了 String 键,您还可以存储 id 或其他任何东西(但要确保键类具有等于哈希码合约并且是不可变的)

    另外请注意,如果有人试图破解大量登录,此方案的效率不会很高。 代码中可能存在一些错误,但我希望你明白这一点。

    【讨论】:

    • 看起来不错,谢谢。每个 User 都有 id,它会在 hashCode() 和 equals() 方法中使用,因此查找数组中指定 id 的 user 对象应该很快。
    • hashCode 不用于 ArrayList 或 LinkedList 或任何其他 List 集合(可以有重复项),它用于基于哈希的集合,例如 HashSet、HashMap
    • 但我将使用它从数组中获取user 对象,例如users.get(logUser) - 这样在数组中找到该对象需要很短的时间。还是我错了?
    • logUser 应该是一个 int,因为在 List get 中是这样定义的 E get(int index);。以及如何计算您的logUser
    • 是的,你是对的。我最好使用ConcurrentHashMap 处理有关用户登录和监控登录尝试的数据。
    【解决方案3】:

    添加saltpepper 可以轻松防止暴力攻击。这些本质上是附加到密码和/或用户名的随机字符,这使得暴力破解成功的几率要低得多。

    Thisthis 详细讨论了盐和胡椒。

    This wikipedia 文章讨论了该方法的一般基础。

    【讨论】:

    • 感谢您的建议,很棒的信息。我将在另一个应用程序中使用它,但这里的登录名和密码是由服务器预定义的,为了方便用户使用非常简单。
    • 您仍然可以使用这种方法,用户将能够输入普通的明文密码和用户名。 saltpepper 对用户隐藏。它在注册/登录密码期间附加到用户名/密码。这个想法是,添加随机字符可以大大降低密码被暴力破解的可能性。无论用例如何,大多数系统都应该(至少)使用 salt
    • 但是,如果密码非常简单(比如说 5 位数字),手动蛮力攻击将如何提供帮助?我想让这种方式很难破解用户密码。
    • 我推荐阅读this,它完美地描述了盐如何增加安全性。基本思路是: 1.用户使用密码password = bob注册。 2. 在注册时,您生成一个 salt salt = t372ri3fiuv 3. 在密码和散列前添加盐:h = hash(salt + password) 4. 然后保存 salt 和散列的 @987654329 @ 数据库密码。 5. 下次用户登录时,您将保存的盐添加到前面,重新计算哈希并检查是否匹配。这是打造更安全的登录系统的第一步。
    • 在我的例子中,salt 的使用没有任何意义,因为密码是由服务器生成的并且只包含数字。我想对其他地方的手动蛮力进行一些保护,我只需更改密码的生成方式以获得更复杂的密码。
    猜你喜欢
    • 1970-01-01
    • 2011-10-02
    • 1970-01-01
    • 1970-01-01
    • 2020-05-12
    • 2016-10-28
    • 1970-01-01
    • 2011-05-14
    • 1970-01-01
    相关资源
    最近更新 更多