GET and POST security

I came across a question on Stack Overflow asked many 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 different ways depending on the method and what you're trying to do. In general, web browsers only deal with two of the eight methods:
  • GET
  • HEAD
  • POST
  • PUT
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. Most browsers ignore the rest of the HTTP protocol suite.

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.

That's GET, what about POST?

So, to explain the above here, we're requesting the root (that one / after POST means root, which is like requesting 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 should be considered unsafe as it preforms an action, which is generally reserved for post. 

Now, what about 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 has nothing to do with using POST as security. It only says some web servers might log the page address because it's part of the URI.

Why am I contradicting the IETF?

I'm not. The wording of section 15.1.3 may be confusing to some people, which explains why some people consider it to mean something else. The sections are titled specifically, "Sensitive Information", This section doesn't say security is granted through POST, it says URIs aren't meant to contain sensitive data: they are resource links. Sensitive data is anything that might cause an action to be preformed or contains exploitable data. 

This doesn't mean "Never" include it, if you read RFC2119 you'll see clear definition for the interpretation of "should not". Saying GET is more secure than POST because URIs are suggested not to contain sensitive information assumes that somehow a web server, 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 over SSL, between my machine and the google web server:

So what have we actually secured against?
  • Man in the middle attacks
  • Eves dropping
  • 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 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 web server to do whatever you want.

Once those SSL requests get to the web server, 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
    • You haven't stopped: hackers, viruses, the web site 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.
Final thoughts and common objections

All this offers is obfuscation. Security through obfuscation presents no genuine security, code, or business advantages to your website.
"POST is less susceptible to phishing and XSS"
Not really, infact, blog posts from five years ago (perhaps earlier) give clear instructions on how to do just that using post. Forwarding a GET request to become a POST request, or vice-verse is an infinitely simple task to do that should take a novice with PHP less than 5 minutes. This claim is absurd.
"Security comes isn't an all-or-nothing game, it's a best effort"
Yes, and some times are entirely foolish to try. If you try to write code for PCI DSS, they don't care (anywhere, what so ever) what your HTTP request method was. You will flunk the requirements if you try to claim it's "more" secure because someone can't cut+paste the link from the URL bar. Yes, this will stop the lowest-level script kiddies, meanwhile, Yuri is going to sell all of your credit card numbers for $1.50 on the black market, no credit card issuer will deal with you, and your silly attitude has ruined your reputation and business. Have a nice day? This is a classic example of variable manipulation.

If a site becomes infected, as is very common, the site may simply start logging everything and reporting it back to the command-and-control.

  • Don't use GET to do anything that preforms an action (such as logging in) as it's against design. 
  • POST has it's place, and can be used as a best-practice to help your user.
  • A hacker 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.
  • Never become so deluded that you think obscurity is security.
So please, stop thinking you're protecting someone's information because "most browsers don't 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.

Labels: , , ,

Full list of archived posts