1/22/2011

Lets end these silly ideas about HTTP "GET vs POST security."

I came across a question on Stack Overflow asked about two years back:
...between a http POST and GET, what are the differences from a security perspective? Is one inherently more secure then another? I realize that POST doesn't expose information on the URL but is there any real value in that or is it just security through obscurity? What is the best practice here?
So, this is is a great and valid question, but unfortunately I see the wrong thoughts about this propagating all over the place to the point where some persons hold the belief that GET in inherently insecure simply because variables are shown in the address bar. I decided to post an answer to that question even though it was two years old, simply because I didn't entirely agree with the accepted answer as there was simply a massive shortage of detail provided, and agreeing with things dogmatically isn't helpful: we don't know what the issues related to these requests are so we can't contextualize it, and we flatly agree with something rather than understand why.

First, lets discuss what these two methods are. 

In the HTTP protocol, you can provide multiple methods of interacting with a resource (for example, index.html could be a resource) and you specify particulars in differnt ways depending on the method and what you're trying to do. In general, web browsers only deal with two of the eight methods:
  • OPTIONS
  • GET
  • HEAD
  • POST
  • PUT
  • DELETE
  • TRACE
  • CONNECT
All of these methods do their own special job within the HTTP protocol, as specified by RFC2616 section 9, but I'm only going to talk about GET and POST, feel free to research these on your own.

When your web browser wants to load a resource, it will generally send a GET request. While you may request data through a post, that generally goes against convention, POST is intended to submit 

Now, lets pretend we have a very basic form that looks like this:


Your browser doesn't use magic to get that resource, it submits something that looks like this in raw-text to the server:






So to quickly summarize this, your GET requests puts the resource in the first line, it's asking for the root to submit a variable username=swordfish, another password=hunter2, and extra=lolcatzThe rest of the content explains to the server what host we're connecting to, what data we are accepting, what our browser is, the character set, and a bunch of information that is really outside of what we're talking about here.


Now you know what a GET request looks like on it's lowest level, what about a POST request?





So, to explain the above here, we're requesting the root (that one / after POST means root, which is like requesting example.com directly), and then we're sending the same sorts of information about who we are, and lastly we're sending the exact same information that we sent in the get request, it's just way down there.


So what's the difference between GET and POST?


A GET method is considered idempotent and safe, and if you read through the RFC I posted way up at the start, you'd know that means they're not intended to take action other than retrieval, and that the request shouldn't have side-effects. This lets web browsers request a resource without an "oops" taking place, such as deleting a hundred records. 


For example, the URL http://example.com/?deleteUserID=1337 should be considered unsafe as it preforms an action, which is generally reserved for post. 


Now, what about http://example.com/?viewUserProfileID=1337 which should let us view a profile? Well, that's fine, it didn't take an action, it returned data.


Okay, yes, RFC21616 does describe 'security concerns' in section 15.1.3, but what is explained is somewhat annoying. It basically says some web servers might log the page address because it's part of the URI. Not to get into a fight with the W3C or anything, but this is exceptionally short-sighted.


Why am I contradicting the w3c?


The way they've worded section 15.1.3 is short-sighted and assumes that somehow a webserver, programs on a server, and web-browser are designed to work the way they've said, and this means that only GET is really a threat because it's a common convention to log the entire URI in the log files.


Well, I've got news for you, 
  • My web server logs could just as easily track all the data, regardless of it being POST or GET
  • My programs on the server could easily log all the data sent in the request, regardless of being POST or GET
  • My web-browser ("user agent") doesn't have to be one of these fancy popular things, I could have written it myself to maliciously log everything, regardless of it being POST or GET
  • Regular HTTP requests that are not sent over SSL can be eavesdropped and/or modified between my machine and the web server (commonly called Man-in-the-middle attack), regardless of it being POST or GET
The only 'security' you're accomplishing here, is preventing someone from checking out your browser history, or running off with server logs from a popular web server (Apache/IIS/XAMPP) that doesn't log POST data (keep in mind, my PHP file (or other language) can very easily keep it's own log file of your POST requests).


What if I use SSL?


Here's what those above requests would look like if sent to encrypted.google.com over SSL, between my machine and the google webserver:





So what have we actually secured against?
  • Man in the middle attacks
  • Evesdropping
  • Some other more complex things that SSL takes care of
So, the entire block of data is encrypted in communication between here and there, but that doesn't prevent you from using a normal browser to bookmark https://example.com/?deleteUserID=1337 and making this request, which goes back to the concept of safe request methods in HTTP: methods with no action.

What if I wasn't using a normal browser? What if I could replay a POST request? I could just as easily delete that person. This isn't security.

If your browser isn't doing what the W3C wants it to do, it doesn't mean it's still safe because it's a GET or a POST. HTTP works regardless of your browser, you can open a telnet session and send raw-text to any webserver to do whatever you want.

Once those SSL requests get to the webserver, they're decrypted and the PHP file can just as easily log all the data you sent along (which only makes sense, it's the one that needs the decrypted data and does something with it). Once you've sent that data, your browser or virus on your computer could just as easily log all that data somewhere else.


Using SSL is a great addition to security, but thinking your even a fraction more secure using POST requests than GET is completely naive:
  • You're only protecting against what's common, not what's possible.
  • You're only preventing the very un-informed from logging into your https://email.example.com/?user=awesome&pass=hunter2
    • You haven't stopped: hackers, viruses, the website admin, your system admin, someone checking over your shoulder, the guy sitting in Starbucks running firesheep. And you won't, regardless of it being POST or GET.
Summary
  • Don't use GET to do anything that preforms an action (such as logging in) as it's against design.
  • Your girlfriend will find out what's in your inbox regardless of your login being POST.
  • Use SSL to get real security, not imaginary security.
  • Make sure you understand where the security really is.
  • Trust nobody.
So please, stop thinking you're protecting someone's information because "most browsers dont log POST." That's entirely asinine from a security perspective: you're not making it secure, you're simply making it harder for people who don't understand the technology in the first place.




More discussion:


There's a discussion on Reddit regarding this.