17

This is related to another question I asked. In summary, I have a special case of a URL where, when a form is POSTed to it, I can't rely on cookies for authentication or to maintain the user's session, but I somehow need to know who they are, and I need to know they're logged in!

I think I came up with a solution to my problem, but it needs fleshing out. Here's what I'm thinking. I create a hidden form field called "username", and place within it the user's username, encrypted. Then, when the form POSTs, even though I don't receive any cookies from the browser, I know they're logged in because I can decrypt the hidden form field and get the username.

The major security flaw I can see is replay attacks. How do I prevent someone from getting ahold of that encrypted string, and POSTing as that user? I know I can use SSL to make it harder to steal that string, and maybe I can rotate the encryption key on a regular basis to limit the amount of time that the string is good for, but I'd really like to find a bulletproof solution. Anybody have any ideas? Does the ASP.Net ViewState prevent replay? If so, how do they do it?

Edit: I'm hoping for a solution that doesn't require anything stored in a database. Application state would be okay, except that it won't survive an IIS restart or work at all in a web farm or garden scenario. I'm accepting Chris's answer, for now, because I'm not convinced it's even possible to secure this without a database. But if someone comes up with an answer that does not involve the database, I'll accept it!

Community
  • 1
  • 1
Josh Hinman
  • 6,745
  • 7
  • 38
  • 47

12 Answers12

15

If you hash in a time-stamp along with the user name and password, you can close the window for replay attacks to within a couple of seconds. I don't know if this meets your needs, but it is at least a partial solution.

Yes - that Jake.
  • 16,725
  • 14
  • 70
  • 96
  • I actually decided to do just this after I asked the question. Great minds think alike :) – Josh Hinman Sep 21 '08 at 02:55
  • Note that if the time-stamp/cookie is not encrypted or is otherwise spoof-able at the user-agent layer then this won't help as a hacker will just manipulate the time-stamp value. – Darrell Teague Jul 21 '14 at 18:37
  • @DarrellTeague you can use hmac to guarantee no-tampering of timestamp – scape Oct 28 '15 at 19:12
  • Preferred tamper resistance method would be to build a meta-data cookie with all fields (timestamp, other fields), hash it, then encrypt it and store the plain-text hash in another cookie. Then compare the hash to the decrypted value to ensure the cookie was not bit-fiddled. – Darrell Teague Jan 25 '16 at 21:27
14

There are several good answers here and putting them all together is where the answer ultimately lies:

  1. Block-cipher encrypt (with AES-256+) and hash (with SHA-2+) all state/nonce related information that is sent to a client. Hackers with otherwise just manipulate the data, view it to learn the patterns and circumvent everything else. Remember ... it only takes one open window.

  2. Generate a one-time random and unique nonce per request that is sent back with the POST request. This does two things: It ensures that the POST response goes with THAT request. It also allows tracking of one-time use of a given set of get/POST pairs (preventing replay).

  3. Use timestamps to make the nonce pool manageable. Store the time-stamp in an encrypted cookie per #1 above. Throw out any requests older than the maximum response time or session for the application (e.g., an hour).

  4. Store a "reasonably unique" digital fingerprint of the machine making the request with the encrypted time-stamp data. This will prevent another trick wherein the attacker steals the clients cookies to perform session-hijacking. This will ensure that the request is coming back not only once but from the machine (or close enough proximity to make it virtually impossible for the attacker to copy) the form was sent to.

There are ASPNET and Java/J2EE security filter based applications that do all of the above with zero coding. Managing the nonce pool for large systems (like a stock trading company, bank or high volume secure site) is not a trivial undertaking if performance is critical. Would recommend looking at those products versus trying to program this for each web-application.

j0k
  • 22,600
  • 28
  • 79
  • 90
Darrell Teague
  • 4,132
  • 1
  • 26
  • 38
13

If you really don't want to store any state, I think the best you can do is limit replay attacks by using timestamps and a short expiration time. For example, server sends:

{Ts, U, HMAC({Ts, U}, Ks)}

Where Ts is the timestamp, U is the username, and Ks is the server's secret key. The user sends this back to the server, and the server validates it by recomputing the HMAC on the supplied values. If it's valid, you know when it was issued, and can choose to ignore it if it's older than, say, 5 minutes.

