数据库安全:中间“to_be_deleted”列/表?

时间:2008-09-19 15:21:56

标签: sql database

每个人都意外地忘记了WHERE查询中的DELETE条款,并对一些未备份的数据进行了一次或两次的抨击。我正在思考这个问题,我想知道我提出的解决方案是否实用。

如果代替实际的DELETE查询,应用程序和维护脚本的行为如下:

UPDATE foo SET to_be_deleted=1 WHERE blah = 50;

然后一个cron作业设置通过实际删除标志的一切?缺点是几乎所有其他查询都需要附加WHERE to_be_deleted != 1,但好处是你永远不会再错误地丢失数据。您可以看到“受影响的2,349,325行”并说“嗯,看起来我忘记了WHERE子句”,并重置了标志。您甚至可以将to_be_deleted字段设为DATE列,因此cron作业会检查行的时间是否已经到来。

此外,您可以从生产数据库用户中删除DELETE权限,因此即使有人设法将某些SQL注入您的网站,他们也无法删除任何内容。

所以,我的问题是:这是一个好主意,还是我没有看到陷阱?

14 个答案:

答案 0 :(得分:4)

如果你想这样做,那很好,但看起来很多工作。有多少人手动更改数据库?它应该很少,特别是如果您的用户有应用程序可以使用。

当我处理生产数据库时,我把一切都放在一个事务中,所以如果我搞砸了,我就可以回滚了。对我来说,这样的标准练习对我有帮助。

我没有看到任何真正的错误,尽管除了每个应用程序中的任何单点数据操作都必须知道这个功能而不仅仅是它想要的数据。

答案 1 :(得分:2)

只要您的应用程序不要求立即删除数据就可以了,因为您必须等待cron作业的下一个间隔。

我认为更好的解决方案和更常见的做法是使用开发服务器和生产服务器。如果您的开发数据库被烧毁,只需重新加载即可。没有伤害。如果您正在测试生产数据库中的代码,那么您应该得到任何不好的事情。

答案 2 :(得分:2)

很多人都有删除标志或行状态标志。但是,如果有人通过后端进行更改(并且他们将会这样做,因为通常人们需要完成无法通过前端完成的批量更改)并且他们犯了错误,他们仍然会经常去删除。最终,在将脚本应用到生产环境之前,这无法替代测试脚本。

另外......如果以下查询被执行“UPDATE foo SET to_be_deleted = 1”会发生什么,因为他们不使用where子句。除非你有一个带有时间戳的审核列,你怎么知道哪些列被删除以及哪些列是错误的?但即使你有一个带有时间戳的审计列,如果审计是通过存储过程或程序员约定完成的,那么这些后端查询可能不会提供信息,让你知道它们刚被应用。

答案 3 :(得分:2)

太复杂了。对此的标准方法是在事务中完成所有工作,因此如果搞砸了并忘记了WHERE子句,那么当您看到“受影响的2,349,325行”结果时,您只需回滚。

答案 4 :(得分:2)

为已删除的行创建并行表可能更容易。原始表上的DELETE触发器(以及UPDATE也可以撤消更改)可以将受影响的行复制到并行表。将datetime列添加到并行表以记录日期和时间。更改时间将允许您使用您的cron作业永久删除超过特定年龄的行。

这样,您就可以在原始表上使用普通的DELETE语句,因此您不会忘记运行特殊的“DELETE”语句。你也回避了to_be_deleted != 1表达式,这只是一个等待不可避免地忘记的错误。

答案 5 :(得分:2)

看起来你在这里描述了三个案例。

  1. 案例1 - 维护脚本。通过开发它们并在除生产箱之外的环境中进行测试,可以将风险降至最低。为了快速维护,请在单个事务中进行维护,并在提交之前检查所有内容。如果出错,请发出rollback命令。对于您不一定要等待或在单个事务中执行的更严重的维护,请考虑在运行维护作业之前直接进行备份,以便在遇到脚本之前始终可以恢复到运行脚本之前严重的问题。

  2. 案例2 - SQL注入。这是一个架构问题。您的应用程序不应将SQL传递到数据库中,应通过包/存储过程/函数控制访问,并且应使用绑定变量应用来自UI并在DDL语句中使用的值,而不应使用通过将字符串附加在一起来创建动态SQL。

  3. 案例3 - 定期批处理作业。这些应该在部署到生产之前进行测试。如果删除太多,则会出现错误,并且必须依赖备份策略。

答案 6 :(得分:2)

  

每个人都不小心忘记了   DELETE查询的WHERE子句和   一次或多次轰炸一些未备份的数据   两次。

没有。我总是将我的DELETE原型设为SELECT s,并且只有后者提供我要删除的结果时才会将WHERE之前的语句更改为DELETE。这让我在做任何事情之前检查我想要影响的行。

答案 7 :(得分:1)

您可以在该表上设置一个选择WHERE to_be_deleted!= 1的视图,并在该视图上完成所有正常选择 - 这样就不必将WHERE放在所有查询上。

答案 8 :(得分:0)

陷阱是它不必要的复杂,有人会无意中忘记检查他们的查询中的标志。还存在可能需要立即删除某些内容而不是等待预定作业运行的问题。

答案 9 :(得分:0)

要避免使用to_be_deleted WHERE子句,可以在delete命令触发之前创建触发器,以将已删除的行插入到单独的表中。当您确定需要删除其中的所有内容时,可以清除此表,或者您可以将其保留用于存档目的。

答案 10 :(得分:0)

您还可以获得“软删除”功能,因此您可以为(某些)最终用户提供“撤消”功能 - 在混合中必须有一个相当强大的缺点,以取消软删除的好处。

答案 11 :(得分:0)

每个其他查询的“WHERE to_be_deleted<> 1”是一个巨大的问题。另一种情况是,一旦你发现了意外的流氓查询,你将如何确定哪些2,349,325 以前被标记为已删除?

我认为实际的解决方案是定期备份,如果失败了,也许是一个删除触发器来捕获要​​被砍掉的元组。

答案 12 :(得分:0)

另一种选择是在每个表上创建一个删除触发器。当删除任何内容时,它会将“要删除”的记录插入到另一个表中,理想情况下名为TABLENAME_deleted。

缺点是db会有两倍的表。

我不推荐一般的触发器,但它可能就是你要找的东西。

答案 13 :(得分:0)

这就是为什么,无论何时手动编辑数据,都应该开始转换,编辑数据,检查数据是否正常(例如,您没有删除的数据超出预期),然后是END TRAN。如果您正在使用Postgres,那么您也希望创建大量的保存点,以便输入错误不会消除您的中间工作。

但是,在许多应用程序中,将软件标记记录视为无效而不是删除它们确实有意义。添加自动更新的last_modified日期,您就可以准备将​​增量更新设置到数据仓库中。即使您没有现在的数据仓库,在准备便宜时,为未来做好准备也绝不会让您痛苦。另外,如果出现手动错误,您仍然可以获得数据,并且可以找到所有在您犯错并修复后被“删除”的记录。 (你仍然应该使用交易。)