tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Apache Wiki <wikidi...@apache.org>
Subject [Tapestry Wiki] Update of "Tapestry5HowToMitigatingLoginAttacks" by Peter Stavrinides
Date Thu, 12 Feb 2009 14:08:10 GMT
Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Tapestry Wiki" for change notification.

The following page has been changed by Peter Stavrinides:
http://wiki.apache.org/tapestry/Tapestry5HowToMitigatingLoginAttacks

------------------------------------------------------------------------------
  = How To Mitigate Login Attacks =
  
- Brute force and dictionary attacks use recursing login attempts to guess passwords. One
of the most effective approaches to mitigate this is to implement a delay between logins,
which effectively slows down these scripts just enough to render them useless. The problem
with implementing an 'account lockout' as an alternative is that its open for abuse, and creates
an administrative overhead. This is a simplified example to illustrate this  principle.
+ Brute force and dictionary attacks use recursive attempts to guess passwords. One of the
most effective approaches to mitigate this is to implement a delay between login attempts,
which effectively slows down these scripts just enough to render them useless. The problem
with implementing 'account lockout' as an alternative is that its open for abuse, and also
may create some administrative overhead. This short article uses a simplified example  to
illustrate this  principle.
  
  
- 1. The first step is to create a simple Pojo that represents a failed login, we also give
it the ability to count failed attempts.
+ 1. The first step is to create a simple Pojo that represents a failed login, we give it
the ability to count failed attempts, and give it a time to live.
  
  {{{
  /**
@@ -17, +17 @@

  	/** Tracks the number of failed login attempts */
  	private int failedLoginCount_ = 0;
  
- 	/** The number of times a sleep was invoked */
+ 	/** The number of times sleep was incurred */
  	private int sleepCount = 0;
  	
- 	/** The delay period (10 minutes in this case)  */
+ 	/** The delay period (10 minutes) */
  	private int SLEEP_INTERVAL = 600000; 
  	
- 	/** @return the failed login count */
- 	public int getFailedLoginCount() {
- 		return failedLoginCount_;
- 	}
+ 	/** The login expired time */
+ 	private Date expireTime_ = null;
+ 	
+ 	
+ 	/** 2 Hours of inactivity expires the counter */
+ 	private final int TIME_TO_LIVE = 2;
+ 	
  	
  	/**
+ 	 * A method to increment the failed logins
- 	 * A method to provide protection against brute force or dictionary attacks
- 	 * 
  	 * @throws InterruptedException 
  	 */
  	public void incrementFailedLoginCount() throws InterruptedException {
- 		//increment the failed login counter
-                 failedLoginCount_ += 1;
  		
+ 		//reset all counters if the object expired
+ 		if (isExpired()) {
+ 			sleepCount = 0;
+ 			failedLoginCount_ = 0;
+ 		}
+ 		
+ 		 //increment the failed login counter
+ 		failedLoginCount_ += 1;
+ 
-                 //every 5th failed login we double the sleep period 
+ 		//every 5th failed login we double the sleep period 
-                 if(failedLoginCount_ == 5){
+ 		if(failedLoginCount_ == 5){
  			sleepCount +=1;	failedLoginCount_ = 0;
- 			Logger.getLogger(getClass()).warn(
- 					"Multiple failed login attempts setting sleep interval: "
- 							+ SLEEP_INTERVAL);
  			Thread.sleep(SLEEP_INTERVAL);
  			SLEEP_INTERVAL = (SLEEP_INTERVAL * 2);
  		}
+ 		
+ 		//set the expires time
+ 		Calendar ts = Calendar.getInstance();
+ 		ts.add(Calendar.HOUR, 2);
+ 		expireTime_ = new Date(ts.getTimeInMillis());
+ 	}
+ 
+ 	/** @return true if this object is expired */
+ 	public boolean isExpired() {
+ 		if(expireTime_ == null){
+ 			return false;
+ 		}
+ 		Date now = new Date();
+ 		long diff = now.getTime() - expireTime_.getTime();
+ 		int hours = (int) (Math.floor(diff / 1000 / 60 / 60));
+ 		// the object has expired 
+ 		if (hours >= TIME_TO_LIVE) {
+ 			return true;
+ 		}
+ 		return false;
  	} 
  	
  	/**
@@ -60, +86 @@

  
  }}}
  
- 2. A Tapestry Singleton service to store failed login attempts, we use the callers IP address
as an identifier
+ 2. The next step is to create a Tapestry Singleton service. The service uses a map store
failed login attempts, and the callers IP address is used as an identifier. 
  
  {{{
  
  public class FailedLoginTracker {
  	
- 	/** A map to track failed logins by IP */
+ 	/** A map to store the FailedLogin objects */
  	private static ConcurrentHashMap<String, FailedLogin> failedLogins_ = new ConcurrentHashMap<String,
FailedLogin>();
  	
+ 	
+ 	/**
+ 	 * @param ipAddress
+ 	 * @throws InterruptedException
+ 	 */
  	public void setFailedLogin(String ipAddress) throws InterruptedException{
  		FailedLogin login = null;
  		if(failedLogins_.containsKey(ipAddress)){
@@ -79, +110 @@

  			login = new FailedLogin();
  		}
  		failedLogins_.put(ipAddress, login);
+ 		
+ 		// Not essential, but I prefer to take out the trash
+ 		cleanUpExpired();
+ 	}
+ 
+ 
+ 	/**
+ 	 * A method to clean the map of failed logins
+ 	 * that have passed their time to live
+ 	 */
+ 	private void cleanUpExpired() {
+ 		for(String ip : failedLogins_.keySet()) {
+ 			if(failedLogins_.get(ip).isExpired())
+ 				failedLogins_.remove(ip);
+ 		}
  	}
  }
  
@@ -92, +138 @@

  
  }}}
  
