Search code examples
javascriptwebkit

How to read a password field remembered by chrome (or any WebKit based browser) with JavaScript?


I have a log on form. Standard, user, password. I have a button, which starts AJAX request to web service which logs on a user. This is a new part built on top of our old site, which didn't use AJAX nor web services. So after the AJAX call to web service is made, the old function of submitting the form is run.

The crucial part is JavaScript must read login and password values and pass them to web service. It works in all browsers except WebKit based.

In WebKit based browsers the value of the password field I read is always empty. When I change the type of the password input to 'text' - I see my remembered password. But if I try to read its value with JavaScript - it's empty.

When you CLICK on this field - you can read it. But user will click the submit button and the site will break, because it won't be able to use web services and AJAX.

Of course you can't click it from JS. No known method works here. I even tried to intercept form's submit event - no joy. I can only read empty value.

(Good work WebKit crew! Great anti-AJAX! Let's get back to the stone age.) Does above really offends you? Feel free to further downvote. I feel free to state that change in browser engine which break compatibility with some sites without considerable benefit is a bad choice.

Anyway, I ask if anyone has found a workaround for this? (If you offended by the word "hack" - I've just changed it to workaround.)

Here's test code which shows what I'm talking about:

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
$(function(){
    $('#form1').on('submit', function(e) {
        console.dir($('#x').val());
        console.dir($('#y').val());
    });
});
</script>
</head>
<body>
    <form id="form1" action="http://www.google.com" method="post">
        <label for="x">Login:</label><input id="x" type="text">
        <label for="y">Password:</label><input id="y" type="password">
        <input type="submit" value="Submit">
    </form>
</body>
</html>

To test it run it from hard disk on Chrome with console. Do not test it from JSFiddle, because it won't work at all from there. Remember to check "Preserve log upon navigation" option. Enter "123" as "Login" and "456" as "Password". Click "Submit" button. Answer "Yes" to "...remember password..." question. You should see "123" and "456" on console. Run the test again. Do not touch remembered login and password fields. Result should be as expected "123" and "456". Run the test again. Enter $('#form1').submit() in console and press enter. The console output will be "123" and "".

So here's the challenge. Rip "456" from Chrome.

To "helpful" downvoters: I've done my homework like reading similar topics on SO and other websites, I've tried many theoretical workarounds myself. I don't ask anyone to do my job for me. I'm stuck and left a challenge for fellow coders / hackers. Of course white-hat ones :)

MAIN UPDATE:

Today I've tested my test code again. Now it works which is just unbelievable! Now the very same web-kit based browsers return "456" as non-webkit ones. And I didn't even restart my PC!

However the problem remains, because some of our clients still report problems with our logon form on Chrome. If it's a feature, not bug, why does it behave so erratic? And as a hypothetical bad guy - why am I still able to read the password when I click it or just change the field type to text from password? The only thing I can't do as a legitimate website developer is send the password to my WCF web service without requiring my user to click the password field first, which is pure nonsense. The unsafe thing is storing the sensitive password AT ALL in the first place. But the user has the choice whether to store the password or not. And I hope it will stay this way.

There must be a workaround for this and I'll find it the same way, how smart developers found workarounds to make AJAX uploads possible, and JSONP when browsers didn't support CORS yet, as this was a weird whim for some back in the days. Or you're better and find it before me :) Win-win, anyway.


Solution

  • It seems like I've accidentally found a solution while writing the example code. I just don't know why it didn't work the first time I tested it.

    Here's the code which illustrates the solution part:

    <!DOCTYPE html>
    <html>
    <head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
    <script>
    $(function(){
        $('#submit1').on('mousedown', function(e) {
            console.dir($('#x').val());
            console.dir($('#y').val());
        });
        $('#form1').on('submit', function(e) {
            console.dir($('#x').val());
            console.dir($('#y').val());
        });
    });
    </script>
    </head>
    <body>
        <form id="form1" action="http://www.google.com" method="post">
            <label for="x">Login:</label><input id="x" type="text">
            <label for="y">Password:</label><input id="y" type="password">
            <input id="submit1" type="submit" value="Submit">
        </form>
    </body>
    </html>
    

    And here's what Chrome's console says:

    >123
    >
    >123
    >456
    

    So I was actually able to read password field in submit event, but not in any preceding event. Which makes all this weird "security feature" even more pointless ;)

    FINAL SOLUTION:

    This all seems like a weird bug in WebKit. Maybe it is, maybe it isn't. The full workaround consists of 2 things. First - use submit event to start reading password field. Second - if you trigger submit event with JavaScript - don't do it immediately on mousedown event or it would fail randomly. If I applied a 500ms timeout between mousedown and submit events - it seems to work every time. I made it work also when replaced mousedown event with click event to fire submit event.

    What's more weird, when replaced mousedown with click - both reads return correct password value. But in my production code I found mousedown and submit sequence better (requiring less changes to other code).