293
votes

When comparing an HTTP GET to an HTTP POST, what are the differences from a security perspective? Is one of the choices inherently more secure than the other? If so, why?

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? Is there ever a reason that I should prefer POST when security is a concern?

Edit:
Over HTTPS, POST data is encoded, but could URLs be sniffed by a 3rd party? Additionally, I am dealing with JSP; when using JSP or a similar framework, would it be fair to say the best practice is to avoid placing sensitive data in the POST or GET altogether and using server side code to handle sensitive information instead?

28
There is a nice blog entry about this on Jeff's blog Coding Horror: Cross-Site Request Forgeries and You.fhe
Wouldn't you use POST for most things. E.g for an API, say you needed to GET data from a DB, but before the server returns data you would have to be authenticated first? Using post you would simply pass your session ID + all the parameters you need for the request. If you used a GET req for this then your session ID could easily be found either in your browser history or somewhere in the middle.James111
I remember this discussion from before the war (99' or '00 or so) when https wasn't prevalent.David Tonhofer
@DavidTonhofer, which war are you referring to? The browser war?DeltaFlyer
@DeltaFlyer No, the Forever War on Stuff, aka GWOT. What have we done.David Tonhofer

28 Answers

214
votes

As far as security, they are inherently the same. While it is true that POST doesn't expose information via the URL, it exposes just as much information as a GET in the actual network communication between the client and server. If you need to pass information that is sensitive, your first line of defense would be to pass it using Secure HTTP.

GET or query string posts are really good for information required for either bookmarking a particular item, or for assisting in search engine optimization and indexing items.

POST is good for standard forms used to submit one time data. I wouldn't use GET for posting actual forms, unless maybe in a search form where you want to allow the user to save the query in a bookmark, or something along those lines.

435
votes

The GET request is marginally less secure than the POST request. Neither offers true "security" by itself; using POST requests will not magically make your website secure against malicious attacks by a noticeable amount. However, using GET requests can make an otherwise secure application insecure.

The mantra that you "must not use GET requests to make changes" is still very much valid, but this has little to do with malicious behaviour. Login forms are the ones most sensitive to being sent using the wrong request type.

Search spiders and web accelerators

This is the real reason you should use POST requests for changing data. Search spiders will follow every link on your website, but will not submit random forms they find.

Web accelerators are worse than search spiders, because they run on the client’s machine, and "click" all links in the context of the logged in user. Thus, an application that uses a GET request to delete stuff, even if it requires an administrator, will happily obey the orders of the (non-malicious!) web accelerator and delete everything it sees.

Confused deputy attack

A confused deputy attack (where the deputy is the browser) is possible regardless of whether you use a GET or a POST request.

On attacker-controlled websites GET and POST are equally easy to submit without user interaction.

The only scenario in which POST is slightly less susceptible is that many websites that aren’t under the attacker’s control (say, a third-party forum) allow embedding arbitrary images (allowing the attacker to inject an arbitrary GET request), but prevent all ways of injecting an arbitary POST request, whether automatic or manual.

One might argue that web accelerators are an example of confused deputy attack, but that’s just a matter of definition. If anything, a malicious attacker has no control over this, so it’s hardly an attack, even if the deputy is confused.

Proxy logs

Proxy servers are likely to log GET URLs in their entirety, without stripping the query string. POST request parameters are not normally logged. Cookies are unlikely to be logged in either case. (example)

This is a very weak argument in favour of POST. Firstly, un-encrypted traffic can be logged in its entirety; a malicious proxy already has everything it needs. Secondly, the request parameters are of limited use to an attacker: what they really need is the cookies, so if the only thing they have are proxy logs, they are unlikely to be able to attack either a GET or a POST URL.

There is one exception for login requests: these tend to contain the user’s password. Saving this in the proxy log opens up a vector of attack that is absent in the case of POST. However, login over plain HTTP is inherently insecure anyway.

Proxy cache

Caching proxies might retain GET responses, but not POST responses. Having said that, GET responses can be made non-cacheable with less effort than converting the URL to a POST handler.

HTTP "Referer"

If the user were to navigate to a third party website from the page served in response to a GET request, that third party website gets to see all the GET request parameters.

Belongs to the category of "reveals request parameters to a third party", whose severity depends on what is present in those parameters. POST requests are naturally immune to this, however to exploit the GET request a hacker would need to insert a link to their own website into the server’s response.

Browser history

This is very similar to the "proxy logs" argument: GET requests are stored in the browser history along with their parameters. The attacker can easily obtain these if they have physical access to the machine.

