incubator-jspwiki-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jalka...@apache.org
Subject svn commit: r658843 - in /incubator/jspwiki/trunk: ./ src/com/ecyrd/jspwiki/ src/com/ecyrd/jspwiki/auth/ src/com/ecyrd/jspwiki/util/ tests/com/ecyrd/jspwiki/util/
Date Wed, 21 May 2008 20:17:37 GMT
Author: jalkanen
Date: Wed May 21 13:17:36 2008
New Revision: 658843

URL: http://svn.apache.org/viewvc?rev=658843&view=rev
Log:
JSPWIKI-44: Added a delay between login attempts.

Added:
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/util/TimedCounterList.java
    incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/util/TimedCounterListTest.java
Modified:
    incubator/jspwiki/trunk/ChangeLog
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/Release.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/auth/AuthenticationManager.java
    incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/util/PriorityList.java
    incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/util/AllTests.java

Modified: incubator/jspwiki/trunk/ChangeLog
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/ChangeLog?rev=658843&r1=658842&r2=658843&view=diff
==============================================================================
--- incubator/jspwiki/trunk/ChangeLog (original)
+++ incubator/jspwiki/trunk/ChangeLog Wed May 21 13:17:36 2008
@@ -1,5 +1,12 @@
 2008-05-11  Janne Jalkanen <jalkanen@apache.org>
 
+        * 2.7.0-svn-27
+        
+        * [JSPWIKI-44].  AuthenticationManager now remembers the last logins
+        and slows down repeated access attempts, to a maximum of 20 seconds.
+
+2008-05-11  Janne Jalkanen <jalkanen@apache.org>
+
         * 2.7.0-svn-26
 
         * [JSPWIKI-236]: Fixed localization for the different login/edit

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/Release.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/Release.java?rev=658843&r1=658842&r2=658843&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/Release.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/Release.java Wed May 21 13:17:36 2008
@@ -77,7 +77,7 @@
      *  <p>
      *  If the build identifier is empty, it is not added.
      */
