我正在组合一个写入数据库(Perl CGI 和 MySQL)的小型 Web 应用程序。CGI 脚本从表单中获取一些信息并将其写入数据库。然而,我注意到,如果我在网络浏览器上点击“重新加载”或“返回”,它会再次将数据写入数据库。我不想要这个。
在这种情况下,防止数据被重写的最佳方法是什么?
我正在组合一个写入数据库(Perl CGI 和 MySQL)的小型 Web 应用程序。CGI 脚本从表单中获取一些信息并将其写入数据库。然而,我注意到,如果我在网络浏览器上点击“重新加载”或“返回”,它会再次将数据写入数据库。我不想要这个。
在这种情况下,防止数据被重写的最佳方法是什么?
不要使用 GET 请求进行修改!RESTful ; _ 使用 POST(或 PUT)代替浏览器应该警告用户不要重新加载请求。在 POST/PUT 请求之后使用普通的 GET 请求重定向(使用 HTTP 重定向)到收据页面,这样就可以刷新页面而不会收到有关重新提交的警告。
编辑:
我假设用户以某种方式登录,因此您已经有了一些跟踪用户的方法,例如会话或类似的。
您可以在显示表单时制作时间戳(或随机哈希等),并将其存储为隐藏字段(除了反跨站点请求令牌之外,我确信您已经拥有了)和会话变量(它安全地存储在您的服务器上),当您收到此表单的 POST/PUT 请求时,您检查时间戳是否与会话中的时间戳相同。如果是这样,您将会话中的时间戳设置为变量且难以猜测(例如时间戳与一些秘密字符串连接),然后您可以保存表单数据。如果有人现在重复请求,您将不会在会话变量中找到相同的值并拒绝该请求。
这样做的问题是,如果用户单击返回以更改某些内容,则该表单无效,并且可能有点苛刻,除非您要更新的是钱。因此,如果您遇到“愚蠢”用户刷新并单击后退按钮从而意外重新发布某些内容的问题,那么仅使用 POST 会提醒他们不要这样做,并且重定向会降低这种可能性。如果您遇到恶意用户的问题,您也应该使用时间戳,尽管它有时会使用户感到困惑,尽管如果用户故意一遍又一遍地发布相同的消息,您可能需要找到一种方法来禁止他们。使用 POST,有一个时间戳,甚至对整个数据库进行全面比较以检查重复的帖子,都不会 如果恶意用户只是编写一个脚本来加载表单并自动提交随机垃圾,那根本没有帮助。(但是跨站点请求保护使这变得更加困难)
使用 POST 请求将导致浏览器尝试阻止用户再次提交相同的请求,但我建议使用某种基于会话的事务跟踪,这样如果用户忽略来自浏览器的警告并重新提交他的查询您的应用程序将防止对数据库的重复更改。您可以在提交表单中包含一个隐藏输入,并将值设置为加密哈希,并在请求被提交和处理时记录该哈希。
我发现跟踪用户在会话中提交的表单数量很方便。然后在呈现表单时,我创建一个包含该数字的隐藏字段。如果用户然后通过按下后退按钮重新提交表单,它将提交旧的 # 并且服务器可以通过检查会话中的内容和表单所说的内容来判断用户已经提交了表单。
只是我的2美分。
如果您还没有使用某种会话管理(它可以让您记录和跟踪表单提交),一个简单的解决方案是在表单中包含某种唯一标识符(作为隐藏元素),它是任何一部分主数据库事务本身,或在单独的数据库表中跟踪。然后,当您提交表单时,您会检查唯一 ID 以查看它是否已被处理。每次呈现表单本身时,您只需要确保您有一个唯一的 ID。
首先,你不能信任浏览器,所以任何关于使用 POST 而不是 GET 的讨论大多都是书呆子 flim-flam。是的,客户可能会收到类似“您是否打算再次重新提交此数据?”的警告,但他们很可能会说“是的,现在别管我了,愚蠢的计算机”。
正确的是:如果您不想重复提交,那么要解决的是您的问题,而不是用户的问题。
您大概知道重复提交意味着什么。可能是几秒内的同一个IP,也可能是同一个博文标题或者最近提交的URL。也许它是值的组合——例如,IP 地址、电子邮件地址和联系表单提交的主题标题。无论哪种方式,如果您在数据中手动发现了一些重复项,您应该能够找到一种在提交时以编程方式识别重复项的方法,或者将其标记为手动批准(如果您不确定),或者只是告诉提交者“你双击了吗?” (如果信息不是非常机密,您可以出示您为他们提供的现有记录并说“这是您要发送给我们的吗?如果是这样,您已经完成了 - 万岁”)
我不会依赖来自浏览器的 POST 警告。用户只需单击“确定”即可使消息消失。
任何时候你都会有一个只需要一次的请求,例如“付款”,发送一个唯一的令牌,它会与请求一起提交回来。令牌返回后将其扔掉,因此您现在可以判断某事何时是有效提交(任何具有非“活动”令牌的事)。在 X 时间后过期活动令牌,例如当用户会话结束时。
(交替跟踪已返回的令牌,如果您之前收到过,则无效。)
每次更改数据时执行 POST,但永远不要从帖子返回 HTML 响应...而是返回重定向到 GET,该 GET 将更新的数据作为确认页面检索。这样,就不用担心他们刷新页面了。如果它们刷新,将发生的只是另一次检索,而不是更改数据的操作。