Browser refresh action

The browser will retry a GET request as soon as the user hits "refresh". It might do that when restoring tabs after shutdown. Any action (say, a payment) will thus be repeated without warning.

The browser will not retry a POST request without a warning.

This is a good reason to use only POST requests for changing data, but has nothing to do with malicious behaviour and, hence, security.

So what should I do?

  • Use only POST requests to change data, mainly for non-security-related reasons.
  • Use only POST requests for login forms; doing otherwise introduces attack vectors.
  • If your site performs sensitive operations, you really need someone who knows what they’re doing, because this can’t be covered in a single answer. You need to use HTTPS, HSTS, CSP, mitigate SQL injection, script injection (XSS), CSRF, and a gazillion of other things that may be specific to your platform (like the mass assignment vulnerability in various frameworks: ASP.NET MVC, Ruby on Rails, etc.). There is no single thing that will make the difference between "secure" (not exploitable) and "not secure".

Over HTTPS, POST data is encoded, but could URLs be sniffed by a 3rd party?

No, they can’t be sniffed. But the URLs will be stored in the browser history.

Would it be fair to say the best practice is to avoid possible placing sensitive data in the POST or GET altogether and using server side code to handle sensitive information instead?

Depends on how sensitive it is, or more specifically, in what way. Obviously the client will see it. Anyone with physical access to the client’s computer will see it. The client can spoof it when sending it back to you. If those matter then yes, keep the sensitive data on the server and don’t let it leave.

177
votes

You have no greater security provided because the variables are sent over HTTP POST than you have with variables sent over HTTP GET.

HTTP/1.1 provides us with a bunch of methods to send a request:

  • OPTIONS
  • GET
  • HEAD
  • POST
  • PUT
  • DELETE
  • TRACE
  • CONNECT

Lets suppose you have the following HTML document using GET:

<html>
<body>
<form action="http://example.com" method="get">
    User: <input type="text" name="username" /><br/>
    Password: <input type="password" name="password" /><br/>
    <input type="hidden" name="extra" value="lolcatz" />
    <input type="submit"/>
</form>
</body>
</html>

What does your browser ask? It asks this:

 GET /?username=swordfish&password=hunter2&extra=lolcatz HTTP/1.1
 Host: example.com
 Connection: keep-alive
 Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/ [...truncated]
 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) [...truncated]
 Accept-Encoding: gzip,deflate,sdch
 Accept-Language: en-US,en;q=0.8
 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

Now lets pretend we changed that request method to a POST:

 POST / HTTP/1.1
 Host: example.com
 Connection: keep-alive
 Content-Length: 49
 Cache-Control: max-age=0
 Origin: null
 Content-Type: application/x-www-form-urlencoded
 Accept: application/xml,application/xhtml+xml,text/ [...truncated]
 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; [...truncated]
 Accept-Encoding: gzip,deflate,sdch
 Accept-Language: en-US,en;q=0.8
 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

 username=swordfish&password=hunter2&extra=lolcatz

BOTH of these HTTP requests are:

  • Not encrypted
  • Included in both examples
  • Can be evesdroped on, and subject to MITM attacks.
  • Easily reproduced by third party, and script bots.

Many browsers do not support HTTP methods other than POST/GET.

Many browsers behaviors store the page address, but this doesn't mean you can ignore any of these other issues.

So to be specific:

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?

This is correct, because the software you're using to speak HTTP tends to store the request variables with one method but not another only prevents someone from looking at your browser history or some other naive attack from a 10 year old who thinks they understand h4x0r1ng, or scripts that check your history store. If you have a script that can check your history store, you could just as easily have one that checks your network traffic, so this entire security through obscurity is only providing obscurity to script kiddies and jealous girlfriends.

Over https, POST data is encoded, but could urls be sniffed by a 3rd party?

