为什么GET请求不更改服务器上的数据?


109

在互联网上,我看到以下建议:

GET永远不要更改服务器上的数据-为此使用POST请求

这个想法的基础是什么?

如果我做一个将数据插入数据库的php服务,并在GET查询字符串中传递参数,那为什么会出错呢?(我使用准备好的语句来处理SQL注入)。POST请求以某种方式更安全吗?

还是有一些历史原因?如果是这样,那么今天的建议有多有效?




感谢您提出这个问题,也感谢@Oded的口头表达,我一直需要参考,以使提出此问题的人们对:)
Benjamin Gruenbaum 2013年

另请参阅HTTP PUT - stackoverflow.com/questions/630453/put-vs-post-in-rest(约是幂等笔记)
Bratch

2
@JoachimSauer尽管GET会将它们从爬网程序中保存下来,但根本的问题是缺少身份验证。任何脚本小子也可以将其过时。
CodesInChaos

Answers:


185

这不是建议。

GET以这种方式在所定义的HTTP协议。它应该是幂等安全的

至于为什么- GET可以缓存并在浏览器中刷新。一遍一遍又一遍。

这意味着,如果你犯同样GET再次,你会插入到你的数据库再次

考虑一下,如果GET成为链接并且被搜索引擎抓取,这意味着什么。您的数据库中将充满重复数据。

我还建议阅读URI,可寻址性以及HTTP GET和POST的用法


在某些浏览器中,链接预取也存在问题-即使页面作者未指明,它们也会调用预取链接。

例如,如果您的注销位于站点上每个页面所链接的“ GET”后面,则仅由于此行为而使人们可以注销。


35
许多很多工具,实用程序,Web爬网程序和其他东西都认为这GET永远不会是破坏性的行为(正确的是,因为它是通过这种方式指定的)。如果现在通过破坏该规范破坏应用程序,则将保留应用程序的两个部分。
Joachim Sauer

7
@NimChimpsky:确实会被更改GET。该建议是错误的。安全意味着用户不能对副作用负责,也不意味着不能有副作用。否则,您将没有服务器的日志文件,这很荒谬!RFC2616的9.1.1节非常清楚地阐明了这一点。
约尔格W¯¯米塔格

8
@JörgWMittag:我不会说“完全错误”,我会说“措词不完美”。GET不应有任何改变,这是它的目标。当然,您可以计数,记录和观察GET请求。但它不应修改您的实际业务数据。
Joachim Sauer

23
@NimChimpsky A GET不应更改所请求的资源GET,但这并不意味着“服务器上的任何内容都不应更改”。当然,在任何请求期间,诸如日志,计数器和其他服务器状态之类的内容都可能发生变化。
埃里克·金

8
几年前,谷歌发布了一个浏览器插件(iirc),该插件可以通过链接预取页面。这也发生在某些设计不佳的控制面板上-URL将导致记录或某些内容在服务器上被写入甚至删除(请考虑post?action = delete)。这导致在用户不知道的情况下执行动作。Google终止了该插件iirc的使用,即使这是Web应用程序制造商使用GET更改状态的错误。
克苏鲁2013年

24

每个HTTP动词都有自己的责任。例如GET,由RFC定义

表示检索Request-URI标识的任何信息(以实体形式)。

POST另一方面,表示插入或更正式地

POST方法用于请求源服务器接受
请求中包含的实体作为
请求行中Request-URI标识的资源的新下属

保持这种方式的原因:

  • 自1991年以来,它非常简单,并且可以在全球互联网范围内使用
  • 坚持单一责任原则
  • 其他各方GET用作信息检索和数据挖掘的手段
  • 假定GET是安全的操作,不会修改资源状态
  • 安全考虑,GET实际上是read,而POST实际上是write
  • GET由浏览器,网络中的节点,Internet服务提供商缓存
  • 除非内容更改,否则GET相同的URL必须向所有用户返回相同的结果,否则您将完全不信任返回的结果

为了完整性,只是为了强制正确使用(来源)

  • GET参数作为URL的一部分传递,该URL的长度很小且有限,默认情况下为256个字符,某些服务器支持4000多个字符。如果要插入长记录,则没有合法的方法将数据传递到
  • 当使用安全连接,̶如TLS,̶网址没有得到加密,从而̶所有参数̶ ̶G̶E̶T̶̶转移纯文本。URL实际上是使用TLS加密的,所以TLS很好。
  • 使用插入二进制数据或非ASCII字符GET是不切实际的
  • GET 如果用户在浏览器中按“后退”按钮,将重新执行
  • 某些较旧的抓取工具可能不会索引带有?符号的URL

