当浏览器重新加载/返回时,如何防止数据库再次被写入?

时间:2008-11-20 15:36:55

标签: database perl cgi idempotent

我正在整理一个写入数据库的小型Web应用程序(Perl CGI和MySQL)。 CGI脚本从表单中获取一些信息并将其写入数据库。但是,我注意到,如果我在网络浏览器上点击“重新加载”或“返回”,它会再次将数据写入数据库。我不想要这个。

在这种情况下,防止重写数据的最佳方法是什么?

7 个答案:

答案 0 :(得分:17)

不要使用GET请求进行修改!是RESTful;使用POST(或PUT)代替浏览器应警告用户不要重新加载请求。在POST / PUT请求之后使用普通GET请求将(using HTTP redirection)重定向到收据页面将使刷新页面成为可能,而不会收到有关重新提交的警告。

编辑:

我认为用户以某种方式登录,因此您已经有了一些跟踪用户的方法,例如会议或类似会议。

您可以在显示表单时创建时间戳(或随机哈希等...),将其存储为隐藏字段(除了反跨站点请求令牌之外我确定您已经在那里< / em>),并在会话变量(安全地存储在您的服务器上),当您收到此表单的POST / PUT请求时,您检查时间戳是否与会话中的时间戳相同。如果是,则将会话中的时间戳设置为变量且难以猜测(例如,时间戳与某些秘密字符串连接),然后您可以保存表单数据。如果有人现在重复请求,您将在会话变量中找不到相同的值并拒绝该请求。

执行此操作的问题是,如果用户单击以更改某些内容,则表单无效,并且可能有点苛刻,除非您需要更新资金。因此,如果您遇到“愚蠢”用户的问题,他们刷新并单击后退按钮,从而意外地重新发布内容,只需使用POST就会提醒他们不要这样做,重定向会降低后者的可能性。如果你有恶意用户的问题,你应该使用时间戳,虽然它有时会使用户感到困惑,但是如果用户故意在一遍又一遍地发布相同的消息你可能需要找到一种方法来禁止它们。使用POST,拥有一个timestam,甚至对整个数据库进行全面比较以检查重复的帖子,如果恶意用户只是编写一个脚本来加载表单并自动提交随机垃圾,则根本无济于事。 (但跨站点请求保护使得这更加困难)

答案 1 :(得分:6)

使用POST请求会导致浏览器尝试阻止用户再次提交相同的请求,但我建议使用某种基于会话的事务跟踪,以便用户忽略来自浏览器的警告和重新提交他的查询您的应用程序将防止重复更改数据库。您可以在提交表单中包含一个隐藏的输入,其值设置为加密哈希值,并在请求被提交和处理时记录该哈希值。

答案 2 :(得分:4)

我发现跟踪用户在会话中执行的表单提交数量非常方便。然后在渲染表单时,我创建一个包含该数字的隐藏字段。如果用户然后通过按后退按钮重新提交表单,它将提交旧的#并且服务器可以通过检查会话中的内容与表单所说的内容来告知用户已经提交了表单。

只需2美分。

答案 3 :(得分:1)

如果您尚未使用某种会话管理(可以让您记录和跟踪表单提交),一个简单的解决方案是在表单中包含某种唯一标识符(作为隐藏元素)是主数据库事务本身的一部分,或在单独的数据库表中跟踪。然后,当您提交表单时,检查唯一ID以查看它是否已被处理。每次呈现表单本身时,您只需确保拥有唯一的ID。

答案 4 :(得分:1)

首先,你不能相信浏览器,所以任何关于使用POST而不是GET的讨论主要是书呆子flim-flam。是的,客户可能会收到“你是不是要重新提交这些数据?”的警告,但他们很可能会说“是的,现在让我一个人,愚蠢的电脑”。

这是正确的:如果你不想要重复提交,那么你需要解决的问题,而不是用户的问题。

您可能已经知道重复提交意味着什么。也许它在几秒钟内就是相同的IP,也许它与博客文章的标题或最近提交的URL相同。也许这是价值观的组合 - 例如联系表单提交的IP地址,电子邮件地址和主题标题。无论哪种方式,如果您手动发现数据中的一些重复项,您应该能够找到一种在提交时以编程方式识别副本的方法,并将其标记为手动批准(如果您不确定),或者只是告诉提交者“你有双击吗?” (如果信息不是非常机密,你可以提供你现有的记录,并说“这是你打算送我们的吗?如果是的话,你已经做过了 - 很好”)

答案 5 :(得分:1)

我不依赖浏览器的POST警告。用户只需单击“确定”即可使消息消失。

任何时候您都会收到一次只需要“付款”的请求,请发送一个唯一的令牌,然后随请求一起提交。它返回后会丢弃令牌,因此您现在可以判断某些内容何时是有效的提交(任何带有令牌“非活动”的令牌)。在X个时间量后使活动令牌过期,例如当用户会话结束时。

(交替跟踪已经回来的代币,如果您之前收到它,则无效。)

答案 6 :(得分:0)

每次更改数据时都执行POST,但永远不会从帖子返回HTML响应...而是将重定向返回到GET,以便将更新的数据作为确认页面进行检索。这样,他们就不用担心刷新页面了。如果他们刷新,那么所有会发生的是另一个检索,而不是数据更改动作。