Here's how SSL works. Remember those two requests I sent above? Here's what they look like in SSL: (I changed the page to https://encrypted.google.com/ as example.com doesn't respond on SSL).

POST over SSL

q5XQP%RWCd2u#o/T9oiOyR2_YO?yo/3#tR_G7 2_RO8w?FoaObi)
oXpB_y?oO4q?`2o?O4G5D12Aovo?C@?/P/oOEQC5v?vai /%0Odo
QVw#6eoGXBF_o?/u0_F!_1a0A?Q b%TFyS@Or1SR/O/o/_@5o&_o
9q1/?q$7yOAXOD5sc$H`BECo1w/`4?)f!%geOOF/!/#Of_f&AEI#
yvv/wu_b5?/o d9O?VOVOFHwRO/pO/OSv_/8/9o6b0FGOH61O?ti
/i7b?!_o8u%RS/Doai%/Be/d4$0sv_%YD2_/EOAO/C?vv/%X!T?R
_o_2yoBP)orw7H_yQsXOhoVUo49itare#cA?/c)I7R?YCsg ??c'
(_!(0u)o4eIis/S8Oo8_BDueC?1uUO%ooOI_o8WaoO/ x?B?oO@&
Pw?os9Od!c?/$3bWWeIrd_?( `P_C?7_g5O(ob(go?&/ooRxR'u/
T/yO3dS&??hIOB/?/OI?$oH2_?c_?OsD//0/_s%r

GET over SSL

rV/O8ow1pc`?058/8OS_Qy/$7oSsU'qoo#vCbOO`vt?yFo_?EYif)
43`I/WOP_8oH0%3OqP_h/cBO&24?'?o_4`scooPSOVWYSV?H?pV!i
?78cU!_b5h'/b2coWD?/43Tu?153pI/9?R8!_Od"(//O_a#t8x?__
bb3D?05Dh/PrS6_/&5p@V f $)/xvxfgO'q@y&e&S0rB3D/Y_/fO?
_'woRbOV?_!yxSOdwo1G1?8d_p?4fo81VS3sAOvO/Db/br)f4fOxt
_Qs3EO/?2O/TOo_8p82FOt/hO?X_P3o"OVQO_?Ww_dr"'DxHwo//P
oEfGtt/_o)5RgoGqui&AXEq/oXv&//?%/6_?/x_OTgOEE%v (u(?/
t7DX1O8oD?fVObiooi'8)so?o??`o"FyVOByY_ Supo? /'i?Oi"4
tr'9/o_7too7q?c2Pv

(note: I converted the HEX to ASCII, some of it should obviously not be displayable)

The entire HTTP conversation is encrypted, the only visible portion of communication is on the TCP/IP layer (meaning the IP address and connection port information).

So let me make a big bold statement here. Your website is not provided greater security over one HTTP method than it is another, hackers and newbs all over the world know exactly how to do what I've just demonstrated here. If you want security, use SSL. Browsers tend to store history, it's recommended by RFC2616 9.1.1 to NOT use GET to perform an action, but to think that POST provides security is flatly wrong.

The only thing that POST is a security measure towards? Protection against your jealous ex flipping through your browser history. That's it. The rest of the world is logged into your account laughing at you.

To further demonstrate why POST isn't secure, Facebook uses POST requests all over the place, so how can software such as FireSheep exist?

Note that you may be attacked with CSRF even if you use HTTPS and your site does not contain XSS vulnerabilities. In short, this attack scenario assumes that the victim (the user of your site or service) is already logged in and has a proper cookie and then the victim's browser is requested to do something with your (supposedly secure) site. If you do not have protection against CSRF the attacker can still execute actions with the victims credentials. The attacker cannot see the server response because it will be transferred to the victim's browser but the damage is usually already done at that point.

35
votes

There is no added security.

Post data does not show up in the history and/or log files but if the data should be kept secure, you need SSL.
Otherwise, anybody sniffing the wire can read your data anyway.

29
votes

Even if POST gives no real security benefit versus GET, for login forms or any other form with relatively sensitive information, make sure you are using POST as:

  1. The information POSTed will not be saved in the user's history.
  2. The sensitive information (password, etc.) sent in the form will not be visible later on in the URL bar (by using GET, it will be visible in the history and the URL bar).

Also, GET has a theorical limit of data. POST doesn't.

For real sensitive info, make sure to use SSL (HTTPS)

19
votes

Neither one of GET and POST is inherently "more secure" than the other, just like neither one of fax and phone is "more secure" than the other. The various HTTP methods are provided so that you can choose the one which is most appropiate for the problem you're trying to solve. GET is more appropiate for idempotent queries while POST is more appropiate for "action" queries, but you can shoot yourself in the foot just as easily with any of them if you don't understand the security architecture for the application you're maintaining.

It's probably best if you read Chapter 9: Method Definitions of the HTTP/1.1 RFC to get an overall idea of what GET and POST were originally envisioned ot mean.

16
votes

The difference between GET and POST should not be viewed in terms of security, but rather in their intentions towards the server. GET should never change data on the server - at least other than in logs - but POST can create new resources.