1
您确定URL未通过TLS加密吗?我的印象是SSL / TLS握手发生在传输HTTP标头之前。这就是为什么很难通过单个IP地址虚拟托管HTTPS站点的原因。我错了吗?
布兰登

这是正确的,我固定它
奥莱克西

2
@Brandon Modern浏览器在TLS握手过程中以明文形式发送主机域(称为服务器名称指示),以允许每个IP地址托管多个域。网址的路径/查询部分受TLS保护。在这方面,GET和其他HTTP动词之间没有区别。
CodesInChaos

9

编辑:之前,我说过POST可以帮助您防御CSRF,但这是错误的。我不认为这是正确的。您必须在所有更改数据的请求中要求会话范围的唯一隐藏令牌,以防止CSRF。

在互联网的早期,有浏览器加速器。这些程序将开始单击页面上的链接以缓存内容。Google Web Accelerator就是这些程序之一。这可能会对单击链接时进行更改的应用程序造成严重破坏。我假设仍然有人在使用加速器软件。

代理服务器和浏览器将缓存GET请求,因此当用户再次访问该页面时,它可能不会将请求发送到您的应用程序,因此用户认为他们已采取了措施,但实际上没有。


1
GET和POST同样可能实现CSRF。例如,攻击者可以在其站点上包括自动提交表单,以触发POST请求。防止CSRF的标准方法是在请求中明确包含攻击者不知道的值(与隐式包含cookie标头不同)。
CodesInChaos

8

如果我做一个将数据插入数据库的php服务,并在GET查询字符串中传递参数,那为什么会出错呢?

最简单的答案是“因为那不是什么GET意思”。

使用GET“ -立即行动特价!”来传递数据的更新是像写情书,并在信封发送它标志着 在这两种情况下,收件人和/或中介机构对您的邮件处理不当都不会感到惊讶


5

对于以数据库为中心的应用程序中的CRUD操作,请使用以下模式:

使用HTTP GET进行读取操作(SQL SELECT)

使用HTTP PUT进行更新操作(SQL UPDATE)

使用HTTP POST进行创建操作(SQL INSERT)

使用HTTP DELETE进行删除操作(SQL DELETE)


3
放置与发布不如您所说。当客户在确切的给定位置修改资源时,放置。对于发布,服务器最终决定资源的确切Uri。
2014年

HTTP PUT是否更像SQL DELETE和INSERT而不是UPDATE?同样,SQL UPDATE可以一次更新许多记录,但是HTTP PUT仅更新一件事。
Backwards_Dave

0

GET永远不要更改服务器上的数据-为此使用POST请求

该建议以及此处的所有答案都是错误的。显然,我过于戏剧化,其他答案也很好,但是我认为确切的建议应为:

GET应该很少更改服务器上的数据-为此使用POST请求

说“从不”太极端了,尽管这里的其他答案准确地解释了为什么您应该“很少”这样做,但是在某些情况下,使用GET更改数据是完全合理的。一个示例是一次性使用电子邮件验证链接。通常,这些链接包含一个GUID,在访问该GUID时必须更改数据。如果正确实现,则随后的相同GET请求将被忽略。

这显然是一个边缘情况,但当然值得注意。


3
如果您的邮件客户端决定在不单击链接的情况下获取链接,该怎么办?例如,因为它想对其进行扫描以查找恶意软件。取消订阅链接的正确方法是进入一个页面,用户可以在其中单击按钮以取消订阅(单击按钮会触发POST请求)。
CodesInChaos

@CodesInChaos-非常好!我同意你的看法。我已经删除了取消订阅的示例,并留下了电子邮件验证作为唯一的示例。除电子邮件验证外,还有其他一些使GET有意义的方法,但目前我还没有想到。
TTT

GET具有副作用的问题同样适用于电子邮件确认。现在,点击链接的客户将确认其他人使用您的电子邮件创建的帐户,从而允许他们模拟您。
CodesInChaos

@CodesInChaos-一个人。您所说的假冒来自相同的用户名或公用个人名称,而不是相同的电子邮件地址,并且无论使用什么电子邮件地址(通常只有服务器始终知道帐户持有人的电子邮件地址),这种冒名都会发生。此外,用别人的电子邮件地址创建一个帐户是没有意义的。那对他们有什么帮助?他们无法控制自己的帐户。
TTT
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.