-    public static final String     BUILD         = "26";
+    public static final String     BUILD         = "27";
     
     /**
      *  This is the generic version string you should use

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/auth/AuthenticationManager.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/auth/AuthenticationManager.java?rev=658843&r1=658842&r2=658843&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/auth/AuthenticationManager.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/auth/AuthenticationManager.java Wed May
21 13:17:36 2008
@@ -46,17 +46,30 @@
 import com.ecyrd.jspwiki.event.WikiEventListener;
 import com.ecyrd.jspwiki.event.WikiEventManager;
 import com.ecyrd.jspwiki.event.WikiSecurityEvent;
+import com.ecyrd.jspwiki.util.TimedCounterList;
 
 /**
  * Manages authentication activities for a WikiEngine: user login, logout, and
  * credential refreshes. This class uses JAAS to determine how users log in.
+ * <p>
+ * The login procedure is protected in addition by a mechanism which prevents
+ * a hacker to try and force-guess passwords by slowing down attempts to log in
+ * into the same account.  Every login attempt is recorded, and stored for a while
+ * (currently ten minutes), and each login attempt during that time incurs a penalty
+ * of 2^login attempts milliseconds - that is, 10 login attempts incur a login penalty of
1.024 seconds.
+ * The delay is currently capped to 20 seconds.
+ * 
  * @author Andrew Jaquith
- * @author Janne Jalkanen
  * @author Erik Bunn
  * @since 2.3
  */
 public final class AuthenticationManager
 {
+    /** How many milliseconds the logins are stored before they're cleaned away. */
+    private static final long LASTLOGINS_CLEANUP_TIME = 10*60*1000L; // Ten minutes
+
+    private static final long MAX_LOGIN_DELAY         = 20*1000L; // 20 seconds
+    
     /** The name of the built-in cookie assertion module */
     public static final String                 COOKIE_MODULE       =  CookieAssertionLoginModule.class.getName();
 
@@ -132,6 +145,10 @@
 
     private boolean               m_useJAAS = true;
 
+    /** Keeps a list of the usernames who have attempted a login recently. */
+    
+    private TimedCounterList<String> m_lastLoginAttempts = new TimedCounterList<String>();
+    
     /**
      * Creates an AuthenticationManager instance for the given WikiEngine and
      * the specified set of properties. All initialization for the modules is
@@ -337,6 +354,8 @@
             return false;
         }
 
+        delayLogin(username);
+        
         UserManager userMgr = m_engine.getUserManager();
         CallbackHandler handler = new WikiCallbackHandler(
                 userMgr.getUserDatabase(),
@@ -356,6 +375,34 @@
         }
         return false;
     }
+    
+    /**
+     *  This method builds a database of login names that are being attempted, and will try
to
+     *  delay if there are too many requests coming in for the same username.
+     *  <p>
+     *  The current algorithm uses 2^loginattempts as the delay in milliseconds, i.e.
+     *  at 10 login attempts it'll add 1.024 seconds to the login.
+     *  
+     *  @param username The username that is being logged in
+     */
+    private void delayLogin( String username )
+    {
+        try
+        {
+            m_lastLoginAttempts.cleanup( LASTLOGINS_CLEANUP_TIME );
+            int count = m_lastLoginAttempts.count( username );
+            
+            long delay = Math.min( 1<<count, MAX_LOGIN_DELAY );
+            log.debug( "Sleeping for "+delay+" ms to allow login." );
+            Thread.sleep( delay );
+            
+            m_lastLoginAttempts.add( username );
+        }
+        catch( InterruptedException e )
+        {
+            // FALLTHROUGH is fine
+        }
+    }
 
     /**
      * Logs the user out by retrieving the WikiSession associated with the

Modified: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/util/PriorityList.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/util/PriorityList.java?rev=658843&r1=658842&r2=658843&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/util/PriorityList.java (original)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/util/PriorityList.java Wed May 21 13:17:36
2008
@@ -31,8 +31,6 @@
  *  <p>
  *  Priority is an integer, and the list is sorted in descending order
  *  (that is, 100 is before 10 is before 0 is before -40).
- *
- *  @author Janne Jalkanen
  */
 public class PriorityList
     extends AbstractList

Added: incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/util/TimedCounterList.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/util/TimedCounterList.java?rev=658843&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/util/TimedCounterList.java (added)
+++ incubator/jspwiki/trunk/src/com/ecyrd/jspwiki/util/TimedCounterList.java Wed May 21 13:17:36
2008
@@ -0,0 +1,230 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.    
+ */
+package com.ecyrd.jspwiki.util;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ *  Provides a List in which all items store their addition time. This
+ *  can then be used to clean the list from old items.
+ *  <p>
+ *  This class is thread-safe - all modifications are blocking, but
+ *  reading is non-blocking (unless a write is ongoing).
+ *  
+ *  @param <T> The class you wish to store here
+ */
+public class TimedCounterList<T> extends AbstractList<T>
+{
+    private ArrayList<CounterItem<T>> m_list = new ArrayList<CounterItem<T>>();
+    private ReadWriteLock             m_lock = new ReentrantReadWriteLock();
+    
+    @Override
+    public T set( int index, T element )
+    {
+        m_lock.writeLock().lock();
+        
+        T t;
+        
+        try
+        {
+            t = m_list.set(index,new CounterItem<T>(element)).m_obj;
+        }
+        finally
+        {
+            m_lock.writeLock().unlock();
+        }
+        
+        return t;
+    }
+    
+    @Override
+    public T get( int index )
+    {
+        m_lock.readLock().lock();
+        
+        T t;
+        
+        try
+        {
+            t = m_list.get(index).m_obj;
+        }
+        finally
+        {
+            m_lock.readLock().unlock();
+        }
+        
+        return t;
+    }
+
+    @Override
+    public int size()
+    {
+        m_lock.readLock().lock();
+        int size = 0;
+
+        try
+        {
+            size = m_list.size();
+        }
+        finally
+        {
+            m_lock.readLock().unlock();
+        }
+        
+        return size;
+    }
+    
+   
+    @Override
+    public void add( int index, T element )
+    {
+        m_lock.writeLock().lock();
+        
+        try
+        {
+            m_list.add(index, new CounterItem<T>(element));
+        }
+        finally
+        {
+            m_lock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public T remove( int index )
+    {
+        m_lock.writeLock().lock();
+        T t;
+
+        try
+        {
+            t = m_list.remove( index ).m_obj;
+        }
+        finally
+        {
+            m_lock.writeLock().unlock();
+        }
+        
+        return t;
+    }
+
+    /**
+     *  Returns the count how many times this object is available in
+     *  this list, using equals().
+     *  
+     *  @param obj
+     *  @return
+     */
+    public int count( T obj )
+    {
+        int c = 0;
+        m_lock.readLock().lock();
+        
+        try
+        {
+            for( CounterItem i : m_list )
+            {
+                if( i.m_obj.equals( obj ) )
+                {
+                    c++;
+                }
+            }
+        }
+        finally
+        {
+            m_lock.readLock().unlock();
+        }
+        
+        return c;
+    }
+    
+    /**
+     *  Performs a cleanup of all items older than maxage.
+     *  
+     *  @param maxage The maximum age in milliseconds after an item is removed.
+     */
+    public void cleanup( long maxage )
+    {
+        m_lock.writeLock().lock();
+        
+        try
+        {
+            long now = System.currentTimeMillis();
+        
+            for( Iterator<CounterItem<T>> i = m_list.iterator(); i.hasNext();
)
+            {
+                CounterItem<T> ci = i.next();
+            
+                long age = now - ci.m_addTime;
+            
+                if( age > maxage )
+                {
+                    i.remove();
+                }
+            }
+        }
+        finally
+        {
+            m_lock.writeLock().unlock();
+        }
+    }
+    
+    /**
+     *  Returns the time when this particular item was added on the list.
+     *  
+     *  @param index The index of the object.
+     *  @return The addition time in milliseconds (@see System.currentTimeMillis()).
+     */
+    public long getAddTime( int index )
+    {
+        m_lock.readLock().lock();
+        long res = 0;
+        
+        try
+        {
+            res = m_list.get( index ).m_addTime;
+        }
+        finally
+        {
+            m_lock.readLock().unlock();
+        }
+        
+        return res;
+    }
+    
+    private static class CounterItem<E>
+    {
+        private E      m_obj;
+        private long   m_addTime;
+        
+        public CounterItem(E o)
+        {
+            m_addTime = System.currentTimeMillis();
+            m_obj = o;
+        }
+    }
+
+
+}

Modified: incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/util/AllTests.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/util/AllTests.java?rev=658843&r1=658842&r2=658843&view=diff
==============================================================================
--- incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/util/AllTests.java (original)
+++ incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/util/AllTests.java Wed May 21 13:17:36
2008
@@ -21,6 +21,7 @@
         suite.addTest( MailUtilTest.suite() );
         suite.addTest( PriorityListTest.suite() );
         suite.addTest( TextUtilTest.suite() );
+        suite.addTest( TimedCounterListTest.suite() );
         
         return suite;
     }

Added: incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/util/TimedCounterListTest.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/util/TimedCounterListTest.java?rev=658843&view=auto
==============================================================================
--- incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/util/TimedCounterListTest.java (added)
+++ incubator/jspwiki/trunk/tests/com/ecyrd/jspwiki/util/TimedCounterListTest.java Wed May
21 13:17:36 2008
@@ -0,0 +1,53 @@
+package com.ecyrd.jspwiki.util;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class TimedCounterListTest extends TestCase
+{
+    TimedCounterList<String> m_list = new TimedCounterList<String>();
+    
+    public void setUp()
+    {
+        m_list.add( "Foo" );
+        m_list.add( "Foo" );
+        m_list.add( "Foo" );
+        m_list.add( "Bar" );
+    }
+    
+    public void testCount()
+    {
+        assertEquals( "Foo", 3, m_list.count( "Foo" ) );
+        assertEquals( "Bar", 1, m_list.count( "Bar" ) );
+        assertEquals( "Baz", 0, m_list.count( "Baz" ) );
+    }
+    
+    public void testCleanup()
+    {
+        try
+        {
+            Thread.sleep(110);
+            
+            m_list.cleanup(100);
+            
+            assertEquals( "Foo", 0, m_list.count( "Foo" ) );
+            assertEquals( "Bar", 0, m_list.count( "Foo" ) );
+            assertEquals( "Baz", 0, m_list.count( "Foo" ) );
+            
+            assertEquals( "size", 0, m_list.size() );
+        }
+        catch( InterruptedException e )
+        {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        
+    }
+    
+    public static Test suite()
+    {
+        return new TestSuite( TimedCounterListTest.class );
+    }
+
+}



Mime
View raw message