[Esapi-user] [OWASP-ESAPI] Issues with Input validation using ESAPI

Kevin W. Wall kevin.w.wall at gmail.com
Thu Jul 29 00:29:14 EDT 2010


[Note: Moving this off obsolete owasp-esapi list to ESAPI-Users list where it
belongs.]

Pardon my rant, but this discussion has triggered one of my hot buttons!
I absolutely *hate* sites that restrict what characters you may use in
a password. It only tells me that that site doesn't know the first thing about
authentication.

>>> Ramesh Kesavanarayanan originally wrote:
>>> <...snip...>
>>> I have defined the regular expression in my esapi.properties file for
>>> this field as follows
>>>
>>> The regular expression I have configured is as follows
>>>
>>> pwdRegEx = "^[ [email protected]\\$#%^&*_+-=\\[\\]\\\\/?\\|><,;:'`~{}()]+$";

>> Craig Younkins replied:
>> <...snip...>
>> No, I think this is a legitimate issue. He doesn't want to canonicalize
>> that parameter because it will mess up the user's password.
>> <...snip...>

And finally Jeff Williams wrote:
> Ah - I missed the variable name.  Excellent point.
<...snip...>
> Any ideas?

Yeah, here's my idea (for this password specific case *only*): How about don't
validate the user's password in this manner AT ALL??? Or, if you insist:

    # Any char allowed, min size of 8 and no max size. Pick your own min
    # size, but I strongly recommend >= 8.
    pwdRegEx = ".{8,}"


In reality, you are doing a disservice to your user community if
you restrict what characters they may use in a password.
E.g., Why aren't non-printable control characters or Unicode characters
allowed? They can be generated on all modern keyboards. Maybe your user is
Hispanic and wants to use ñ in their password; who are you to decide that
they shouldn't be allowed to do that?

There is only one legitimate reason to restrict a password and that's because
it does not adhere to some security password policy, but even then, that
password policy should be designed to ensure a minimum of complexity
(or "entropy" as cryptographers would refer to it). Consider for moment
an attacker doing a dictionary attack? What's the best way to assist
the attacker? How about eliminating more than half (if you figure Unicode)
of the legal character set to make the cracker's job so much easier. Black
hats everywhere will thank you and probably buy you a beer. :)

Now, you didn't say whether this password was going to be used to
authenticate or as a candidate for a user's new password. Obviously
what you do to "validate" a password in each case is different, but in
/neither/ case do you want to restrict things by not allowing certain
characters.

If the user is authenticating, you are going to probably going to
create your own Authenticator class and configure it in your
ESAPI.properties class. (The reference implementation of Authenticator
interface provided in ESAPI--FileBasedAuthenticator--is really only a "toy"
class intended for illustrative purposes. You don't want to use it.
Trust me.)

Once you will get an instance of your Authenticator implementation class
(wired via ESAPI.Authenticator property in ESAPI.properties),
you use it to verify that the user provided the correct password and
then log them into your application.

I envision it something like this (think I got most of this right;
am partly drawing on memory rather than looking at any example or at
the actual source code, but at my age a little bit rot isn't too
unusual, so you should double check):

    ...
    String username = ESAPI.validator().getValidInput(
                                            request.getParameter("username") );
    String password = request.getParameter("password");
        // Note: Even though your password policy says passwords
        //       must have min length of N chars (say 8), this is
        //       not the place to check that!
    if ( username != null && username.length > 0 &&
         password != null && password.length > 0 )
    {
        User user = ESAPI.setAccountName( username );
        Authenticator authenticator = ESAPI.authenticator();
        if ( authenticator.verifyPassword( user, password ) ) {
            authenticator.setCurrentUser( user );
            authenticator.login();
        } else {
            // tell user "Invalid username and/or password" while
            // redirecting back to login page
            ...
        }
   } else {
        // tell user that both username & password are required
        // but don't log or display either.
        ...
   }
   ...

When you write your implementation of the Authenticator interface,
that verification should ideally not do anything with the plaintext (i.e.,
as passed in) password other than perhaps hash it. If you are rolling
your own authentication system (note: not recommended, use LDAP
instead or something from Acegi Security System for Spring), at the
most that you want to do is to first hash it with something like SHA-1
or SHA-256. Ideally, you should salt the password with a /random/ salt,
which is much preferred to using the username as the salt like is
documented in Authenticator.hashPassword(); using the username as
a salt is not nearly effective against attacks using rainbow tables
as decent length random salt is.  [Note: A good example of this is
the salted SHA-1 hash that LDAP uses.]

So your Authenticator would either hash your password (and possibly
base64- or hex-encode it) before storing it or simply pass it on,
unaltered, downstream for something else to deal with (e.g., AD, LDAP
bind, etc.). But you *damn sure* don't want to log it (I recommend
the amputation of a finger for the first such offense ;-) or display
it back to the user in an error message or otherwise, so you should
NOT need to worry about XSS or SQL / LDAP injections involving it.
(Username is OK to restrict to, say, ASCII. That doesn't weaken security.)
However, if you feel you _must_ write your own authN system, if you
store are the password in a DB, use SQL with parameterized types and/or
store a hex or base64 encoded version of the salted hash and you
won't need to worry about SQL injections either.


That leaves the other situation of checking the password for complexity.
Again, you never log the candidate password or display it back to the
user--EVER!!! So again XSS is not an issue. And if password is acceptable,
then you should store it similar to above or according to what is required
of your downstream authN service.

Checking candidate passwords for complexity is, well a complex
topic and this rant is already long enough, but I'm sure you know
the normal drill. If not, I'd suggest that you check out how
pam_cracklib.so does it.  For starters, see the pam_cracklib man
page available at http://linux.die.net/man/8/pam_cracklib and then
search for and read through the source code. (Note: If you hash passwords
as I suggest, you can't do much in the way of history other than
the comparing against previous hashes. If you do that, I recommend
you first convert the old password to all lowercase before hashing
so that users just can reuse old passwords that differ only in case.)

If you have other specific questions on what I've discussed, email
me off list. Don't want to bore others any more than I already have.
(I've been told my "lectures" make them sleepy.)

Regards,
-kevin
-- 
Kevin W. Wall
"The most likely way for the world to be destroyed, most experts agree,
is by accident. That's where we come in; we're computer professionals.
We cause accidents."        -- Nathaniel Borenstein, co-creator of MIME



More information about the Esapi-user mailing list