何时最好清理用户输入?

When is it best to sanitize user input?

提问人:Aaron 提问时间:8/30/2008 最后编辑:Script47Aaron 更新时间:7/11/2022 访问量:25792

问:

用户等于不可信。永远不要相信不可信用户的输入。我明白了。但是,我想知道什么时候是清理输入的最佳时间。例如,您是盲目地存储用户输入,然后在访问/使用时对其进行清理,还是立即清理输入,然后存储此“清理”版本?也许除了这些之外,还有一些其他方法我没有。我更倾向于第一种方法,因为来自用户输入的任何数据仍然必须谨慎处理,其中“清理”的数据仍然可能在不知不觉中或意外地是危险的。无论哪种方式,人们认为哪种方法是最好的,出于什么原因?

XSS SQL 注入 用户输入 清理

评论


答:

-1赞 Craig 8/30/2008 #1

我发现立即清洁它有两个好处。首先,您可以对其进行验证并向用户提供反馈。第二,您不必担心在其他地方使用数据。

14赞 Daniel Jennings 8/30/2008 #2

我喜欢尽早对其进行清理,这意味着当用户尝试输入无效数据时,就会进行清理。如果他们的年龄有一个文本框,并且他们输入了数字以外的任何内容,我不会让字母的按键通过。

然后,无论读取数据(通常是服务器),当我读取数据时,我都会进行健全性检查,以确保不会因为更坚定的用户(例如手动编辑文件,甚至修改数据包)而溜进来

编辑:总体而言,请尽早进行消毒,并在您看不到数据的任何时候进行消毒,哪怕是一秒钟(例如文件保存 - >文件打开)

评论

8赞 Fluffy 7/19/2010
...甚至是禁用 js 的用户:/
21赞 csauve 3/31/2015
这是个坏建议。你应该清理你的输出,而不是你的输入。
8赞 RavenHursT 7/14/2016
@csauve是正确的。不知道为什么这是公认的答案,因为很明显这里的正确解决方案是清理输出。不要尝试“检测”“不良”或“恶意”输入。以这种方式处理问题最终将尝试实现几乎无限数量的启发式解决方案。有关更多信息,请参阅此处:owasp.org/index.php/...
4赞 csauve 7/15/2016
再次阅读问题和答案时,丹尼尔可能会将“Sanitize”解释为“验证”。如果要验证输入是否与字段的条件匹配(即必须为正整数),则在输入时进行验证。如果要像防止恶意值一样进行清理,请在输出时进行清理。
0赞 Sean Chambers 8/30/2008 #3

在将用户输入下放到应用程序的较低层之前,应始终将其视为恶意输入。始终尽快处理清理输入,在检查恶意意图之前,不应出于任何原因将其存储在数据库中。

1赞 mk. 8/30/2008 #4

在存储数据之前清理数据。通常,您不应该在没有首先清理输入的情况下执行任何 SQL 操作。您不想让自己遭受 SQL 注入攻击。

我遵循这些基本规则。

  1. 仅通过 POST 修改 SQL 操作,例如 INSERT、UPDATE、DELETE。 永远不要 GET。
  2. 逃避一切。
  3. 如果您期望用户输入是某种东西,请确保检查它是否是某种东西。例如,您正在请求一个号码,然后确保它是一个号码。使用验证。
  4. 使用过滤器。清理不需要的字符。
1赞 Martin 8/30/2008 #5

用户是邪恶的!

好吧,也许并非总是如此,但我的方法是始终立即进行消毒,以确保我的后端附近没有任何风险。

额外的好处是,如果在输入点进行清理,则可以向用户提供反馈。

评论

1赞 Aaron 9/16/2008
马丁,是只有我还是这充满了影射?:)
0赞 Martin 11/24/2008
当我写它时,我无意成为这种情况,重读它我必须同意你的看法:)
1赞 BrianH 8/30/2008 #6

假设所有用户都是恶意的。 尽快对所有输入进行消毒。 句点。

1赞 Dillie-O 8/30/2008 #7

在对数据进行任何处理之前,我会对其进行清理。我可能需要获取“名字”和“姓氏”字段,并将它们连接到插入到数据库中的第三个字段。我打算在进行串联之前对输入进行清理,这样我就不会出现任何处理或插入错误。越快越好。即使在前端(在 Web 设置中)使用 Javascript 也是理想的,因为这将在没有任何数据进入服务器的情况下发生。

