Announcement Announcement Module
Collapse
No announcement yet.
Passwords: secure transport and hashing Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Passwords: secure transport and hashing

    The system I'm developing requires two entry points into the back end: an admin web app (included in the WAR with the back end) and SOAP-based client access (a design requirement to allow both web-based and native client applications). I've got Axis/Tomcat configured to require HTTPS on the SOAP connections, but still don't like the idea of sending the raw password over the wire; I have the client hash it immediately, before its sent to the back end. The problem I'm running into is that the DaoAuthenticationProvider assumes the Authentication object's credentials are unhashed. This works for the admin site, which uses an Acegi/Spring login form, but breaks when used with the SOAP connections.

    Does this mean I need to have two separate authentication providers? One for the spring-based admin website (which uses Acegi's web login form) and another for the web services clients?

    Maybe a better question is, where should I be hashing the password? And maybe more importantly, since the username and password are sent from the client web-app to the backend for each request, is there a better way to protect the password than simply hashing it and communicating with HTTPS? Like add a randomly generated number to every message sent over the wire?
    Last edited by markds75; Jan 25th, 2006, 04:24 AM.

  • #2
    Add a salt

    After reading some other threads, I've realized that one thing I'll need to do is add a salt to the passwords. But I still haven't found a way to have acegi compute the hash for passwords entered in the spring-based admin website, and not compute the hash for (already hashed) passwords coming in from the web services.

    Comment


    • #3
      The best practise is HTTPS and using BASIC authentication for the web services. If you can't use HTTPS, use Digest authentication, which provides a standardised way of performing hashing which includes a salt. To use Digest, however, you will require a plaintext version of the password in a server-side database for comparison reasons. Using BASIC, on the other hand, would allow you to use any hashing model you wanted to. Have you looked at the PasswordEncoder interface which is used by DaoAuthenticationProvider?

      Comment


      • #4
        I have also used WSS4J. This is an implementation of the OASIS spec for web service security. If you're using AXIS, this is an easy choice. You simply pass the password from ACEGI to the WSS4J password callback. If it is authenticated you can simply add the user to the SecurityContextHolder. This works really well, I use it to provide AOP Method level security on my business objects. If you use the UsernameToken authentication method, it passes the username in the xml, then a nonce, and the encrypted password.

        Here is my callback handler integrated with ACEGI


        Code:
        public class ServiceAuthenticationCallbackHandler implements CallbackHandler {
        
        	/**
        	 * Generic constructor
        	 * 
        	 */
        	public ServiceAuthenticationCallbackHandler() {
        		// empty constructor
        	}
        
        	/**
        	 * Handle our incoming request chain
        	 */
        	public void handle(Callback[] callbacks) throws IOException,
        			UnsupportedCallbackException {
        
        		for (int i = 0; i < callbacks.length; i++) {
        			if (callbacks[i] instanceof WSPasswordCallback) {
        
        				WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
        
        				if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN) {
        					throw new UnsupportedCallbackException(
        							callbacks[i],
        							"The passwordType \"PasswordText\" is not supported. Please use passwordType \"PasswordDigest\"");
        				}
        
        				authenticate(MessageContext.getCurrentContext(), pc);
        			
        			} else {
        				// if we get here the config is wrong
        				throw new UnsupportedCallbackException(callbacks[i],
        						"Unrecognized Callback");
        			}
        		}
        
        	}
        
        	/**
        	 * Call out the acegi security and put the credentials into the
        	 * CurrentThread
        	 * 
        	 * @param msgContext
        	 * @param callback
        	 * @throws IOException
        	 */
        	protected void authenticate(MessageContext msgContext,
        			WSPasswordCallback callback) throws IOException {
        
        		HttpServlet servlet = (HttpServlet) msgContext
        				.getProperty(HTTPConstants.MC_HTTP_SERVLET);
        
        		ApplicationContext ctx = WebApplicationContextUtils
        				.getRequiredWebApplicationContext(servlet.getServletContext());
        
        		AuthenticationProvider provider = (AuthenticationProvider) ctx
        				.getBean("authenticationProvider");
        
        		Authentication auth = new UsernamePasswordAuthenticationToken(callback
        				.getIdentifer(), callback);
        
        		// catch a wrong username and give the caller a hint
        		try {
        			provider.authenticate(auth);
        
        		} catch (BadCredentialsException e) {
        			throw new IOException(
        					"Could not autheticate with the given username and password");
        		}
        		
        		SecurityContextHolder.getContext().setAuthentication(auth);
        
        	}
        And my authentication DAO

        Code:
        /**
         * Uses the access method to check the password against the <code>WSPasswordCallback</code>
         * @author Todd Nine
         *
         */
        public class WebServiceSecurityDao extends DaoAuthenticationProvider {
        
        	/**
        	 * Send the password to the authentication object
        	 */
        	protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        		//TODO, this currently supports only a plaintext password
        		String password = userDetails.getPassword();
        		
        		Object obj = authentication.getCredentials();
        		
        		if(!(obj instanceof WSPasswordCallback)){
        			throw new WebServiceSecurityException("Principal is not an instance of " +WSPasswordCallback.class);
        		}
        		
        		//set the callback.  We don't need to check since the callback will do this.
        		((WSPasswordCallback)obj).setPassword(password);
        		
        	}
        }

        Comment

        Working...
        X