HTTP POST vs GET: Is One More Secure For Use In REST APIs?
The use of HTTP POST vs HTTP GET for read-only (or query) operations in REST APIs recently came up in a conversation. For this particular shop, there had been a long-standing ban on the use of GET requests for use in homegrown applications. This had been the case since before REST APIs were in common use and traditional web applications (server-side generated HTML) were the standard architecture. The only problem was no one really knew or remembered why it wasn’t allowed — just that it was “insecure”. It’s unfortunate that such a standard evolved because it prevents the full syntax of HTTP to be utilized in their REST APIs.
Still, we must understand what originally motivated such a standard to to be put in place in the beginning. Exploring motivations for such a standard is the focus of this blog post. This discussion will cover many topics. Unfortunately, there isn’t time to give every topic the attention it deserves but rather summarize the important points related to the relative security of GET and POST requests.
First, let’s ask ourselves why we might want to use an GET request in a RESTful API? The whole point of an API is to be able to exchange information using the native protocol of the World Wide Web as it was meant to be used — HTTP v1.1 (or v2) for our purposes. This architectural style (philosophy) is laid out in the Roy Fielding PhD dissertation (2000). That dissertation does not specifically discuss when one HTTP method or another should be used. Nor, does it specifically mention CRUD operations. The details of when to use one method over another are left to the HTTP spec, “the language of the World Wide Web.”
So, REST API design has to draw inspiration from other sources and a little pragmatism — and avoid the religion. This includes, at least the subset of available HTTP operations that implement traditional CRUD capabilities (Create, Read, Update, Delete); this is typically POST, GET, PUT, and DELETE. Though, it doesn’t have to be this way. Maybe we replace PUT with PATCH. Maybe DELETE isn’t used and is simply an additional update that PUT/PATCH could address. In a later article, Roy Fielding clarifies several points from his original thesis including:
- that he doesn’t mention CRUD (and why)
- that REST requires that the methods be used uniformly for all resources (paths)
- that nothing in the REST architectural style requires that a specific method be used or not used.
In fact, the title of that article is “It is okay to use POST”. I thought about naming this blog post the same, but I wouldn’t want to be unoriginal. He further clarifies “ The main reason for my lack of specificity [defining how to use HTTP methods] is because the methods defined by HTTP are part of the Web’s architecture definition, not the REST architectural style. Specific method definitions (aside from the retrieval:resource duality of GET) simply don’t matter to the REST architectural style, so it is difficult to have a style discussion about them. The only thing REST requires of methods is that they be uniformly defined for all resources…”
If I adhered to all the pseudo-rules I’ve heard over the years about what can and cannot be used as part of REST APIs, there would be very little of the HTTP 1.1 spec left that could actually be used — which is kind of silly. I had someone tell me once that a true REST API can only use a 201 return code (rather than a 200) for POST calls. Well, I suppose, I’ve violated that rule many times — it’ll be okay. Hopefully, the reader recognizes this paragraph as sarcasm.
If the reader has ever seen any of my REST API design work, they will quickly recognize that I am comfortable with using GET, POST, PUT, and DELETE. This is inline with Leonard Richardson’s API Maturity model. In order to do this with REST APIs, the API designer must be comfortable with and be allowed to use available HTTP methods as appropriate.
Most of this conversation is centered around the use of GET within a web application running in a browser. There are some exceptions. We will consider both end-user browser-based apps and Business-to-Business (B2B) or system-to-system communication via REST APIs.
Relative security of HTTP POST vs. GET
I’ve been in organizations that have implemented a global, heavy-handed, ban of the use of GET requests for API-style communication or even traditional web applications. The reasons cited are usually something along the lines of:
- The data will be seen in the address bar of a browser and recorded in the browser history.
- GET is somehow less secure than POST when transmitted over the wire
- The URL (including query parameters) are logged on the server.
If HTTPS (TLS v1.2 or better) is being used, neither GET nor POST request parameters can be read over the wire. If an AJAX call is made in the background, nothing shows up in the address bar or browser history. An HTTP access log doesn’t have to record query parameters or, if you wanted to be really fancy, don’t log values that are sensitive. Or, better yet, don’t put sensitive values in HTTP query parameters.
These points are potentially valid, but less intrusive solutions can be implemented to mitigate potential problems. Implementing heavy handed policies like banning the use of GET requests in applications without performing a detailed threat assessment amounts to security theater (which means, it probably isn’t helping very much from an overall security posture).
Often these questions center around PII, HIPAA, or similar specifications. Generally, placing this type of data directly into a the query parameter should be avoided. Some type of abstraction should be introduced to the design that can be used to indirectly reference sensitive data — think of something like tokenization for PCI data.
In general, it is strongly advised to operate within the spirit of what these methods are supposed to mean. There is some gray area and room for dogma/religion to creep into it — best to avoid that. Above all else, be consistent. In the absence of any other guidelines, be consistent. Make sure that it is well understood how HTTP methods are being used in APIs that you control.
Following that CRUD strategy described above + OPTIONS method (CORS) is the strategy I have used in almost every REST API I’ve design. The exceptions to that were driven by politics and silly compromises with people that have fallen into the traps described in this blog post.
Side Effects (versus intended purpose) of HTTP GET & POST
GET requests should be used to retrieve data when designing REST APIs; POST requests should be used to create data when designing REST APIs. Creating something is a side effect — if not the point.
The HTTP GET method isn’t supposed to have side effects. It’s considered read-only for retrieving data. It is generally considered a bad practice to implement any type of write behavior (the things that will have side effects) in an HTTP GET. Though, there isn’t really anything preventing a developer from doing this.
The recommendation here is to avoid unintended or non-intuitive behavior by creating some type of “write” functionality in a GET request. Save that for the HTTP POST operations. This is a situation where using POST is recommended from the very definition of the spec over GET. This applies to traditional web applications as well as REST APIs.
HTTP Logging on Servers (and proxies)
If your application is logging sensitive values, no matter how they are passed, STOP IT. Stop it now! This includes the traditional HTTP access log that most web server technology has enabled by default. This is a bad practice and one that basic auditing and adherence to best practices should be able to mitigate.
That being said, this doesn’t mean disable all logging in production. I’ve walked into several shops that either completely disabled all production logging of web servers and application servers or disabled the HTTP access log. It is nearly impossible to do forensic analysis or after-the-fact troubleshooting performed on a system that has had all application and middleware logging disabled. The goal should be to filter out or remove to begin with the logging of parameters that are sensitive or may contain sensitive information. Depending on the product being used, this could either be very easy or nearly impossible. I would consider the capabilities needed here to be part of the technical requirements for any product being considered; though, most organizations aren’t thinking that holistically when making product selections.
If you have control over the application’s infrastructure and what is logged, then it is possible to guarantee nothing sensitive is being logged. The real test is an independent audit (usually part of some type of security review or penetration test) performed to ensure that sensitive values are not being logged.
If your application relies upon third party APIs or systems that your organization has no control, it is much more difficult to directly ensure that best practices are being followed. This can be mitigated by legal agreements, but only in the most formal situations will hands-on audits be allowed; that type of or level of relationship tends to be expensive. For an intermediary such as a proxy server that may be logging request information (URLs), these are hopefully either under the control of the HTTP client organization or HTTP server organization. In either case, the proxy server is part of the trusted infrastructure of one of those organizations. In theory, that trust extends to the other organization through some type of legal agreement. There is an assumption in the phrase “trusted infrastructure” that it is properly configured (including logging) to meet requirements.
One should also take into account how log aggregation and storage is handled (think Splunk and the like). If sensitive values do need to be logged, encrypted file systems and proper access control must be implemented. Who has access to logs? What is the log retention policy? This should all be addressed as part of an organization’s information security standards and best practices, which are applied to the application and the trusted infrastructure.
A lot of this is going to come down to trade-offs of proper auditing, operational support, assurance that sensitive information is not being logged, and if it is, that proper safeguards are in place. Recently, I was part of a conversation where the proper use of trace logging was being discussed. Ordinarily, no sensitive data is logged (info, error, debug-level logging), but in a troubleshooting situation where low-level details such as interaction with the secure credential storage platform (think CyberArk or Hashicorp Vault) need to be explored, it may be necessary to have trace logging that dumps more to logs than one would otherwise want. Following such situations, it’s time to rotate passwords, keys, certs, OAuth2 client credentials, etc.
Some products I have worked with provide direct capability to mark which fields should be logged (not logged) or filtered under specific conditions The typical IT system has many layers and many logs. Effective monitoring is just as complex as the the underlying application being monitored. You can consider proper logging, log aggregation, and log security/sanitizing a part of that monitoring apparatus.
This discussion applies to both traditional web applications and REST APIs.
This section should be its own blog post. I’m veering away from the primary topic a little, but this is important.
Proxy Caching of HTTP GET requests
The comments made in the last section about which organization owns the proxy are relevant here. If a proxy cannot be categorized as trusted, steps should be taken to avoid it.
I haven’t encountered many situations recently where a proxy had generic response caching enabled for APIs. Where I have seen response caching was on something like a CDN in front of an API Gateway or built into an API Gateway. Enabling this feature was usually done with specific intent that made sense for the API responses being cached. When used, it adheres to the HTTP directives regarding how long something should be cached and whether it should be cached.
There is also the variations of caching based on HTTP Header, query parameters, full path, etc. This can vary widely.
I haven’t seen a situation where it was desirable to cache responses on intermediary HTTP proxies for REST APIs with sensitive information. I suppose it could happen, but it would be part of that trusted infrastructure discussion in the last section.
In theory, you could cache the response of any HTTP method, but this rarely makes sense for write operations. If it was necessary to cache something, trying to implement caching of read-only API requests that wrapped in POSTs would be challenging (it could be done with certainly fully featured products, but it would lead to nonsensical situations). This would be a reason to use GET for read-only API calls rather than POST.
This section applies to traditional web applications and REST APIs. For the REST APIs, response caching doesn’t come up that often, but it does sometimes.
Exposing information in the browser (Address Bar, History, Refresh, Cache, etc)
It is true, you can see GET requests with query parameters in the address bar and Browser History. You can also see the full contents of POST, PUT, and other HTTP methods in browser Developer Tools. One could always delete the history; though,this will typically require the cooperation (or at least permission) of the end user.
Hitting the browser refresh button will replay the same HTTP GET request for requests that are in the address bar.
For REST API calls that are made as AJAX calls in the background (let’s call this HTTP requests the browser makes that are not reflected in the address bar), the call is not recorded in the browser history or shown in the address bar (at least with the major browsers). For that reason, I’m not usually to concerned about the use of a GET call with a REST API because none of these concerns apply.
If a machine/device or browser is compromised in some way, the attacker is going to be able to see the information anyway.
I don’t think anything is really lost here by using HTTP GET with REST APIs, with or without sensitive values as long as precautions are taken. The browser history can always be deleted.
This section applies to REST APIs if the API Consumer is using a browser.
HTTP requests generated by a browser will usually include a request header called Referer (the spec misspelled the word back in the day). This will contain the URL of the page that generated it. If sensitive information is contained in a GET request URL, it is possible the same URL could end up in future request Referer headers. A POST request will not have this problem. So, another point in favor of POST requests.
Exposing information over the wire
If unencrypted data is passed over a network connection, that data will be viewable by actors with access on that network. On the public internet, this means potentially everybody. This is equally true whether it is HTTP or another clear text protocol or an HTTP POST request or an HTTP GET request.
If all requests are passed over HTTPS (TLS v1.2 or better), the information will not be exposed over the wire. Now, if your corporate, outbound HTTP(S) proxy that the browser is configured to interact with (trust) is inspecting HTTPS traffic, then you effectively have a man-in-the-middle that is reading your encrypted traffic, but this is generally part of a larger compliance and trusted infrastructure setup. So, it’s probably okay for the assurances that we are seeking.
Over the years, people have stated a concern that an HTTP GET URL is exposed over the wire when HTTPS is being used. There seems to be a false impression in some industry circles that TLS/HTTPS only encrypts the message body of an HTTP request. This is NOT how TLS/SSL works. All data that makes up the HTTPS request (including method, path, query parameters, headers, etc) are encrypted on the client and decrypted on the server with AES256 (or better) encryption. That is true regardless of what method is used.
The bottom-line is that the GET and POST requests are equally confidential on the wire when using HTTPS. So, this provides no impetus to use one over the other. This applies to traditional web applications and REST APIs.
Search Engine Spiders
All search engines have a component (a spider) that will periodically scan every reachable site on the internet (adhering to some basic rules). The search spiders (Google, Bing, less, warm-fuzzy, things you don’t really want crawling your site) will follow every link on your site (all the reachable paths that use GET), but will not submit random forms they find. If a GET request has some type of side effect, this could potentially create undesirable situations. For the well-behaved search engines, a robot.txt file will limit what what can be scanned. Likewise, well-behaved search engines aren’t going to follow form POST operations. But, that doesn’t mean someone or something else won’t.
Bottom-line, there are potential issues here with GET requests that wouldn’t otherwise exist with POST requests. As long as best-practices (no side-effects in GET calls, protect sensitive endpoints with proper authentication + authorization, etc) are applied, this isn’t necessarily a problem.
This probably isn’t a problem for a REST API where there isn’t going to be a link to “click” inline in a page that can be easily discovered, but depending on your web application architecture, this may not be true.
Web accelerators are software or hardware that aim to speed up the web experience for a human user. An example would be the web accelerator functionality in NGINX. This component may run on the user’s device or be part of a proxy on the network. While the basic idea may be well-meaning, this technology is able to “click” all the links (ie, the GETs) on a page / site within the context of an authenticated user. So, if an application is using GET requests to modify (or delete), it will delete everything the web accelerator tells it to.
This is an example of a “confused deputy attack”.
Most organizations I have worked with aren’t using this type of technology. Nevertheless, it is a vote in favor of using POST requests for any form submission that results in changes to the system, which you should be doing anyway based on the “side effects” discussion earlier.
Confused Deputy Attack
Per Wikipedia, a Confused Deputy “is a legitimate, more privileged computer program that is tricked by another program into misusing its authority on the system. It is a specific type of privilege escalation.” If the browser is the deputy in this story, both GET and POST requests can be involved in a confused deputy attack. If the attacker controls a website (either by owning it originally or taking control through some type of attack), GET and POST requests are equally easy to submit without the user’s consent or action. This is a Cross Site Request Forgery (CSRF), which is a type of Confused Deputy Attack.
This is a concern for traditional web applications and REST APIs, but most likely is going to require the use of a web browser for the API Consumer.
Cross-Site Request Forgery
From OWASP, a Cross-Site Request Forgery attack “is an attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated. With a little help of social engineering (such as sending a link via email or chat), an attacker may trick the users of a web application into executing actions of the attacker’s choosing. If the victim is a normal user, a successful CSRF attack can force the user to perform state changing requests like transferring funds, changing their email address, and so forth. If the victim is an administrative account, CSRF can compromise the entire web application.”
Suppose a website allows images to be embedded as part of user input to a discussion forum, and is not under control of an attacker. If it allows images to be referenced via URLs on third-party websites, then GET requests will be used. This provides a way to create a CSRF attack by injecting an arbitrary GET request, in theory. There particular situation cannot happen with a POST request. There are, however, other approaches, that can result in a CSRF attack with a POST request.
CSRF attacks can impact POST and GET requests. It is true that it is easier to make this vulnerability happen with HTTP GETs (as noted above, for example) than with HTTP POSTs, but only slightly easier. There are mitigation strategies that can successfully mitigate this type of attack for both GET & POST requests. This is discussed briefly here and here.
This is a concern for traditional web applications and REST APIs, but most likely is going to require the use of a web browser for the API Consumer.
Cross-Site Scripting Attack
This type of vulnerability can impact both GETs and POSTs. There are mitigation strategies that can work across both GET & POST requests.
This is a concern for traditional web applications and REST APIs, but most likely is going to require the use of a web browser for the API Consumer.
Sensitive values in GET requests
Generally, the best practice would be to avoid putting sensitive values in the URLs (including query parameters). This discussed earlier in the “Relative security of HTTP POST vs. GET” Section.
Given the rich set of functionality available in HTTP-based REST APIs and the ease of working with HTTP GETs to retrieve data in a REST API, a wholesale ban seems counterproductive and unnecessary. Instead, through data classification standards, code reviews, and other techniques described in this post, avoid putting sensitive values in URL paths and query parameters. Consider how such mandates eliminate the use of HATEOAS (it makes it impossible).
This blog post covered a lot of territory. It is true there are some situations where the use of a POST request has security advantages over a GET request without taking additional precautions, but there are also situations where both are equally vulnerable to common attack vectors. It is critical that best-practices and mitigation strategies for common / known vulnerabilities be built into all web applications. Those small advantages the POST request have do not justify removing a GET request from our API design tool belt.
As long as GET requests are used appropriately with all best-practices applied, it can be used safely with REST APIs and web applications.
Чем отличаются HTTP-методы GET и POST
HTTP-методы GET и POST — самые распространённые способы отправить или получить данные с сервера. Но в разных случаях оба метода могут быть небезопасными или неудобными в использовании. В этой заметке рассмотрим, какой метод когда использовать.
GET — метод для чтения данных с сайта. Например, для доступа к указанной странице. Он говорит серверу, что клиент хочет прочитать указанный документ. На практике этот метод используется чаще всего, например, в интернет-магазинах на странице каталога. Фильтры, которые выбирает пользователь, передаются через метод GET .
POST — метод для отправки данных на сайт. Чаще всего с помощью метода POST передаются формы.
Протокол HTTP очень прост и состоит, по сути, из двух частей — заголовков и тела запроса или ответа.
Тело запроса — это информация, которую передал браузер при запросе страницы. Но тело запроса присутствует только если браузер запросил страницу методом POST . Например, если отправлена форма, то телом запроса будет содержание формы.
Пример GET-запроса. Информация передаётся прямо в заголовке.
Пример POST-запроса. Информация передаётся в теле запроса:
GET для безопасных действий, POST для опасных
Говоря совсем просто, GET-запросы лучше не использовать с приватной информацией. Вот почему:
- Они кэшируются. Это значит, что логин и пароль, переданные через GET-запрос, могут остаться в интернете навсегда, например, в веб-архиве или кэше Гугла.
- Остаются в истории браузера. Чтобы узнать, какие данные отправлялись, достаточно нажать Ctrl+H.
- Сохраняются в закладках и пересылаются. Можно не обратить внимания и опубликовать в соцсетях или отправить ссылку с приватной информацией в GET-запросе.
- Сохраняются в логах сервера. Например, нельзя отправлять данные банковских карт через GET-запрос, так как это создаёт риски для пользователей.
Таким образом, любые важные данные — логины, пароли, данные карты, персональные данные — лучше передавать с помощью метода POST . Также метод POST поддерживает тип кодирования данных multipart/form-data , что позволяет передавать файлы.
Руководство по выбору между GET и POST
К сожалению на практике встречается множество несостыковок в использовании GET вместо POST и наоборот. Оба HTTP метода могут приводить к одинаковым результатам, но их некорректное использование может привести к неожиданным и потенциально опасным последствиям.
Поэтому, что-бы быть уверенным в том, что мы делаем все правильно, я представляю Вам руководство по выбору между GET и POST.
Давайте вспомним, что в строках запросов, пара переменная/значение, передается в GET через вот такой URL запрос:
а в POST запросе она передается в теле заголовка:
Основы: GET против POST
Давайте введем новое слово в свой словарный запас, термин — идемпотентный (Вам не стоит лезть в википедию за его трактовкой: идемпотентность это свойство объекта проявляющееся в том, что повторное действие над этим объектом не изменяет его), а разделы 9.1, 9.3 и 9.5 RFC 2616 помогут нам составить первое правило GET против POST.
Правило #1: Используйте GET для безопасных действий и POST для небезопасных
RFC указывает Интернет браузерам на то, что пользователей необходимо предупреждать о повторном использовании POST запроса, потому что это действие потенциально небезопасно (к примеру оформление онлайн оплаты).
Однако, пока браузер соблюдает это требование RFC, может поясним почему POST должен использоваться для небезопасных действий, и почему мы не должны использовать POST для безопасных?
Просто примите к сведению то, что GET запросы используются чаще:
- GET запросы могут кэшироваться
- GET запросы могут оставаться в истории браузера
- GET запросы можно сохранять в своих закладках
- GET запросы можно передавать, распространять и т.д.
- GET запросы можно легко изменять
Примечание: Если Вам необходимо извлекать лучшее из обоих методов, небезопасное действие можно превратить в безопасное сделав его идемпотентным и таким образом обезопаситься от возможной проблемы многочисленных повторений запросов. Вы назначаете каждому запросу свой уникальный ID и проверяете его на сервере, был ли запрос с таким ID обработан ранее. На самом деле все небезопасные действия должны быть идемпотентными, так как пользователя не остановят никакие предупреждения.
GET против POST: копаем глубже
Правило #2: Используйте POST для операций с важной информацией
Так как в GET запросах строка запроса находится в открытом виде, мы должны заботиться о своей безопасности и о том, что пользователи будут работать с важными данными, такими как пароли или номера кредитных карт:
1. Наши пользователи могут и не догадываться об этом, передавая важные данные через URL другим лицам или когда история серфинга в их браузере может быть просмотрена другими пользователями этого компьютера (хотя это может и не сработать при работе с AJAX ориентированными сервисами).
2. Мы сами нарушаем закон о частной информации, сохраняя, к примеру, в логах своего сервера номера CV2s с кредитных карт пользователей.
Правило #3: Используйте POST для операций с большими данными
Несмотря на то, что RFC не описывает такой параметр, как длина URL, Internet Explorer упорно придерживается мнения, что максимальная длина URL не может превышать 2,048 символов, это накладывает некоторые ограничения на использование GET.
Правило #4: Используйте GET в AJAX приложениях
Когда используется XMLHttpRequest, браузеры реализуют POST как двухпроходный процесс (сперва посылают заголовок, а затем данные). Это означает, что GET запросы более отзывчивые, что так необходимо для хорошего AJAX окружения.
Хотя правила обычно существуют для убедительных причин, хорошо бы знать то, что за ними скрывается. Я сам ненавижу правила, у которых нет объяснений и надеюсь, что все вышесказанное поможет Вам уяснить правила различий GET против POST.
Выбирая между этими двумя методами необходимо иметь некоторое чутье и я думаю следующая схема поможет Вам в этом выборе:
Is either GET or POST more secure than the other?
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?
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?
29 Answers 29
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
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 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.
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.
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.
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.