可怕的是,您甚至可能还想开始清理来自数据库的数据。最近激增的 ASPRox SQL 注入攻击是双重致命的,因为它会感染给定数据库中的所有数据库表。如果您的数据库托管在同一数据库中托管了多个帐户,则您的数据会因其他人的错误而损坏,但现在您已经加入了向访问者托管恶意软件的行列,因为您自己的初始错误。

当然,这需要大量的前期工作,但如果数据至关重要,那么这是一项值得的投资。

3赞 Peter Stone 8/30/2008 #8

尽早是好的,绝对在你尝试解析它之前。您稍后要输出的任何内容,尤其是传递给其他组件(即 shell、SQL 等)的任何内容都必须进行清理。

但不要太过分 - 例如,密码在存储之前会进行哈希处理(对吗?哈希函数可以接受任意二进制数据。而且您永远不会打印出密码(对吗?因此,不要解析密码 - 也不要对它们进行清理。

此外,请确保您是从受信任的进程进行清理的 - JavaScript/任何客户端都比无用的安全性/完整性更糟糕。(不过,尽早失败可能会提供更好的用户体验 - 只需在两个地方都这样做。

2赞 Jon 'links in bio' Ericson 8/30/2008 #9

Perl 有一个 taint 选项,它认为所有用户输入都是“污点”,直到用正则表达式检查它。受污染的数据可以被使用和传递,但它会污染它所接触的任何数据,直到未被污染。例如,如果将用户输入附加到另一个字符串,则新字符串也会受到污染。基本上,任何包含受污染值的表达式都会输出受污染的结果。

被污染的数据可以随意抛出(污染数据),但是一旦它被对外部世界有影响的命令使用,perl脚本就会失败。因此,如果我使用受污染的数据来创建文件、构造 shell 命令、更改工作目录等,Perl 将失败并出现安全错误。

我不知道还有另一种语言有类似“污点”的东西,但使用它非常令人大开眼界。令人惊讶的是,如果您不立即取消污染,受污染的数据会以如此快的速度传播。对于程序员来说,自然而正常的事情,比如根据用户数据设置变量或打开文件,在打开污点的情况下似乎是危险和冒险的。因此,完成工作的最佳策略是在从外部获得一些数据后立即取消污染。

我怀疑这也是其他语言中最好的方法:立即验证用户数据,这样错误和安全漏洞就不会传播得太远。此外,如果潜在的漏洞位于一个地方,则审核代码中的安全漏洞应该更容易。而且,您永远无法预测哪些数据以后将用于什么目的。

评论

3赞 csauve 3/31/2015
“立即验证用户数据”= 错误。你的最后一句话说得对:“而且你永远无法预测哪些数据以后会用于什么目的。这就是为什么您需要在使用时清理数据,而不是在创建数据时清理数据的原因。
1赞 Jon 'links in bio' Ericson 3/31/2015
@csauve:哦,我不认为你不应该在使用数据之前也对其进行健全性检查。但是让我问你:如果你从用户那里收集数据,结果证明它在以后的某个日期无法使用,你如何提示用户纠正问题?老实说,这个问题真的有点错误的二分法。
1赞 csauve 4/1/2015
您应该根据业务需要检查不可用的数据,但具有特殊字符的数据不一定不可用。您只需要根据插入它的语言对其进行适当的编码(即 url 编码,html 编码)。
6赞 cpm 8/30/2008 #10

最重要的是在逃跑时始终保持一致。意外的双重消毒是蹩脚的,不消毒是危险的。

对于 SQL,只需确保数据库访问库支持自动转义值的绑定变量即可。任何手动将用户输入连接到 SQL 字符串的人都应该更清楚。

对于 HTML,我更喜欢在最后一刻逃脱。如果你破坏了用户输入,你就永远无法找回它,如果他们犯了错误,他们可以在以后编辑和修复。如果你破坏了他们的原始输入,它就永远消失了。

15赞 Kibbee 8/31/2008 #11

这取决于您正在进行什么样的消毒。

为了防止 SQL 注入,不要对数据本身执行任何操作。只需使用准备好的语句,这样,您就不必担心弄乱用户输入的数据,并使其对您的逻辑产生负面影响。你必须稍微清理一下,以确保数字是数字,日期是日期,因为一切都是来自请求的字符串,但不要尝试做任何检查来做一些事情,比如阻止关键字或任何东西。

为了防止 XSS 攻击,在存储数据之前修复数据可能会更容易。然而,正如其他人所提到的,有时拥有用户输入内容的原始副本是件好事,因为一旦你改变了它,它就会永远丢失。这几乎是太糟糕了,没有一种万无一失的方法来确保您的应用程序只发布经过清理的 HTML,就像您可以确保您不会通过使用准备好的查询被 SQL 注入捕获一样。

3赞 Radu Maris 7/19/2010 #12

我的意见是尽快清理用户输入的客户端和服务器端,我就是这样做的

  1. (客户端),允许用户 在字段中仅输入特定键。
  2. (客户端),当用户使用 onblur 转到下一个字段时,测试他输入的输入 针对正则表达式,如果有什么不好,请通知用户。
  3. (服务器端),再次测试输入, if 字段应该是 INTEGER 检查(在 PHP 中您可以使用 is_numeric() ), IF 字段具有众所周知的格式 根据正则表达式检查它,所有 其他(如文字评论),只是 逃避他们。如果有任何可疑情况,请停止脚本执行,并向用户返回通知,告知用户他输入的数据无效。

如果某些事情看起来确实是可能的攻击,脚本会向我发送邮件和短信,这样我就可以尽快检查并阻止它,我只需要检查我登录所有用户输入的日志,以及脚本在接受输入或拒绝输入之前所做的步骤。

14赞 Epiphany 8/20/2010 #13

我像 Radu 一样清理我的用户数据......

  1. 第一个同时使用正则表达式并控制允许字符的客户端 使用绑定到事件的 javascript 或 jQuery 输入到给定的表单字段中,例如 onChange 或 OnBlur,它会在输入之前删除任何不允许的输入 提交。然而,要意识到,这真的只有让那些 用户知道,数据也将在服务器端进行检查。它 与其说是任何实际的保护,不如说是警告。

  2. 其次,这些天我很少看到这样做了,第一次检查是 完成服务器端是检查提交表单的位置。 仅允许从您指定为有效页面提交表单 位置,您甚至可以在读取任何数据之前杀死脚本。授予 这本身是不够的,因为拥有自己服务器的优秀黑客可以“欺骗” 域和 IP 地址,使脚本显示它即将到来 从有效的表单位置。

  3. 接下来,我什至不必这么说,但总是,我的意思是总是,运行 您的脚本处于污点模式。这迫使你不要偷懒,要勤奋 第 4 步。

  4. 使用格式正确的正则表达式尽快清理用户数据,以适应 表单上任何给定字段的预期数据。不要走捷径 臭名昭著的“独角兽的魔法号角”,吹过你的污点检查...... 或者,您还不如首先关闭污点检查,以获得所有好处 这将为您的安全做出贡献。这就像给精神病患者一把锋利的刀,承受 你的喉咙,并说'你真的不会用那个伤害我,对吧'。

    在第四步中,这是我与大多数人的不同之处,因为我只消毒 我将以可能提供安全性的方式实际使用的用户数据 风险,例如任何系统调用、对其他变量的赋值或对 存储数据。如果我只使用用户输入的数据来与数据进行比较 我自己存储在系统上(因此知道我自己的数据是安全的), 那么我就懒得清理用户数据了,因为我永远不会去找我们 这本身就是一个安全问题。例如,将用户名输入为 一个例子。我仅使用用户输入的用户名来检查它是否匹配 我的数据库,如果为 true,则使用数据库中的数据来执行 我可能会在脚本中调用它的所有其他函数,知道它是安全的,并且永远不会 之后再次使用用户数据。

  5. 最后,是过滤掉这些天机器人尝试自动提交的所有内容,并使用 “人工身份验证”系统,例如验证码。如今,这已经足够重要了 我花时间编写了我自己使用照片的“人工身份验证”模式 以及“人类”输入他们在图片中看到的内容的输入。我这样做是因为 我发现验证码类型的系统确实惹恼了用户(你可以通过他们的 眯起眼睛试图破译扭曲的字母......通常超过 和 一遍)。这对于使用 SendMail 或 SMTP 的脚本尤其重要 对于电子邮件,因为这些是您饥饿的垃圾邮件机器人的最爱。

简而言之,我会像对我妻子一样解释它......你的服务器就像一个受欢迎的夜总会,你拥有的保镖越多,你可能遇到的麻烦就越少 在夜总会。我在门外有两个保镖(客户端验证和人工身份验证),一个保镖就在门内(检查有效的表单提交位置......“这真的是你在这个身份证上吗”),还有几个保镖 靠近门(运行污点模式并使用良好的正则表达式来检查 用户数据)。

我知道这是一篇较旧的帖子,但我觉得它足够重要,任何可能在我访问这里后阅读它的人都会意识到在安全性方面没有“灵丹妙药”,并且需要所有这些相互结合才能使您的用户提供的数据安全。仅仅使用其中的一两种方法实际上毫无价值,因为它们的力量只有在它们齐心协力时才存在。

或者总而言之,正如我妈妈经常说的那样......“安全总比后悔好”。

更新:

这些天我正在做的另一件事是对我的所有数据进行 Base64 编码,然后对将驻留在我的 SQL 数据库上的 Base64 数据进行加密。以这种方式存储它需要大约三分之一的总字节,但在我看来,安全优势超过了数据的额外大小。

评论

0赞 Gaurav Sharma 6/22/2011
谢谢:)用于分享。我喜欢这2点。即在提交表格之前检查来源。
8赞 TRiG 6/28/2011
锁定盲人用户不是最佳做法。
0赞 Your Common Sense 9/2/2013
更新让它变得很棒。
0赞 Funktr0n 4/17/2014
所以我猜“污点模式”是一个笑话,但你能详细说明一下它是什么,只是为了让我理解吗?
1赞 Script47 3/25/2019
通过我同意您的编辑@CommonSenseCode,我将其回滚如下:meta.stackoverflow.com/questions/381780/...
52赞 Your Common Sense 9/2/2013 #14

不幸的是,几乎没有一个参与者清楚地理解他们在说什么。按照字面。只有 Kibbee 设法直截了当地走。

本主题是关于消毒的。但事实是,像每个人都如此渴望谈论的广义“通用消毒”这样的事情根本不存在。

无数种不同的媒介,每种媒介都需要自己独特的数据格式。此外,即使是单一的特定介质,其部分也需要不同的格式。比如说,HTML 格式对于嵌入在 HTML 页面中的 javascript 是无用的。或者,字符串格式对 SQL 查询中的数字毫无用处。

事实上,正如大多数赞成的答案所建议的那样,这种“尽早消毒”是不可能的。因为人们无法分辨数据将用于哪个特定介质或介质部分。比如说,我们正准备防御“sql-injection”,逃避一切移动的东西。但是哎呀!- 一些必填字段没有填写,我们必须将数据填回表单而不是数据库......添加了所有斜杠。

另一方面,我们努力逃避了所有的“用户输入”......但是在SQL查询中,我们没有引号,因为它是一个数字或标识符。而且没有“消毒”对我们有所帮助。

第三 - 好吧,我们尽最大努力净化可怕、不可信和不屑一顾的“用户输入”......但是在一些内部过程中,我们使用了这些数据,没有任何格式(因为我们已经尽力了!) - 哎呀!已经得到了二阶注入的所有荣耀。

因此,从现实生活使用的角度来看,唯一正确的方法是

  • 格式化,而不是任何“消毒”
  • 使用前
  • 根据一定的介质规则
  • 甚至遵循该媒体不同部分所需的子规则。

评论

8赞 cHao 9/2/2013
通读回复后,我有一种很想发布这样的东西的冲动。
1赞 alexw 2/16/2015
看来,我们真正需要的不是“通用清理剂”,而是一个精心设计的、灵活的框架,用于解决你的要点,同时仍然保持代码的合理性和可维护性。
3赞 RavenHursT 7/14/2016
这是一个比公认的答案要好得多的答案......facepalm 头像在这里是完美的:-)