我想听听您对以下问题的建议:
我在 Tomcat 上使用 jsp 有网络应用程序,用户在输入登录名和密码(两者都是自动定义的,并且在创建帐户后不会更改)后就可以进入他们的个人页面。
我想在服务器上使用带有用户登录ID的ArrayList对用户帐户进行一些保护,其中某些登录ID的不成功登录数量将被保留(一段时间后会有线程将金额值归零)。
如果金额大于某个定义的值 - 阻止登录(直到金额被清理)并发送到用户电子邮件链接,点击该金额后,该金额值将在服务器内部设置为 0。我会努力解决这个问题,但我的问题是这种方法是否正确,这样的 ArrayList 是否能满足需求:
List<User> users = Collections.synchronizedList(userList);
并使用同步的set
和get
方法访问它。
目的是防止暴力攻击(手动甚至服务器驱动)。
有没有办法防御访问攻击(短时间内多次登录尝试)?
提前致谢。
最佳答案
and access it using synchronized set and get methods.
您究竟如何从这样的列表中获取用户?在每次登录尝试期间,您必须迭代整个列表才能找到该用户,或者您始终知道数组中的确切位置,或者列表可能已排序,因此您可以使用二分搜索,但在这种情况下,插入在复杂性方面效率低下。 此外,用户结构或您想要存储最新失败登录的任何位置也必须同步。
恕我直言,就实现而言,最简单的解决方案之一可能如下。请注意,在一段时间后不需要额外的线程来重置尝试次数,但在一段时间后,您必须清除最近失败登录时间超出可观察时间范围的对。导致结构尺寸可能会以其他方式增大。
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();
}
}
重置时(单击通过电子邮件发送的链接后),您可以简单地删除使用此类登录的条目。除了字符串键之外,您还可以存储 id 或其他内容(但要确保键类具有 equals hashcode 约定并且是不可变的)
另请注意,如果有人尝试破解大量登录,此方案将不会非常有效。 代码中可能存在一些错误,但我希望您能理解这一点。
关于java - 如何保护用户页面免受暴力攻击,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34151920/