Nice proxies won't cache POST data, but they may cache GET data from the URL, so you could say that POST is supposed to be more secure. But POST data would still be available to proxies that don't play nicely.

As mentioned in many of the answers, the only sure bet is via SSL.

But DO make sure that GET methods do not commit any changes, such as deleting database rows, etc.

7
votes

My usual methodology for choosing is something like:

  • GET for items that will be retrieved later by URL
    • E.g. Search should be GET so you can do search.php?s=XXX later on
  • POST for items that will be sent
    • This is relatively invisible comapred to GET and harder to send, but data can still be sent via cURL.
7
votes

This isn't security related but... browsers doesn't cache POST requests.

6
votes

Neither one magically confers security on a request, however GET implies some side effects that generally prevent it from being secure.

GET URLs show up in browser history and webserver logs. For this reason, they should never be used for things like login forms and credit card numbers.

However, just POSTing that data doesn't make it secure, either. For that you want SSL. Both GET and POST send data in plaintext over the wire when used over HTTP.

There are other good reasons to POST data, too - like the ability to submit unlimited amounts of data, or hide parameters from casual users.

The downside is that users can't bookmark the results of a query sent via POST. For that, you need GET.

5
votes

Consider this situation: A sloppy API accepts GET requests like:

http://www.example.com/api?apikey=abcdef123456&action=deleteCategory&id=1

In some settings, when you request this URL and if there is an error/warning regarding the request, this whole line gets logged in the log file. Worse yet: if you forget to disable error messages in the production server, this information is just displayed in plain in the browser! Now you've just given your API key away to everyone.

Unfortunately, there are real API's working this way.

I wouldn't like the idea of having some sensitive info in the logs or displaying them in the browser. POST and GET is not the same. Use each where appropriate.

3
votes
  1. SECURITY as safety of data IN TRANSIT: no difference between POST and GET.

  2. SECURITY as safety of data ON THE COMPUTER: POST is safer (no URL history)

2
votes

It is harder to alter a POST request (it requires more effort than editing the query string). Edit: In other words, it's only security by obscurity, and barely that.

2
votes

The notion of security is meaningless unless you define what it is that you want to be secure against.

If you want to be secure against stored browser history, some types of logging, and people looking at your URLs, then POST is more secure.

If you want to be secure against somebody sniffing your network activity, then there's no difference.

1
votes

Many people adopt a convention (alluded to by Ross) that GET requests only retrieve data, and do not modify any data on the server, and POST requests are used for all data modification. While one is not more inherently secure than the other, if you do follow this convention, you can apply cross-cutting security logic (e.g. only people with accounts can modify data, so unauthenticated POSTs are rejected).

1
votes

I'm not about to repeat all the other answers, but there's one aspect that I haven't yet seen mentioned - it's the story of disappearing data. I don't know where to find it, but...

Basically it's about a web application that mysteriously every few night did loose all its data and nobody knew why. Inspecting the Logs later revealed that the site was found by google or another arbitrary spider, that happily GET (read: GOT) all the links it found on the site - including the "delete this entry" and "are you sure?" links.

Actually - part of this has been mentioned. This is the story behind "don't change data on GET but only on POST". Crawlers will happily follow GET, never POST. Even robots.txt doesn't help against misbehaving crawlers.

1
votes

You should also be aware that if your sites contains link to other external sites you dont control using GET will put that data in the refeerer header on the external sites when they press the links on your site. So transfering login data through GET methods is ALWAYS a big issue. Since that might expose login credentials for easy access by just checking the logs or looking in Google analytics (or similar).

1
votes

RFC7231:

" URIs are intended to be shared, not secured, even when they identify secure resources. URIs are often shown on displays, added to templates when a page is printed, and stored in a variety of unprotected bookmark lists. It is therefore unwise to include information within a URI that is sensitive, personally identifiable, or a risk to disclose.

Authors of services ought to avoid GET-based forms for the submission of sensitive data because that data will be placed in the request-target. Many existing servers, proxies, and user agents log or display the request-target in places where it might be visible to third parties. Such services ought to use POST-based form submission instead."

This RFC clearly states that sensitive data should not be submitted using GET. Because of this remark, some implementors might not handle data obtained from the query portion of a GET request with the same care. I'm working on a protocol myself that ensures integrity of data. According to this spec I shouldn't have to guarantee integrity of the GET data (which I will because nobody adheres to these specs)

1
votes

As previously some people have said, HTTPS brings security.

However, POST is a bit more safe than GET because GET could be stored in the history.

