如何防止无限重试 - Apache Shiro RESTful服务

时间:2018-03-09 17:23:33

标签: rest security tomcat shiro

目标
我正在使用RESTEasy框架设置RESTful Web服务。为了安全起见,我使用Apache Shiro。我希望我的api停止接受请求或超时登录的人。

问题
每当我使用浏览器(chrome)访问某些URL时,我都可以尝试无限次登录。似乎是一个非常糟糕的主意,允许这样做。作为一项措施,我确保记住nr次登录尝试,用户3次后无法登录。但是,通过暴力攻击,您仍然可以阻止所有用户登录。我想要一个更通用的解决方案。

Shiro.ini

[main]
# We store users and passwords inside the realm.
myRealm = com.myproject.shiro.DatabaseRealm
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $cacheManager

[urls]
/api/version = anon
/api/** = authcBasic

DatabaseRealm

public class DatabaseRealm extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // No clue what to do with this functin. I only use authentication and not authorization, so probably just nothing.
        return null;
    }

    /**
     * Check if the user inputted is valid. The user can login if holds:
     * 1. Password is correct. (if not, nrOfLogonAttempts++)
     * 2. LogonUser.nrOfLogonAttemps is less than 3
     * 3. LogonUser.dateEndValid is null or >= today.
     * @param authenticationToken Token with basic information.
     * @return SimpleAuthenticationInfo
     * @throws AuthenticationException Whenever the user cannot login.
     */
    @SuppressWarnings("ConstantConditions")
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws UnknownAccountException, IncorrectCredentialsException, LockedAccountException, ExpiredCredentialsException {
        // Connect with the database.
        DbContext context = null;
        try {
            context = DbContextUtil.getContextFromTomcat();
            // Lookup user in the database.
            LogonUserMyDao logonUserMyDao = new LogonUserMyDao(context);
            LogonuserPojo logonuserPojo = logonUserMyDao.fetchOneByUsername(((UsernamePasswordToken) authenticationToken).getUsername());
            if (logonuserPojo == null) {
                throw new UnknownAccountException("Could not find user.");
            }

            // Check password
            String plainTextPassword = new String(((UsernamePasswordToken) authenticationToken).getPassword());
            if (!BCryptUtil.checkPassword(plainTextPassword, logonuserPojo.getPassword())) {
                // We will note this event.
                logonuserPojo.setNroflogonattempts(logonuserPojo.getNroflogonattempts() + 1);
                logonUserMyDao.update(logonuserPojo);
                context.commit();
                throw new IncorrectCredentialsException("Incorrect password.");
            }

            // Check nrOfLogonAttempts
            if (logonuserPojo.getNroflogonattempts() >= 2) {
                throw new LockedAccountException("Cannot login anymore.");
            }

            // Check date
            if (logonuserPojo.getDateendvalid() != null && DateTimeUtil.isBeforeToday(logonuserPojo.getDateendvalid())) {
                throw new ExpiredCredentialsException("Account is expired.");
            }

            // User is valid, so return some info.
            return new SimpleAuthenticationInfo(logonuserPojo.getUsername(), plainTextPassword, getClass().getName());
        } catch (SQLException e) {
            MyLogger.logError("Could not connect to user database.", e);
            throw new AuthenticationException("Could not connect to databse.");
        } finally {
            if (context != null) {
                try {
                    context.getConnection().close();
                } catch (SQLException e) {
                    MyLogger.logError("Could not close connection", e);
                }
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

您是否在寻找更一般的DDOS保护?根据应用程序的运行位置(例如AWS Shield),有一些选项可供选择。

您还可以使用以下内容阻止连接到达您的数据库:https://github.com/iTransformers/ddos-servlet-filter(但是,仍然需要在您的应用程序中处理请求)

在Shiro方面,计算你的尝试并不是一个坏主意,但你需要注意用户管理方面的事情(用户如何解锁,支持请求?等待30分钟?)而不是记录失败,您可能只想记录/审核所有尝试(当然不包括实际密码)。使用任一选项调用支持或n分钟窗口,这可能有助于提供一些支持或简单查询的上下文。