6

i'm trying to implement secure CSRF protection to HTML login form, i know the best way to implement CSRF protection is storing random csrf_key in a session, but i want to add CSRF to my login & register forms... and i do not want to store many sessions for any anonymous unregistered users...

so i want to create the best secure posibble without using sessions or database, with only form hidden field /& a cookie, and after the login i will use sessions csrf protection.

my idea of secured user_storage only csrf:

csrf_token= AES(ip+useragent+timestamp+random_data, csrf_aes_site_key)

when csrf_aes_site_key is hard-coded in config file. and after every login/register i will decrypt the AES string + velidate that the ip&ua is matching the request ip&ua, and timestamp is not too match ahead, let say 5 min (if csrf_timestamp + 18000>=current_ts), and random_data is just randomness (and make sure that the same user will not get the same csrf_token if requested multiple times in the same ts)...

so ... is it secure enough, is it good solution? if not, any other suggestions to solve this dilemma? thank!

EDIT: the implementation i just created, and it's working fine, but is it good enough?

full example: https://github.com/itaiarbel/aes_based_csrf_protection

issue 1: user may take the csrf_token and submit to the form successfuly using the same token for the next 5min bug? what do i care if the user submit many times? as long as it not csrf attack...

issue 2: if the page is left open for 5min, the user will faild login, (refrash login page automaticaly every 5 min? maby change it to 1h?)

can you spot any specific security risk with this implementation? or can i assume this is a secure way of doing CSRF protection?

mins
  • 6,478
  • 12
  • 56
  • 75
itai
  • 302
  • 5
  • 15
  • `with only form hidden field /& a cookie` These are both client-side so that will not be secure by definition. – jeroen Apr 19 '15 at 18:45
  • but correct me if i'm wrong, attacker can't manipulate\view my csrf_token data, and if he can he can't create a request from different ip with this token, and it expiers in 5 min..., the only downside is that that same token can be use many times by the same user on the same form until expires... it's looks pretty secure to me... client-side storage security is effected only by the way you save it, i only use the user storage as a storage unit for my encrypted-secured-data that can't be manipulated without me noticing... – itai Apr 19 '15 at 19:17
  • Btw, why not just a random number? What is the purpose to use encryption here? – zerkms Apr 19 '15 at 20:04
  • since the data never saved on the server, only on client-side, so i will have no data to compare the random string to, no way to verify the token... if you use sessions the best way is to save a random string in a session... but if you do not want to save sessions files/database records for every page visitor, this is my work-around, just for the login/register forms... – itai Apr 19 '15 at 20:16
  • @itai there is - you compare a token from the form with the one from the cookies. – zerkms Apr 19 '15 at 20:17
  • 1
    You can probably use an HMAC (https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) instead of encryption. It will be way faster. – Neil Smithline Apr 19 '15 at 21:05
  • If you're only using one server (eg: not distributed), can't you just store CSRF random numbers in an in-memory data structure? You can just time them out after a few minutes. – Neil Smithline Apr 19 '15 at 21:08
  • this can work with many servers as you want, (assuming that all share the same time zone), but i starting to think this is an overkill... compering random string of cookie_value to hidden_field_value is good enugh for login / register form... – itai Apr 19 '15 at 21:53
  • Also see http://github.com/deceze/Kunststube-CSRFP – deceze Apr 20 '15 at 07:23
  • very nice approach – Bruno Dec 23 '18 at 17:45
  • @itai you have true "think this is an overkill ... is good enugh for login" - but when I create refreshed token there is no "hidden field" – Bruno Dec 23 '18 at 17:47

3 Answers3

8

The method with storing the CSRF token in cookie is quite widely used (AngularJS, Django) but it works a bit differently. The server sends the token in cookie, the client uses JavaScript to read the cookie and reflect the token in a HTTP header. The server should only verify the value from the HTTP header, even though the cookie will be sent automatically as well.

The actual cookie and header names are not important as soon as both JavaScript frontend and backend are aware of them.

This prevents CSRF because only JavaScript running from the authentic origin will be able to read the cookie (see detailed discussion on Wikipedia). The token can be a HMAC of the session cookie, which avoids the need to remember token state on the server side.

The main advantage is that this approach (unlike the one with token in form fields) works with single-page, JavaScript based applications where you don't generate the HTML on the server and can't really embed the CSRF token in their code.

kravietz
  • 10,667
  • 2
  • 35
  • 27
  • 1
    You write: "The token can be a HMAC of the session cookie". So you're assuming there's a session cookie. I thought the whole point of the OP's proposed solution was that there should be no server-side storage whatsoever. When a session (with a cookie) was started, this means state is being kept server-side. – zmippie May 18 '20 at 07:21
0

It will work for most clients - but will fail horribly where the client is accessing your site via load balanced proxies (the client IP you see with change). A more correct solution would use the organization data from the whois record or the ASN number, but there's an expense in looking these up - depending on the volume of traffic simply using the forst 16 bits of the (IPV4) address is probably sufficient.

You may also run into problems depending on how much of the user agent you store (Google Chrome can update itself on the fly).

symcbean
  • 47,736
  • 6
  • 59
  • 94
0

Looks highly insecure to me. Do not use this. Login forms and registration forms need full CSRF protection; using any kind of reduced protection is not acceptable.

Your setup seems easily attackable by anyone behind the same NAT (and thus sharing the IP address), which is very common in people's homes and even many workplaces. Here's an example scenario:

  1. Malicious coworker (MC) registers a new account on the website
  2. MC figures out victim's user-agent, which is quite easy to do
  3. MC fetches a CSRF token from the website while sending the victim's user-agent
  4. MC crafts a malicious website, containing:
    • A hidden login form that auto-submits on load
    • The CSRF token from above
    • A funny cat meme
  5. MC sends link to victim saying "lol, look at this"
  6. Victim enjoys the meme, but is now logged into the website without their knowledge
  7. MC convinces victim to start using the website, generating data or metadata on the logged in account
  8. MC can view all this data, since they have access to the same account

Steps 3 to 6 can easily happen within 5 minutes.

jlh
  • 4,349
  • 40
  • 45