I was reading this stackoverflow comment which describes how rails authenticity tokens work: Understanding the Rails Authenticity Token
and the highest rated response begins with this:
"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."
This makes sense to me as an abstract concept but I wanted to understand how this works in a concrete sense, I wanted to see exactly how this happens so I am crystal clear about how this works, so I created a new rails app and scaffolded a User with just one field name then I dropped a binding.pry in Users#create right up top inside the action, which should happen directly after the user submits the form. The pry session began right after I added a new user name, hit submit and it moves to create...So I inspected the source of my application in my web browser to find that the csrf-token content value in my rails generated csrf meta tags do not match the hidden authenticity token value within my form and neither one matches the value I get if, during the same pry session, I examine the session.to_hash property and inspect the "_csrf_token" value.
I then tried setting up a pry in the Users#new action and the Users#create action and noted the "_csrf_token" value on the session and compared it to the values of the form fields and meta tags once I quit and my app moved to the pry in the Users#create action but nothing matched. It seems like none of these three values match at all.
Yet protect_from_forgery with: :exception is set in my application controller and from what I read in the top rated response I was expecting to see matching values...somewhere. It seems like nothing matches. So I currently have no concept of what rails is matching what to in order to allow for the form to proceed and the data to be saved.
The author of the top rated response also says that an authenticity_token is stored on the session but all I see is a _csrf_token (are these the same?) and a session_id and, as I said, they don't match anything. I see no match whatsoever.
If rails is matching something to the value in my form field, it doesn't seem to be the value of the '_csrf_token' unless its converting it to something else behind the scenes and then matching that value to the value in the hidden form field or something. I don't feel like I understand what is going on.
Rails is built on top of the HTTP protocol.
HTTP is stateless, which means that each request has to be treated as unique (all the supporting data has to be built each time).
To do this, Rails has the session, which is a series of "cookies" stored on the browser's system:
HTTP is a stateless protocol. Sessions make it stateful.
These sessions are used to keep small snippets of data which are used by Rails to rebuild your user's "environment" with each request. Devise stores user_id in the session, and Rails keeps a ton of other data in there, too.
--
So as opposed to - for example - a game, where you have a continual flow of data via a stateful protocol such as TCP (once the connection is established, it stays established), Rails basically reconnects with each new request.
Part of this process is form submission.
To prevent problems arising from this (IE bots/spammers sending millions of requests), Rails uses CSRF to "match" the authenticity token in the user's session with the one displayed on the web page.
This basically says that the user submitting the form is the one who originally made the request, not some bot which got the from through a proxy or some shady software.
You end up with the following:
Related
I feel like I've seen this in many Rails apps, and it never made sense to me (but maybe that's because it's well past midnight and my brain is mush).
When I edit (for example) a user at /admin/users/20/edit, and I get a validation error, and the controller code is something like this:
def update
if #user.update(user_params)
redirect_to(some_path, notice: 'Record updated')
else
render("edit") # <<<<<<<<<<<<<<<<<<<
end
end
instead of going to /admin/users/20/edit, it shows /admin/users/20 in the browser.
This seems all well and good, but without /edit it is not a valid GET URI, so other code which is consuming the HTTP_REFERER, and which (naturally) expects it to be a valid GET URI will take the user to an error page.
In my case there is an internal gem which handles impersonation of users by admin users. Ending an impersonation takes the admin user back to the referrer, and if they have had the referer modified by a validation error, then they get an error.
I could
modify the gem to handle this case (a hassle, but perhaps necessary)
add a route to make this URL valid for editing without /edit (seems like it shouldn't be necessary, and seems a bit kludgey),
but I want to know if there is a reason this is happening. Is this in fact standard Rails behavior or have I overlooked something? If this is standard, is there a good widely-accepted fix? If it is not standard Rails behavior, where should I look for the culprit?
It's pretty normal behaviour because when you update a user you will do a PUTor PATCH to /admin/users/20. So if there is a validation error, you are rendering the edit template and the url stays the same (/admin/users/20)
You could do a redirect instead of render, but in that case you are losing some info about the validation error. Or you should send it a long with the redirect.
This is a common Rails beginner hangup and the key here is really understanding HTTP verbs, the concept of idempotency and Rails flavored REST.
The /new and /edit actions in Rails respond to GET requests. They are idempotent - the page will look the same to any visitor and if you reload the page you'll get the exact same page. They only serve to display a form in classical applications.
Updating resources done with PATCH /users/1 (or PUT in legacy Rails apps). These verbs are non-idempotent as they modify a resource. Unlike many other frameworks Rails uses the HTTP method to destinguish between different actions instead of using for example POST /users/1/update or POST /users/1/delete.
What you're doing when you call render("edit") is not redirecting the user back to the form. You're rendering a view and displaying the result of performing a non-idempotent action. This is not something that can be linked to as the result depends on the input passed in the request body and neither can you reload the page without resending the exact same request - and in this case sending the result again is not guarenteed to give the same result. Some browsers do not allow this at all and almost all will warn you.
This seems all well and good, but without /edit it is not a valid GET URI, so other code which is consuming the HTTP_REFERER, and which (naturally) expects it to be a valid GET URI will take the user to an error page.
This is an X & Y problem. The result of editing a record is not idempotent and thus cannot be linked to. Using HTTP_REFERER is in itself also problematic as its not guarenteed to be sent by the client.
While you can create a scheme to redirect back with the user input stuffed into the query string or the session this is the wrong answer to the wrong question.
where should I look for the culprit?
Whatever gem you're using might not be a good solution for the original problem - or even good at all. Impersonating a user might be a lot more fuss then just creating a separate endpoint for admins to edit users directly.
It certainly sounds like a very brittle solution.
To generalize this question I asked this morning, and please accept my apologies if this has been asked before and I simply don't know what to search for, but I'm curious how Rails handles the following situation:
Using Devise, I log in a user, with an ID of 2.
I click on a link that has been created to "edit my profile" (which simply would go to the /users/2/edit page).
Using Firebug (or something similar), I modify the form and change the action from action='/users/2' to action='/users/5'.
I change an element on the form, and click submit.
At this point, Rails appears to allow the submission and update user with ID 5 with my changes.
I'm guessing I'm not the first one to ask this question. It seems to me like Rails should handle this "out of the box", but I could be wrong. Does Rails handle this natively and I'm just missing something? Has this been asked before on SO or somewhere else that I'm missing?
A few things:
Don't create a route that accepts a DB id. Instead, make it something like /my_profile.
If an id is passed in the params, ignore it entirely in the controller. Instead lookup the current_user that is logged in and show them their own profile regardless of what profile/user id is passed in.
Finally, and possibly most important, use authorization (what a user is allowed to do) in order to disallow one user from editing another user's profile. Not to be confused with authentication (user logins/logouts).
With this approach it won't matter if the DOM is changed, because the server should never implicitly trust what is passed to it, which is the problem you're facing now. Any web/app server must always confirm that the parameters being passed to it are actually valid in the context of what the current user is allowed to do.
This idea that the server should never trust what's passed to it is a critical idea to apply to every single action in your app, without exception.
How do miscellaneous account management pages fit into a RESTful design in Rails 3?
For example, a user registers (create action) and is then forwarded to a registration success page (? action) where they are asked to now verify their email address via a url with a token (emailed to them).
When they click the link in the email, technically they are "updating" their account as part of the verification process right? So I'm thinking that would somehow map to the "update" action but the update action is expecting a PUT request. Is that correct? How do you make that work via the email?
I'm also wondering how forgot password, reset password, etc also fit into a RESTful design? Just trying to wrap my head around this.
Just because you have a result design, doesn't mean you HAVE to restrict yourself to only CRUD verbs that map 1:1 to Get/Post/Put/Delete. That said, if you want to get really RESTful, you can start to think of some of these things in terms of being their own resources. For example user verification:
User signs up, and gets sent a verification email, you already have that all squared away RESTfully it looks like
Verification url looks like: http://app.com/user_verifications/new?token=foobar (GET)
They follow the url and maybe are presented with a "Hello Dan, welcome back! Click here to verify your account" at that point you submit a form to http://app.com/user_verifications to trigger the create action there. Now on the backend, you can perform whatever actions you want, updating the user, setting them to active, or actually creating a "UserVerification" model.
Not a perfect example, but the idea is that the RESTful interface you are providing has an additional resource, in this case "user_verifications" and a user is acting upon it via HTTP methods in order to achieve the user's goals. You can apply similar logic to reset/forgot password either with a "UserSession" type resource or even as specific as a specific "ForgotPassword" resource.
Success page is just create.html.erb file. Usually you are redirecting from create action, but here you can just render success template.
Verifying. If you want to stay REST you should add one more step: GET verify, where is the form with your token present, which will lead to PUT update action. User recieves a link to this page.
But I prefer to use simple GET request here, which will update information without any additional clicks.
The same way you work with restoring passwords and other functionality. You add a page to with form that gets email, then you send a letter with link to a page with form filled with tokens and so on.
I have a Symfony app that populates the "widgets" of a portal application and I'm noticing something (that seems) odd. The portal app has iframes that make calls to the Symfony app. On each of those calls, a random user key is passed on the query string. The Symfony app stores that key its session using myUser->setAttribute(). If the incoming value is different from what it has in session, it overwrites the session value.
In pseudo-code (and applying a synchronous nature for clarity even though it may not exist):
# Widget request arrives with ?foo=bar
if the user attribute 'foo' does not equal 'bar'
overwrite the user attribute 'foo' with 'bar'
end
What I'm noticing is that, on a portal page with multiple widgets (read: multiple requests coming in more or less simultaneously) where the value needs to be overwritten, each request is trying to overwrite. Is this a timing problem? When I look at the log prints, I'd expect the first request that arrives to overwrite and subsequent requests to see that the user attribute they received matches what was just put into cache by the initial request.
In this scenario, it could be that subsequent requests begin (and are checked) even before the first one--the one that should overwrite the cached value--has completely finished. Are session values not really available to subsequent requests until one request has completed entirely or could there be something else that I'm missing?
Thanks.
Attributes of the user do not get written to storage until the end of the request (in sfUser::shutdown). Attributes get loaded into sfUser at the beginning of a request. So in this case, the second request would have to be initiated after the first request is finished. Your best options are probably
Add hardRead and hardWrite methods to sfUser (look at what sfUser::initialize and sfUser::shutdown do respectively).
Use another method of storing the information that has better support for concurrency. The database or potentially the caching system you're using could work. For example, I think this could be done using APC cache.
Note that depending on what class you're using for storage, user attributes may not get written to $_SESSION at all. Symfony supports using many methods for storing user attributes (e.g. database, cache).
A common web problem is where a user clicks the submit button of a form multiple times so the server processes the form more than once. This can also happen when a user hits the back button having submitted a form and so it gets processed again.
What is the best way of stopping this from happening in ASP.NET MVC?
Possibilities as I see it are:
Disable the button after submit - this gets round the multiple clicks but not the navigation
Have the receiving action redirect immediately - browsers seem to leave these redirects out of the history
Place a unique token in the session and on the form - if they match process the form - if not clear the form for a fresh submit
Are there more?
Are there some specific implementations of any of these?
I can see the third option being implemented as an ActionFilter with a HtmlHelper extension in a similar manner to the anti-forgery stuff.
Looking forward to hearing from you MVC'ers out there.
Often people overlook the most conventional way to handle this which is to use nonce keys.
You can use PRG as others have mentioned but the downside with PRG is that it doesn't solve the double-click problem, it requires an extra trip to the server for the redirect, and since the last step is a GET request you don't have direct access to the data that was just posted (though it could be passed as a query param or maintained on the server side).
I like the Javascript solution because it works most of the time.
Nonce keys however, work all the time. The nonce key is a random unique GUID generated by the server (also saved in the database) and embedded in the form. When the user POSTs the form, the nonce key also gets posted. As soon as a POST comes in to the server, the server verifies the nonce key exists in its database. If it does, the server deletes the key from the database and processes the form. Consequently if the user POSTs twice, the second POST won't be processed because the nonce key was deleted after processing the first POST.
The nonce key has an added advantage in that it brings additional security by preventing replay attacks (a man in the middle sniffs your HTTP request and then replays it to the server which treats it as a legitimate).
You should always return a redirect as the HTTP response to a POST. This will prevent the POST from occuring again when the user navigates back and forth with the Forward/Back buttons in the browser.
If you are worried about users double-clicking your submit buttons, just have a small script disable them immediately on submit.
You might want to look at the Post-Redirect-Get (PRG) pattern:
This really isn't MVC specific, but the pattern we follow on our web pages is that actions are performed with AJAX calls, rather than full page POSTs. So navigating to a url never performs an action, just displays the form. The AJAX call won't be in the history
Along with the disabling of buttons, you can add a transparent div over the entire web page so that clicking does nothing. We do this at my work and add a little friendly label saying processing request..
The most elegant solution I found was to use ActionFilters:
Blog post is here