The difference between GET and POST is interface-related, not security-related. A better example for why you avoid GET for non-idempotent operations is to prevent a webcrawler from coming along and innocently making massive changes to your site just by following links. Security-wise, there's little difference between the two, it's trival for the client to change the type of request. (If I can't get some guy on a forum by embedding a picture, I'll send him a link to some website I control where there's a hidden <form> that posts to the site I want.) CSRF is only preventable by putting secret tokens in your forms that get echoed in the submission request (or by double-submitting the cookie as I do, but tptacek will be in here shortly to tell me why that is so silly).
Nope, that's exactly what I came here to say. Thanks!
People conflate CSRF-ability with destructive GET operations because a lot of apps that are pervasively CSRF-able also happen to have lots of inadvertant destructive GET's, but the bad GET endpoints are a reliability flaw more than a security flaw.
There is one security difference. POST doesn't leave the data in the URL bar at the top of the browser. I sure would rather a site submit my credit card via POST than via GET.
In addition to tzs' comment above, POST parameters do not get logged by the web server either (default Apache logging, etc), unless you're using a module like mod_security to log POST data.
Besides, don't use GET for non-idempotent requests (those which change state). That's just webapp development 101.
Either I'm completely misunderstanding your grammar, or you don't understand what idempotent means... idempotent and state-changing are almost opposites.
You don't understand what idempotent means. An action is idempotent if repeating it does not change the result. For example, multiplying by zero is idempotent, 0 * x is the same as 0 * 0 * 0 * 0 * x. But of course it changes something, as 0 * x is clearly not x in the general case.
In the http case,
GET and HEAD should not change any state (be safe and idempotent), PUT and DELETE should change state but be idempotent, and POST can do whatever it wants.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
Maybe you saw the text after @marcinw changed his text? I agree with you, of course I will say that most http GET actions I see are trivially idempotent -- that is to say they are idempotent because they don't change the state at all.
No, I saw the original version that cautioned against state changing idempotent GET requests. The changed version is actually worse, as it only cautions against non-idempotent GETs, which is not enough. State changing idempotent requests via GET are quite as bad.
In fact, idempotency is irrelevant for GET, as GET requests should be safe, which trivially implies that they are idempotent. So requiring idempotency in addition to safety does nothing except making the prose harder to read.
> GET and HEAD should not change any state
> (be safe and idempotent)
> But of course it changes something, as
> 0 * x is clearly not x in the general case.
If 0 * x is idempotent, but it changes things then how can 'should not change state' be equivalent to idempotent? If I use a GET request to submit a shopping cart (for example), as long as submitting that URL a second time doesn't placee a second order for a copy of the original shopping cart (maybe it has a unique identifier so the backend can tell that it's already been submitted), then that should be idempotent, right?
Using GET to submit a shopping cart can be "idempotent", but it is not "safe" since it changes state on the server. GET should always be both safe and idempotent.
Sorry, but your "correction" made your statement even more inaccurate. You should have elided the scary latin "idempotent" and kept "state changing", as the latter is what GET requests must avoid. GET http://example.com/index?action=delete;recursive=true is idempotent, but usually not what you want to see in action on your site...
However, just to play devil's advocate, there is a class of attacks based around injecting code into logfiles since they'll log the query string of a GET request. If you can then get the server to say include that logfile as a php file, it will execute the embedded PHP.
Of course the real vuln isn't logging GET params (it's convincing the server to include the logfile as .php), but I thought it was worth pointing out.
I think that in 2011, checking referer headers can be considered an acceptable form of CSRF prevention. Some very old browsers and plugins allow referer headers to be spoofed, but by now so many other vulnerabilities have been found in all those that if you're still using them then CSRF is the least of your problems.
Indeed. I actually just finished implementing the secret token method throughout my app less than a minute ago. I have fullscreen and mobile versions... and without going into too much detail on exactly why in my particular case, it's cleaner and easier to use simple links (i.e., GET) for something like removing items from lists... so the token method is the only feasible solution to prevent something like I mentioned in my other comment (fake images and what not). But with the fullscreen version, since it's all AJAX and to make things easy on myself, I converted everything (except file uploads) to send data via POST (all links are generated by a function so instead of href it's onclick+ajax)... along with the token too of course... which was all a win-win because I lifted the data limit that GET used to impose as well.
But anyway... today I learned that even though GET and POST are no different security-wise... always use a secret token when security is an issue... and it's best to use POST because it allows MUCH more data than GET.
This bit me in the ass once and I learned my lesson.
I was working at a social networking startup and I created a Groups feature which allowed users to create their own groups with a forum and a photo gallery and a member list, news feed and so on.
Then one day a user sent us an email claiming her group was displaying erratic behavior (users randomly banned, posts randomly deleted, etc). It took us weeks to figure it out, but our ops guy eventually helped us track it down to Google Web Accelerator, which was pre-fetching URLs displayed on the page via GET (links labeled "Ban" and "Delete").
This unintentionally effected a similar "Confused Agent" exploit since the app was misusing HTTP in precisely the manor described in this post.
Google Web Accelerator has since been discontinued for precisely this reason.
The example there is one of a cross-site request forgery (CSRF or XSRF). They pose a solution for POST requests, namely, the "Anti-Forgery Token". This is the right way to stop CSRF on post forms.
However, this is a defense which works just as well for GET requests. Just put a nonce in the URL. /do.php?action=delete&id=3&nonce=88e3a6fe57854f2ed18c. Solves it just as well. (Another common solution is to duplicate the session cookie in the URL.) It is not bad form to have a get request which changes state so long as there is a nonce in the URL.
Why do people insist on hammering in the screws, and screwing in the nails?
If you want to send data from the browser back to the server, use a post. It isn't difficult!
Is the motivation behind this secretly that there is some retarded 3rd party framework in play here that doesn't support post? Something like Ruby on Rails or Django or some weird ass Java REST library???
There's no reason to bookmark the "Delete this email" link (a la SquirrelMail, RoundCube). Sometimes links make sense to use, and it's possible to safely make GET requests change state when you need to.
Edit:
Now that I think about it, why would you want to bookmark a link which modified state? The only time to use a nonce is to make it secure against CSRF attacks. You can't bookmark them when they're a post anyways, so you don't lose anything. Am I wrong somewhere?
I'm positive that rails and fairly sure that django don't subscribe to the madness of state changing get-requests. From what I've seen of Java though, all bets are off.
On the other hand, if you store it in the cookie you can't have multiple sessions running at once in different tabs. Session hijacking is only a concern if there's something worth stealing in the session, which isn't always the case. Both approaches have their advantages and disadvantages.
Why is this so? It seems like I can do a CSRF attack via any BB as just a normal forum user who can place an href in a message. How does one do that with a POST? Sorry if this is obvious.
Instead of directing them to a BB, direct them to a site you control and embed a self-submitting form via post. Probably more difficult from a social engineering perspective, but just as effective.
If the attacker can add a form and script to a page, then the attacker can arrange for the form to automatically POST using script.
If the attacker cannot add a form and script directly to the forum, then the attacker can try to trick the victim into following a link to a page where the attacker can add these elements.
Switching from GET to POST makes the attack more difficult because social engineering is required, but it does not eliminate the problem.
Yes, but the attacker can not get the browser to silently fetch the page (regardless of whether Javascript is disabled) just by sticking, say, an <img> tag somewhere.
Another way (or in addition to using tokens) is to not expose hard delete functionality to the front-end period.
One thing I've done is to create a 'recycle' table in the database with a unique ID.
Also give every table in your database a deleted column.
If something gets deleted, create a new row in the 'recycle' table and use that ID to populate the 'deleted' column of all the rows that were deleted. This way each delete associates itself to multiple rows.
This offers you the ability to "undo" any delete + all associated rows with the delete from an admin interface that is not exposed publicly to web users.
Don't use GET (<a href="api?deleteId=213">delete</a>) requests for operations that are intended to modify content. Some browser plugins follow/crawl links which could inadvertently ruin the users day ;-)
And dont forget the refeerer header that will contain any GET data. I've spoted several sites with this flaw by just looking at the data in my visit log.
This is probably an atypical type of thread for HN but I figured I'd share anyway. It's pretty obvious once you read it but I was a bit curious on the topic regarding SSL/https and sending data via GET. I'd never thought of posting an "image" (or whatever) for an admin to see that could contain harmful data in its URL.