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.
Besides, don't use GET for non-idempotent requests (those which change state). That's just webapp development 101.
Edited for clarity.
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
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?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.
<img src="http://example.com/do.php?action=delete&id=3 />
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.