Search code examples
ruby-on-rails-4ember.jscsrf-protectionember-cli-rails

CSRF token session_store with ember-simple-auth alongside Devise


Is it possible to implement Rails csrf through cookie_store at the same while using ember-simple-auth Devise?

Guides like this one always deactivate Rails.application.config.session_store which from my understanding does not allow Rails to keep track of csrf tokens which causes Rails to lose track of sessionsg. After attempting many solutions including:

  1. require jquery_ujs on Rails manifesto.
  2. Rails.application.config.session_store :disabled.
  3. https://github.com/abuiles/rails-csrf.
  4. Changing Ember.js Adapter to append CSRF.

The end result is still pretty much the same:

Can't verify CSRF token authenticity followed by Completed 422 Unprocessable Entity if protect_from_forgery is set with :exception instead of :null_session.

Example Transaction:

Partial Request HEADER:

X-CSRF-Token:1vGIZ6MFV4kdJ0yYGFiDq54DV2RjEIaq57O05PSdNdLaqsXMzEGdQIOeSyAWG1bZ+dg7oI6I2xXaBABSOWQbrQ==

Responder HEADER

   HTTP/1.1 422 Unprocessable Entity
   Content-Type: text/plain; charset=utf-8
   X-Request-Id: 71e94632-ad98-4b3f-97fb-e274a2ec1c7e
   X-Runtime: 0.050747
   Content-Length: 74162

  The response also attaches the following:
  Session dump
  _csrf_token: "jFjdzKn/kodNnJM0DXLutMSsemidQxj7U/hrGmsD3DE="

The rails-csrf response from my csrf branch (branch has been deleted).

beforeModel() {
    return this.csrf.fetchToken();
},

Partial dump of the return statement:

_result: Object
param: "authenticity_token"
token: "1vGIZ6MFV4kdJ0yYGFiDq54DV2RjEIaq57O05PSdNdLaqsXMzEGdQIOeSyAWG1bZ+dg7oI6I2xXaBABSOWQbrQ=="

From my understanding, all of these attempted solutions have the common root: session_store is disabled...


Solution

  • Update!

    The answer below turned out to be wrong in nature after learning more about CSRF protection and Ember-Cli-Rails.


    The idea here is to have two storages: A cookie-based storage only for the csrf token that is maintained by Rails, and a localStorage maintained by Ember-simple-auth where the user authenticity token, email and id are being taken care of while a custom session SessionAccount inherits those values and validates them against the server before setting the user that will be available for the entire Ember.js.

    The validation by the SessionAccount occurs in order to detect any tampering with the localStorage. The validation occurs every time the SessionAccount queries the localStorage (e.g page reload) as it communicates with the server through a Token model (token, email and id.) The server responds with 200 or 404 through a TokenSerializer which only renders email or the validation error, thus not disclosing the frontend to see other authentication_tokens unless the user sign in through the login form which requires email and password.

    From my understanding, the weak spots in this methodology are not susceptible enough to be so easily hackable unless:

    • Someone invades the server and get the database content. Although the passwords are salted, any person who has the database dump can change the localStorage token, email and id to the person they want to impersonate and the server validation will work. However, this can be minimized by a worker that changes the authentication token for non-logged in users every 24 hours (or any other timeframe.) The code example section currently does not have the worker since I still have not learned about them.
    • Someone know the password and email of the person they want to hack... Not much I can do about that one at the moment.
    • Someone intercept the data being passed around through the JSON API. A strong SSL implementation should do a good job.
    • If your sessionAccount has something in the lines of is_Admin, then the token could be sent alongside the POST request for admin only requests in order for further backend validation since you can never trust the frontend.
    • Something else? Those are the ones I am aware of.

    Now onto the practical approach:

    1. Set Rails.application.config.session_store :csrf_cookie_store, key: '_xxxxx_id', httponly: false on the session_store.db.
    2. Create the csrf_cookie_store under lib and require it on application.rb with require 'csrf_cookie_store'.
    3. Set protect_from_forgery with: :exception on application.rb.
    4. Create a TokenController to handle Validation.
    5. TokenSerializer so that only the email is sent back.
    6. Update your Devise Session controller to change the token upon login and to skip authenticity token validation on session destroy.
    7. Check your routes for the tokens to be create only and to have the custom Devise session.
    8. Create the Ember.js Token Model
    9. Match your SessionAccount to what I created.
    10. Update your Devise Authenticator to send a delete request to the server when the session is being invalidated.
    11. Test it out.