7

Is there any problem with outputing a user's password to the HTML in an hidden field (see use case below before flaming xD)?

The use case is a registration form with two steps. The user fills in the password in the first step, but in order to avoid storing the field's values somewhere in the server, the application sends the values back to the client in the HTML as hidden fields. These fields are resubmitted on the server when the client submits the second step of the registration. This way, the server receives all the values at once and the logic is simpler.

This basically just splits the registration request into two requests where the second one is the only one that matters. The problem is I'm not too confortable with having the password sit there in the HTML, but I can't evoke a good enough reason for this to be wrong...

Edit

Just to make things a bit harder, let's assume that HTTPS is used everywhere, and X-Frame-Options is being used to prevent Clickjacking. Can anybody think of any attack that wasn't previously mentioned that works with this setup?

6 Answers 6

12

I see a few reasons why not to do that:

  • The client browser may (and probably will) cache the received HTML page into some file on the local disk. It is usually not a good idea to have passwords "as is" in files on the disk.

  • Since you store data on the client side (in the HTML), you must think about what happens when the client does not cooperate, i.e. sends back something other than what your server wrote in the HTML. Depending on your context, this may or may not be an issue, but at least some thinking must be done about that.

The generic, safe way to do this kind of thing is to send back to the client an encrypted blob, which contains the state data you want to store on the client. The blob should use both encryption and a MAC to protect against alterations from malicious clients. Where you actually put that blob is not very important; you can put it in a hidden field in the HTML, or in a cookie -- it won't really matter since, through encryption and MAC, you don't actually trust the client machine for anything.

1
  • Your second reason is not a problem in this use case. Only the data that is sent in the second request is acted upon (the first request is just reflected back as hidden fields), so even if the user changed it, he wouldn't get any farther than he would by tampering with the data in the first request. The first reason, however, applies just right, and your solution is simple enough that there is no reason not to do it! Thank you! May 3, 2013 at 18:14
4