But even more, sadly, sometimes the election of POST or GET is not up to the developer. For example a hyperlink is always send by GET (unless its transformed into a post form using javascript).

0
votes

GET is visible to anyone (even the one on your shoulder now) and is saved on cache, so is less secure of using post, btw post without some cryptographics routine is not sure, for a bit of security you've to use SSL (https)

0
votes

One reason POST is worse for security is that GET is logged by default, parameters and all data is almost universally logged by your webserver.

POST is the opposite, it's almost universally not logged, leading to very difficult to spot attacker activity.

I don't buy the argument "it's too big", that's no reason to not log anything, at least 1KB, would go a long way for people to identify attackers working away at a weak entry-point until it pop's, then POST does a double dis-service, by enabling any HTTP based back-door to silently pass unlimited amounts of data.

0
votes

The difference is that GET sends data open and POST hidden (in the http-header).

So get is better for non-secure data, like query strings in Google. Auth-data shall never be send via GET - so use POST here. Of course the whole theme is a little more complicated. If you want to read more, read this article (in German).

0
votes

Recently an attack was published, that allows man in a middle to reveal request body of compressed HTTPS requests. Because request headers and URL are not compressed by HTTP, GET requests are better secured against this particular attack.

There are modes in which GET requests are also vulnerable, SPDY compresses request headers, TLS also provides an optional (rarely used) compression. In these scenarios the attack is easier to prevent (browser vendors already provided fixes). HTTP level compression is a more fundamental feature, it is unlikely that vendors will disable it.

It is just an example that shows a scenario in which GET is more secure than POST, but I don't think it would be a good idea to choose GET over POST from this attack reason. The attack is quite sophisticated and requires non-trivial prerequisites (Attacker needs to be able to control part of the request content). It is better to disable HTTP compression in scenarios where the attack would be harmful.

0
votes

Disclaimer: Following points are only applicable for API calls and not the website URLs.

Security over the network: You must use HTTPS. GET and POST are equally safe in this case.

Browser History: For front-end applications like, Angular JS, React JS etc API calls are AJAX calls made by front-end. These does not become part of browser history. Hence, POST and GET are equally secure.

Server side logs: With using write set of data-masking and access logs format it is possible to hide all or only sensitive data from request-URL.

Data visibility in browser console: For someone with mallicious intent, it's almost the same efforts to view POST data as much as GET.

Hence, with right logging practices, GET API is as secure as POST API. Following POST everywhere, forces poor API definitions and should be avoided.

0
votes

There are no security unless https is used - and with https, the transfer security is the same between GET and POST.

But one important aspect is the difference for client and server when it comes to remembering requests. This is very important to remember when considering GET or POST for a login.

With POST, the password is processed by the server application and then throw away, since a good design would not store passwords - only cryptographically secure hashes - in the database.

But with GET, the server log would end up containing the requests complete with the query parameters. So however good the password hashes in the database are, the server log would still contain passwords in clear text. And unless the file system is encrypted, the server disk would contain these passwords even after the log files have been erased.

The same problem happens when using access tokens as query parameters. And this is also a reason why it is meaningful to consider supplying any access token in the HTTP header data - such as by using a bearer token in the Authorization header.

The server logs are often the weakest part of a web service, allowing an attacker to elevate their access rights from leaked information.

-3
votes

This is an old post, but I'd like to object to some of the answers. If you're transferring sensitive data, you'll want to be using SSL. If you use SSL with a GET parameter (e.g. ?userid=123), that data will be sent in plain text! If you send using a POST, the values get put in the encrypted body of the message, and therefore are not readable to most MITM attacks.

The big distinction is where the data is passed. It only makes sense that if the data is placed in a URL, it CAN'T be encrypted otherwise you wouldn't be able to route to the server because only you could read the URL. That's how a GET works.

In short, you can securely transmit data in a POST over SSL, but you cannot do so with a GET, using SSL or not.

-3
votes

Even POST accepts GET requests. Assume you have a form having inputs like user.name and user.passwd, those are supposed to support user name and password. If we simply add a ?user.name="my user&user.passwd="my password", then request will be accepted by "bypassing the logon page".

A solution for this is to implement filters (java filters as an e) on server side and detect no string queries are passed as GET arguments.

-5
votes

Post is the most secured along with SSL installed because its transmitted in the message body.

But all of these methods are insecure because the 7 bit protocol it uses underneath it is hack-able with escapement. Even through a level 4 web application firewall.

Sockets are no guarantee either... Even though its more secure in certain ways.