Understanding the Rails Authenticity Token

前端 未结 10 1007
攒了一身酷
攒了一身酷 2020-11-22 05:55

I am running into some issues regarding the Authenticity Token in Rails, as I have many times now.

But I really don\'t want to just solve this problem and go on. I w

相关标签:
10条回答
  • 2020-11-22 06:36

    Minimal attack example that would be prevented: CSRF

    On my website evil.com I convince you to submit the following form:

    <form action="http://bank.com/transfer" method="post">
      <p><input type="hidden" name="to"      value="ciro"></p>
      <p><input type="hidden" name="ammount" value="100"></p>
      <p><button type="submit">CLICK TO GET PRIZE!!!</button></p>
    </form>
    

    If you are logged into your bank through session cookies, then the cookies would be sent and the transfer would be made without you even knowing it.

    That is were the CSRF token comes into play:

    • with the GET response that that returned the form, Rails sends a very long random hidden parameter
    • when the browser makes the POST request, it will send the parameter along, and the server will only accept it if it matches

    So the form on an authentic browser would look like:

    <form action="http://bank.com/transfer" method="post">
      <p><input type="hidden" name="authenticity_token" value="j/DcoJ2VZvr7vdf8CHKsvjdlDbmiizaOb5B8DMALg6s=" ></p>
      <p><input type="hidden" name="to"                 value="ciro"></p>
      <p><input type="hidden" name="ammount"            value="100"></p>
      <p><button type="submit">Send 100$ to Ciro.</button></p>
    </form>
    

    Thus, my attack would fail, since it was not sending the authenticity_token parameter, and there is no way I could have guessed it since it is a huge random number.

    This prevention technique is called Synchronizer Token Pattern.

    Same Origin Policy

    But what if the attacker made two requests with JavaScript, one to read the token, and the second one to make the transfer?

    The synchronizer token pattern alone is not enough to prevent that!

    This is where the Same Origin Policy comes to the rescue, as I have explained at: https://security.stackexchange.com/questions/8264/why-is-the-same-origin-policy-so-important/72569#72569

    How Rails sends the tokens

    Covered at: Rails: How Does csrf_meta_tag Work?

    Basically:

    • HTML helpers like form_tag add a hidden field to the form for you if it's not a GET form

    • AJAX is dealt with automatically by jquery-ujs, which reads the token from the meta elements added to your header by csrf_meta_tags (present in the default template), and adds it to any request made.

      uJS also tries to update the token in forms in outdated cached fragments.

    Other prevention approaches

    • check if certain headers is present e.g. X-Requested-With:
      • What's the point of the X-Requested-With header?
      • https://security.stackexchange.com/questions/23371/csrf-protection-with-custom-headers-and-without-validating-token
      • Is an X-Requested-With header server check sufficient to protect against a CSRF for an ajax-driven application?
    • check the value of the Origin header: https://security.stackexchange.com/questions/91165/why-is-the-synchronizer-token-pattern-preferred-over-the-origin-header-check-to
    • re-authentication: ask user for password again. This should be done for every critical operation (bank login and money transfers, password changes in most websites), in case your site ever gets XSSed. The downside is that the user has to type the password multiple times, which is tiresome, and increases the chances of keylogging / shoulder surfing.
    0 讨论(0)
  • 2020-11-22 06:40

    What is an authentication_token ?

    This is a random string used by rails application to make sure that the user is requesting or performing an action from the app page, not from another app or site.

    Why is an authentication_token is necessary ?

    To protect your app or site from cross-site request forgery.

    How to add an authentication_token to a form ?

    If you are generating a form using form_for tag an authentication_token is automatically added else you can use <%= csrf_meta_tag %>.

    0 讨论(0)
  • 2020-11-22 06:46

    What happens

    When the user views a form to create, update, or destroy a resource, the Rails app creates a random authenticity_token, stores this token in the session, and places it in a hidden field in the form. When the user submits the form, Rails looks for the authenticity_token, compares it to the one stored in the session, and if they match the request is allowed to continue.

    Why it happens

    Since the authenticity token is stored in the session, the client cannot know its value. This prevents people from submitting forms to a Rails app without viewing the form within that app itself. Imagine that you are using service A, you logged into the service and everything is ok. Now imagine that you went to use service B, and you saw a picture you like, and pressed on the picture to view a larger size of it. Now, if some evil code was there at service B, it might send a request to service A (which you are logged into), and ask to delete your account, by sending a request to http://serviceA.com/close_account. This is what is known as CSRF (Cross Site Request Forgery).

    If service A is using authenticity tokens, this attack vector is no longer applicable, since the request from service B would not contain the correct authenticity token, and will not be allowed to continue.

    API docs describes details about meta tag:

    CSRF protection is turned on with the protect_from_forgery method, which checks the token and resets the session if it doesn't match what was expected. A call to this method is generated for new Rails applications by default. The token parameter is named authenticity_token by default. The name and value of this token must be added to every layout that renders forms by including csrf_meta_tags in the HTML head.

    Notes

    Keep in mind, Rails only verifies not idempotent methods (POST, PUT/PATCH and DELETE). GET request are not checked for authenticity token. Why? because the HTTP specification states that GET requests is idempotent and should not create, alter, or destroy resources at the server, and the request should be idempotent (if you run the same command multiple times, you should get the same result every time).

    Also the real implementation is a bit more complicated as defined in the beginning, ensuring better security. Rails does not issue the same stored token with every form. Neither does it generate and store a different token every time. It generates and stores a cryptographic hash in a session and issues new cryptographic tokens, which can be matched against the stored one, every time a page is rendered. See request_forgery_protection.rb.

    Lessons

    Use authenticity_token to protect your not idempotent methods (POST, PUT/PATCH, and DELETE). Also make sure not to allow any GET requests that could potentially modify resources on the server.


    EDIT: Check the comment by @erturne regarding GET requests being idempotent. He explains it in a better way than I have done here.

    0 讨论(0)
  • 2020-11-22 06:47

    since Authenticity Token is so important, and in Rails 3.0+ you can use

     <%= token_tag nil %>
    

    to create

    <input name="authenticity_token" type="hidden" value="token_value">
    

    anywhere

    0 讨论(0)
提交回复
热议问题