A good resource for this type of development is The Do's and Don'ts of Client Authentication on the Web

Chris Kite
  • 385
  • 1
  • 7
  • Well, not the 'only' thing one can do (see my answer) but yes, using timestamps (with hashing and encryption of the tokens flowing back and forth so they cannot be fiddled) will make the task more manageable as expired tokens are simply ignored. All of this these days is well baking into OAUTH2, JWT et al now. – Darrell Teague Mar 20 '22 at 13:26
5

You could use some kind of random challenge string that's used along with the username to create the hash. If you store the challenge string on the server in a database you can then ensure that it's only used once, and only for one particular user.

Chris AtLee
  • 7,798
  • 3
  • 28
  • 27
2

In one of my apps to stop 'replay' attacks I have inserted IP information into my session object. Everytime I access the session object in code I make sure to pass the Request.UserHostAddress with it and then I compare to make sure the IPs match up. If they don't, then obviously someone other than the person made this request, so I return null. It's not the best solution but it is at least one more barrier to stop replay attacks.

Kelsey
  • 47,246
  • 16
  • 124
  • 162
1

The ViewState includes security functionality. See this article about some of the build-in security features in ASP.NET . It does validation against the server machineKey in the machine.config on the server, which ensures that each postback is valid.

Further down in the article, you also see that if you want to store values in your own hidden fields, you can use the LosFormatter class to encode the value in the same way that the ViewState uses for encryption.

private string EncodeText(string text) {
  StringWriter writer = new StringWriter();
  LosFormatter formatter = new LosFormatter();
  formatter.Serialize(writer, text);
  return writer.ToString();
}
awe
  • 21,938
  • 6
  • 78
  • 91
1

Use https... it has replay protection built in.

Clintm
  • 4,505
  • 3
  • 41
  • 54
1

Can you use memory or a database to maintain any information about the user or request at all?

If so, then on request for the form, I would include a hidden form field whose contents are a randomly generated number. Save this token to in application context or some sort of store (a database, flat file, etc.) when the request is rendered. When the form is submitted, check the application context or database to see if that randomly generated number is still valid (however you define valid - maybe it can expire after X minutes). If so, remove this token from the list of "allowed tokens".

Thus any replayed requests would include this same token which is no longer considered valid on the server.

matt b
  • 138,234
  • 66
  • 282
  • 345
1

I am new to some aspects of web programming but I was reading up on this the other day. I believe you need to use a Nonce.

Ethan Post
  • 3,020
  • 3
  • 27
  • 27
1

(Replay attacks can easily be all about an IP/MAC spoofing, plus you're challenged on dynamic IPs )

It is not just replay you are after here, in isolation it is meaningless. Just use SSL and avoid handcrafting anything..

ASP.Net ViewState is a mess, avoid it. While PKI is heavyweight and bloated, at least it works without inventing your own security 'schemes'. So if I could, I'd use it and always go for mutual authent. Server-only authentification is quite useless.

rama-jka toti
  • 1,404
  • 10
  • 16
0

If you only accept each key once (say, make the key a GUID, and then check when it comes back), that would prevent replays. Of course, if the attacker responds first, then you have a new problem...

Matt Bishop
  • 1,487
  • 1
  • 12
  • 19
0

Is this WebForms or MVC? If it's MVC you could utilize the AntiForgery token. This seems like it's similar to the approach you mention except it uses basically a GUID and sets a cookie with the guid value for that post. For more on that see Steve Sanderson's blog: http://blog.codeville.net/2008/09/01/prevent-cross-site-request-forgery-csrf-using-aspnet-mvcs-antiforgerytoken-helper/

Another thing, have you considered checking the referrer on the postback? This is not bulletproof but it may help.

m.s.
  • 16,063
  • 7
  • 53
  • 88
Ryan Lanciaux
  • 5,965
  • 2
  • 37
  • 49
  • The question states that he can't rely on cookies for authentication. The MVC Anti-Forgery token in fact does rely on cookies, so it is unlikely that this would work for the question's scenario. Also, we should be careful about what we mean by "replay attacks" - anti-forgery tokens are designed to prevent CSRF attacks. Replay attack can come in various flavors - including from the originating user - in which case anti-forgery token would be useless. – Nathan May 06 '09 at 22:18