+ = Conclusion =
  
- 4. I have tried to keep the example above as simple as possible, but there are a number
of enhancements that one could recommend, the most obvious being more intelligent tracking
options other than just the IP, as well as the following modification, which ensures that
the FailedLogin object expires (resetting itself) once its 'time to live' passes. The following
code is untested therefore omitted from step 1.
+ I have tried to keep the example above concise, but there are a several enhancements that
come to mind: 
+  * The most obvious being more intelligent tracking options other than just by IP, as these
can be easily faked.
+  * It also might be worth checking multiple user names tried by the same IP, or if each
failed attempt by the same user / IP uses a different session id
  
+ By combining these checks intelligently you can get a good indication of a scripted attack.

- {{{
- 	public void incrementFailedLoginCount() throws InterruptedException {
- 		
- 		if(expireTime_ != null){
- 			Date now = new Date();
- 			long diff = now.getTime() - expireTime_.getTime();
- 			int hours = (int) (Math.floor(diff / 1000 / 60 / 60));
- 			//this object has expired so 
- 			if (hours >= TIME_TO_LIVE){
- 				sleepCount = 0;
- 				failedLoginCount_ = 0;
- 			}
- 		} 
- 		
- 		failedLoginCount_ += 1;
- 		if(failedLoginCount_ == 5){
- 			sleepCount +=1;	failedLoginCount_ = 0;
- 			Logger.getLogger(getClass()).warn(
- 					"Multiple failed login attempts setting sleep interval: "
- 							+ SLEEP_INTERVAL);
- 			Thread.sleep(SLEEP_INTERVAL);
- 			SLEEP_INTERVAL = (SLEEP_INTERVAL * 2);
- 		}
- 		
- 		//set the expires time
- 		Calendar ts = Calendar.getInstance();
- 		ts.add(Calendar.HOUR, 2);
- 		expireTime_ = new Date(ts.getTimeInMillis());
- 		System.out.println(expireTime_);
- 	} 
- }}}
  
+ ... any suggestions, corrections etc. are encouraged!
+ 
+ Peter
+ 

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org


Mime
View raw message