Throttling Login Attempts

New CAS documentation site

CAS documentation has moved over to apereo.github.io/cas, starting with CAS version 4.x. The wiki will no longer be maintained. For the most recent version of the documentation, please refer to the aforementioned link.

Versions prior to CAS 3.3.5, had a simplistic approach to throttling attempts based on IP Addresses. Since CAS 3.3.5, those options have been expanded.

For single node CAS instances, there are two in-memory approaches: One by IP Address, and one by IP Address + username combination.
For multi-node CAS instances there is one option that combines the auditing capabilities of the Inspektr package with the throttling package (and relies on a database).

Throttle Intercept Activation and Release

The throttle feature intercepts attempted logins for an IP or IP+username once the configured failed login threshold per time has been reached. At that point, further logins from that source are intercepted before reaching the CAS service. Once  throttle intercept has been activated for an IP or IP/username, it stays active until the count of failed logins decays to less than the threshold. Further valid or invalid login attempts from a throttled source increase the count for each attempt. Decay is performed by repeatedly decrementing a counter of failed logins/further attempts by 1.  The rate of decrements is controlled by the repeatInterval parameter specified in the periodicThrottleCleanerTrigger bean (see below, note that this parameter is set in milliseconds).  Once the failed login count passes under the failureThreshold value, the throttle is released.

3.5.0

As of version 3.5.0, the behavior of throttling (for both the in-memory and Inspektr approaches) has changed according to CAS-1107 - Getting issue details... STATUS . Throttling is now performed based on a rate, the number of failed login attempts allowed per minute. For example, consider a case in Inspektr where failureRangeInSeconds=60 and failureThreshold=10. This leads to a rate of 10 failed attempts per minute, or one every six seconds. The throttler works by maintaining this rate between pairs of login attempts. A throttle flag is set which will block further login attempts if the last two attempts surpassed the threshold rate. Consider the code snippet below which returns the value of the flag.

Excerpt from InspektrThrottledSubmissionByIpAddressAndUsernameHandlerInterceptorAdapter.java
protected boolean exceedsThreshold(final HttpServletRequest request) {

	...

	// Compute rate in submissions/sec between last two authn failures and compare with threshold
	return 1000.0 / (failures.get(0).getTime() - failures.get(1).getTime()) > getThresholdRate();
}

 

Notes on Logging of Throttled Logins

Failed logins are logged in your Inspektr audit table, if Inspektr is configured, and/or in your cas.log file.Once failed logins reach the threshold you configure, throttling is logged in your cas.log file as:

WARN [org.jasig.cas.web.support.InMemoryThrottledSubmissionByIpAddressHandlerInterceptorAdapter] - *** Possible Hacking Attempt from [x.x.x.x]. More than yy failed login attempts within zz seconds.

Once throttling has started for an IP, further attempts to log in are intercepted before they get to the CAS application and logged as 403 errors in your webserver access logs. If you're not fronting your CAS Tomcat server with Apache httpd, you may need to configure the access log "Valve" to get standard access logs in Tomcat (see http://tomcat.apache.org/tomcat-6.0-doc/config/valve.html).

Configuration of the In-Memory Approaches

 

CAS-1107 - Getting issue details... STATUS tracks an issue that breaks the in-memory approach specifically for CAS versions 3.4 and higher. For this reason, the in-memory approach is only illustrated below for a prior CAS version.

 

  1. Configuration of the in-memory approach is simple. Merely configure the interceptors into the handler mappings in the cas-servlet.xml (you really only need to do it in the one that handles /login).
  2. Configure in a new file (e.g. throttleInterceptorTrigger.xml) in spring-configuration directory a Quartz trigger to call the decrementCount() method as frequently as you'd like to decrement the failure attempts (1 per second is probably sufficient).

Configuration of the Inspektr Approach

As you've already done the work for configuring Inspektr, this method merely requires you to configure the new interceptor and give it a DataSource and an AuditTrailManager instance.

Example of In-Memory

In cas-servlet.xml

3.5.x

No proper "loginController" is defined in cas-servlet.xml, unlike in earlier versions. All you need to do is add the throttleInterceptor to the FlowHandlerMapping.

<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping" p:flowRegistry-ref="flowRegistry" p:order="2">
	<property name="interceptors">
		<list>
			<ref local="localeChangeInterceptor"/>
			<ref bean="throttleInterceptor"/>
		</list>
	</property>
</bean>

3.3.x

<bean id="handlerMappingB" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
   <property name="mappings">
      <props>
         <prop key="/login">loginController</prop>
      </props>
   </property>
   <property name="interceptors">
      <list>
         <ref bean="localeChangeInterceptor" />
         <ref bean="throttleInterceptor" />
      </list>
   </property>
</bean>

In spring-configuration/throttleInterceptorTrigger.xml

3.3.x - 3.5.x

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

<bean id="throttleInterceptor" class="org.jasig.cas.web.support.InMemoryThrottledSubmissionByIpAddressAndUsernameHandlerInterceptorAdapter" 
   p:failureRangeInSeconds="120"
   p:failureThreshold="100"/>

<bean id="throttleInterceptorJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
   p:targetObject-ref="throttleInterceptor"
   p:targetMethod="decrementCounts" />

<bean id="periodicThrottleCleanerTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"
   p:jobDetail-ref="throttleInterceptorJobDetail"
   p:startDelay="0"
   p:repeatInterval="1000" />
</beans>

Example of Inspektr

3.3.x

<!-- Handler Mapping -->
<bean id="handlerMappingB" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
   <property name="mappings">
      <props>
         <prop key="/login">loginController</prop>
      </props>
   </property>
   <property name="interceptors">
      <list>
            <bean class="org.jasig.cas.web.support.InspektrThrottledSubmissionByIpAddressAndUsernameHandlerInterceptorAdapter"
               p:failureRangeInSeconds="120"
               p:failureThreshold="100">
            <constructor-arg index="0" ref="auditTrailManager" />
            <constructor-arg index="1" ref="dataSource" />
         </bean>
         <ref bean="localeChangeInterceptor" />
      </list>
   </property>
</bean>

Note, you should configure Inspektr per the instructions. You may wish to expand the default client IP and server IP table space to account for IPv6.

Recording client IP address

Allow inspektr to record the real client IP address in the web.xml file:

<filter>
  <filter-name>CAS Client Info Logging Filter</filter-name>
  <filter-class>com.github.inspektr.common.web.ClientInfoThreadLocalFilter</filter-class>
  <!-- set this parameter to get real Address from HTTP HEADER when we use Load-balance -->
  <init-param>
    <param-name>alternativeIpAddressHeader</param-name>
    <param-value>X-Forwarded-For</param-value>
  </init-param>
</filter>