5.12. 禁止 HTTP GET 执行非查询操作

使用 HTTP 的 Web 应用程序应禁止将 HTTP ``GET'' 或 ``HEAD'' 方法用于查询以外的任何操作。HTTP 包括多种不同的方法;最常用的两种方法是 GET 和 POST。GET 和 POST 都可以用于从表单传输数据,但 GET 方法在 URL 中传输数据,而 POST 方法单独传输数据。

使用 GET 执行非查询操作(例如更改数据、转账或注册服务)的安全问题在于,攻击者可以创建一个包含恶意表单数据的 URL 的超文本链接。如果攻击者说服受害者点击链接(在超文本链接的情况下),或者仅仅是查看页面(在来自 HTML 的 img 标签的图像等包含信息的情况下),受害者将执行 GET。当执行 GET 时,攻击者创建的所有表单数据将由受害者发送到指定的链接。这是一种跨站恶意内容攻击,在 第 7.15 节 中进一步讨论。

如果恶意跨站内容攻击可以执行的唯一操作是让用户查看意外数据,那么这不是一个非常严重的问题。当然,这仍然可能是一个问题,因为可以使用此功能进行一些攻击。例如,由于用户请求意外内容,可能存在隐私泄露的风险;由于看起来请求非法或具罪证性的材料,可能会产生现实世界的影响;或者通过使用户以某些方式请求信息,信息可能会以通常不会暴露的方式暴露给攻击者。但是,如果恶意攻击者不仅可以通过跨站链接导致数据查看,还可以导致数据更改,则可能会造成更严重的后果。

典型的 HTTP 接口(例如大多数 CGI 库)通常会隐藏 GET 和 POST 之间的差异,因为对于获取数据,以“相同方式”处理这些方法很有用。但是,对于实际导致数据查询以外的操作,请检查请求是否为 POST 以外的其他内容;如果是,只需显示一个已填充的表单,其中包含给定的数据,并要求用户确认他们确实要执行该请求。这将防止跨站恶意内容攻击,同时仍然使用户能够方便地通过单击一下来确认操作。

实际上,HTTP 规范强烈建议这种行为。根据 HTTP 1.1 规范(IETF RFC 2616 第 9.1.1 节),“GET 和 HEAD 方法不应具有执行检索以外操作的意义。这些方法应被视为“安全”的。这允许用户代理以特殊方式表示其他方法,例如 POST、PUT 和 DELETE,以便用户意识到正在请求可能不安全的操作。”

为了公平起见,我应该指出,这并没有完全解决问题,因为在某些浏览器(在某些配置中),脚本化的 POST 请求可以执行相同的操作。例如,想象一下一个启用了 ECMAscript (Javascript) 的 Web 浏览器接收到以下 HTML 代码片段 - 在某些浏览器上,仅显示此 HTML 代码片段将自动强制用户向攻击者选择的网站发送 POST 请求,并带有攻击者定义的表单数据
  <form action=http://remote/script.cgi method=post name=b>
    <input type=hidden name=action value="do something">
    <input type=submit>
  </form>
  <script>document.b.submit()</script>
感谢 David deVitry 指出这一点。但是,尽管此建议不能解决所有问题,但仍然值得这样做。部分原因是,剩余的问题可以通过更智能的 Web 浏览器(例如,始终在允许 ECMAscript 发送 Web 表单之前确认数据)或通过 Web 浏览器配置(例如,禁用 ECMAscript)来解决。此外,此攻击在许多跨站脚本攻击中不起作用,因为许多网站不允许用户发布“script”命令,但允许任意 URL 链接。因此,将 GET 命令可以执行的操作限制为查询可以显着提高 Web 应用程序的安全性。