Your use case is contextually not any different than re-displaying a simple single page form that user failed to fill in correctly, and for sake of convenience your server code re-filled the fields that user did fill in correctly. We see many such examples across the web. The only difference being, that you use two pages for a complete set of required (and optional) user data you're collecting. You could as well be using twenty of such steps, it doesn't really matter from security perspective (it might to the users tho, but that's a different matter), as long as:

  • You've taken care of TLS
  • You treat all user inputs, regardless of the page they come from, as a whole
  • You don't unnecessarily expose clear text passwords in end user cache

You mentioned in the comments that this will be on HTTPS, so the first one is taken care of. Both of the other two are completely manageable, too:

Your server side code that verifies user input will have to be written in such a way to accommodate multiple user form pages, but also check against input constraints incrementally, meaning each new page will check all the data input on previous ones as well, up until the page the user is currently on. You also only accept all these separate forms as a whole, meaning a failure on one reverts to displaying that particular end user input stage, drops all subsequent form data, and displays relevant error for that page (or stage, step, however you want to name it). The important part is, that you accept all of the user's input only once all data is verified against your requirements in all of the form entry stages. Only then you can safely write anything to your database - i.e. all requirements were met.

Why is sending back end user passwords in clear text not a problem? Because you've already sent it at least once in the other direction - to the server. If that's not supposed to be a problem, sending it back using same TLS shouldn't just as well. Shouldn't, because you'll still have to consider this data being cached at user end by the browser, as @ThomasPornin pointed out. This is however controllable with browser cache header tags, be it as HTML tags, or in the HTTP server response headers. I won't go into detail how to achieve that, as it's a well covered subject, and has many threads on StackOverflow as well. The main takeaway is, that if your front-end was already prised open by some XSS injection, nothing is stopping this injection from collecting end user data on form submit from that same input field, so sending it back doesn't really expose it any more for these types of attacks, but client cache should still be considered to prevent exposing clear text passwords to other attack types.

Others have also suggested using AJAX for parts of your form data. While true that it can help with user input, guiding users as they go with feedback, you should never accept only partial data as something to rely on later, when completing either stage of form submission, or when completing the process as a whole. Checking display name availability, or similar is fine tho, but if it's in the mean time taken by another user, you most certainly don't want to rely on that previous AJAX request to decide, if the current user still gets to use it.

2
  • 1
    Wow, thank you for the time you took to post such an extensive answer. I think you convinced me that this can be done securely as long as the right measures are taken! May 7, 2013 at 18:24
  • +1 I think you have thought it through quite well. Jun 14, 2013 at 8:51
3

If you don't want to store the password on the server, why are you doing two steps to the user creation? If you are only checking username availability, why not use AJAX postback to send back only the information that needs to be verified and do everything in one form?

Even if you do need to send it back, I don't see why it couldn't be kept on the user's session as long as the server never sends it back to the user. You could immediately do the hash that will be applied when storing it to the DB so that nothing you wouldn't otherwise be storing eventually is persisted.

3
  • The registration form is quite big, so a second page is needed where the user just confirms that he entered the data correctly. Why this is not being stored in the session, I can't give you a definite answer since I'm not the developer doing this application, but if I had to guess, I'd say it was to avoid storing the data of half a registration in the server, maybe to prevent DoS... May 3, 2013 at 18:24
  • 1
    @fvieira that makes sense, though a captcha on first page submission would also be an effective DoS prevention. You could also do a "fake" second page via AJAX by simply having it go back to the top of the page and ask the user to confirm their information. May 3, 2013 at 18:45
  • 2
    Agreed, this screams AJAX. Send the information in the form WITHOUT the password, use Javascript to hide whatever's no longer relevant to the user, and when they confirm, THEN send the final bundle of data, including password. May 3, 2013 at 22:13
3

By putting the clear-text password (or any sensitive data) inside the source code or the DOM of the page you are taking a big risk.

For example, just a simple Cross-Site Scripting (XSS) vulnerability makes it trivial to read that value and send it wherever the attacker wishes.

Furthermore, other techniques involving Clickjacking are used to read content from arbitrary iframe sources, such as the one containing your password.

Storing sensitive data that has no need to be there (ie. the user doesn't need to read it) inside the source of web pages is never a good idea.

1
  • I had already thought about XSS, but if there was one in the registration form, it would be easy for an attacker to fool users to provide their passwords anyway, having the password there just makes it easier. The Clickjacking, however, I hadn't thought about, and although we use X-Frame-Options to prevent it, it's still an interesting way of exploiting the use case I provided in the question! May 6, 2013 at 15:42
3

If, for whatever reason, the password needs to be sent back and 'just sit there' on the second why not encode it server side with a salt string ie myencode($salt, $pass); send the encoded pass back and when it's returned decode it ie mydecode($pass2); This would seem like the easiest route that would still maintain a decent level of security.

Or, a little more complicated but to quote a previous poster "this screams ajax"

2

Most definitely a problem. Transmitting the password in any clear text fashion opens up a lot of security problems.

One particularly that comes to mind, are you sure that the user's network is not being sniffed or eavesdropped? By allowing the clear text password (in general) any one that may be eavesdropping the user's network will not have full access to their password. This creates other issues because many users use the same password on multiple websites. You have just allowed for your user to become a victim through poor coding.

One a side note, why not just move the password part to the 2nd phase of the registration and be the last step? That way it is (hopefully) transmitted in an encrypted and safe manner.

4
  • 1
    Sorry, maybe I wasn't clear on what I meant by cleartext. What it means is that the password is in the HTML without being hashed/encrypted, but I didn't specify anything about the transport layer. Actually https is being used for the whole site, which is the only way to make sure things are secure. May 3, 2013 at 19:02
  • Also, the password goes into the network in the first and second request already, so having it in the network in a response does not add any vulnerability that didn't yet exist. As long as https is used in all requests, that is not where the problem lies. May 3, 2013 at 19:05
  • @fvieira Try attacking someone and you'll realize that's not quite true; each time you pass critical security information over the network, you give hackers another opportunity. Not every transmission has the same level of scrutiny by the hacker. Imagine for example a hacker that spoofs the user's IP; they won't be able to see what the user sends, but they will receive what's intended for them - sending the password back out gives such a hacker a much better shot at breaking in. Basically, imagine hackers as trying things and seeing what sticks. May 3, 2013 at 22:10
  • 1
    @Chris Yes, I know what you mean, and this sounded bad for me too since the time I heard about it. I just wanted concrete examples about what could go wrong that applied to my use case. The IP spoofing attack, for example, I think it wouldn't work since HTTPS is used and the attacker wouldn't be able to read the response anyway... But I do know that some working attack might appear that I haven't thought about yet, I just wanted to see if anyone could suggest one. May 6, 2013 at 15:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .