2

I've made a modification to the following, to prevent a users username appearing in the login box.

    <div class="row clearfix">
    <label for="j_username">Username:</label>
    <input tabindex="1" type="text" name="j_username" id="j_userName" class="text" value='<c:if test="${param.login_error == 'authFailure'}">${SPRING_SECURITY_LAST_USERNAME}</c:if>' />
    <p class="forgot-password">
    <a tabindex="5" href="forgot-username-password.htm">Forgot your username or password?</a></p>
    </div>

    <input tabindex="1" type="text" name="j_username" id="j_userName" **autocomplete="off"** class="text" value='<c:if test="${param.login_error == 'authFailure'}">${SPRING_SECURITY_LAST_USERNAME}</c:if>' />

I thought this would prevent usernames being saved, do you know why Chrome and FF seem to ignore this?

cheers,

Ben

Ben Coughlan
  • 515
  • 2
  • 12
  • 23

1 Answers1

6

I think the problem lies in a higher implementation of auto-form filling that browsers are using to assist in ease-of-use. Even when specifying autocomplete="off", Google Chrome will still fill in these passwords, but in a predictable manner that we can use to break this functionality, thus achieving the functionality we're looking for.

Note: One solid reason for disabling autocomplete of passwords is for CSRF prevention as outlined here.

Consider the following:

<form action="login.asp" method="post" class="form-horizontal" name="login-form" role="form" autocomplete="off">
  <input type="hidden" name="<%=STATIC_CSRF_TOKEN_NAME%>" value="<%=AntiXssInstance.HtmlAttributeEncode(GetExternalCSRFToken())%>" />
  <input type="email" class="form-control" id="email" name="email" placeholder="Email address">

  <input type="password" class="form-control" id="password" name="password" placeholder="Password">

  <button type="submit" class="btn btn-success">Login</button>
</form>

Now compare it with this:

<form action="login.asp" method="post" class="form-horizontal" name="login-form" role="form" autocomplete="off">
  <input type="hidden" name="<%=CSRF_TOKEN_NAME%>" value="<%=GetExternalCSRFToken()%>" />
  <input type="email" class="form-control" id="email" name="email" placeholder="Email address">

  <input type="password" style="display: none;" id="password-breaker" />
  <input type="password" class="form-control" id="password" name="password" placeholder="Password">

  <button type="submit" class="btn btn-success">Login</button>
</form>

Notice how I'm doubling up on password fields, but the one that contains the value I actually want to process will be properly named. Hence, the predictable manner in which browsers like Google Chrome "override" the autocomplete="off" attribute is by searching for <input type="password" /> that comes immediately after the login ID field for which the browser has stored the user's password. This is why this attribute doesn't seem to be working in certain browsers, including Chrome.

However, by using this approach I've outlined, it essentially tricks the browser into pre-filling that #password-breaker field (which we're not going to use) with the user's password and leaves the actual #password field blank.

I haven't tested this in every possible browser and version as of yet, but I find it now to be a reasonable "cross-browser" compatibility workaround and complementary step to solely relying on the non-working autocomplete attribute.

Matt Borja
  • 1,361
  • 16
  • 35