何时最好清理用户输入?
Posted
技术标签:
【中文标题】何时最好清理用户输入?【英文标题】:When is it best to sanitize user input? 【发布时间】:2010-09-07 06:52:57 【问题描述】:用户等于不可信。永远不要相信不可信用户的输入。我明白了。但是,我想知道清理输入的最佳时间是什么时候。例如,您是否盲目地存储用户输入,然后在访问/使用时对其进行清理,或者您是否立即清理输入然后存储此“清理”版本?除了这些之外,也许还有一些我没有想到的其他方法。我更倾向于第一种方法,因为任何来自用户输入的数据仍然必须谨慎处理,“清理”的数据可能仍然在不知不觉中或意外危险。无论哪种方式,人们认为哪种方法最好?出于什么原因?
【问题讨论】:
【参考方案1】:不幸的是,几乎没有一个参与者清楚地理解他们在说什么。字面上地。只有Kibbee 成功了。
这个话题都是关于消毒的。但事实是,像每个人都急于谈论的广义“通用消毒”这样的东西根本不存在。
有无数种不同的媒介,每一种都需要自己的、不同的数据格式。此外 - 即使是单个特定的媒介也需要不同的部分格式强>。比如说,html 格式对于嵌入在 HTML 页面中的 javascript 是没有用的。或者,对于 SQL 查询中的数字,字符串格式化是没有用的。
事实上,正如大多数赞成的答案所建议的那样,这种“尽早清理”只是不可能。因为人们无法分辨数据将在哪个特定介质或介质部分中使用。说,我们正准备防御“sql-injection”,逃避所有移动的东西。但是哎呀! - 一些必填字段未填写,我们必须将数据填写回表单而不是数据库......添加了所有斜线。
另一方面,我们努力地转义了所有“用户输入”......但在 sql 查询中我们没有引号,因为它是一个数字或标识符。没有任何“消毒”帮助过我们。
另一方面 - 好吧,我们尽最大努力清理可怕、不可信和不屑一顾的“用户输入”……但在某些内部流程中,我们使用了这些数据而没有任何格式(因为我们已经尽了最大努力! ) - 哎呀!已经获得了辉煌的二阶注射。
所以,从实际使用的角度来看,唯一正确的方法是
格式化,而不是任何“清理” 使用前 根据某些媒体规则 甚至遵循此媒体不同部分所需的子规则。【讨论】:
阅读回复后,我很想发布这样的内容。 看来我们真正需要的不是“通用清理程序”,而是一个设计良好、灵活的框架,用于解决您的要点,同时仍保持代码的健全和可维护性。 这是一个比公认的好得多的答案...facepalm 头像在这里很完美:-)【参考方案2】:我像 Radu 一样清理我的用户数据...
第一个客户端同时使用正则表达式并控制允许的字符 使用与事件相关的 javascript 或 jQuery 输入给定的表单字段,例如 onChange 或 OnBlur,它甚至可以在任何不允许的输入之前删除它 提交。然而要意识到,这真的只会让那些 知道的用户,数据也将在服务器端进行检查。它是 比任何实际保护更多的是警告。
其次,我现在很少看到这样做了,第一次检查是 完成服务器端是检查表单提交的位置。 仅允许从您指定为有效的页面提交表单 位置,您甚至可以在读取任何数据之前终止脚本。的确, 这本身是不够的,因为拥有自己服务器的优秀黑客可以“欺骗” 域和 IP 地址,使其在您的脚本中显示为即将到来 从有效的表单位置。
接下来,我什至不必说这个,但总是,我的意思是 总是,运行 您的脚本处于污点模式。这迫使你不要偷懒,要勤奋 第 4 步。
使用格式正确的正则表达式尽快清理用户数据 表单上任何给定字段的预期数据。不要走捷径,比如 臭名昭著的“独角兽的魔法号角”吹过你的污点检查...... 或者你也可以一开始就关闭污点检查 它将为您的安全做。这就像给精神病患者一把锋利的刀 你的喉咙,然后说“你真的不会那样伤害我”。
这是我在第四步中与大多数其他人不同的地方,因为我只消毒 我将以一种可能会带来安全性的方式实际使用的用户数据 风险,例如任何系统调用、对其他变量的赋值或任何写入 存储数据。如果我只使用用户输入的数据与数据进行比较 我自己存储在系统上(因此知道我自己的数据是安全的), 然后我就懒得清理用户数据了,因为我永远不会去给我们它的方式 这本身就是一个安全问题。例如,将用户名输入作为 一个例子。我只使用用户输入的用户名来检查它是否匹配 我的数据库,如果为真,之后我使用数据库中的数据执行 我可能在脚本中调用它的所有其他函数,知道它是安全的,并且永远不会 之后再次使用用户数据。
最后,过滤掉这些天机器人尝试的所有自动提交,使用 “人工认证”系统,例如验证码。这些天这很重要 我花时间编写了自己的使用照片的“人工身份验证”模式 以及让“人类”输入他们在图片中看到的内容的输入。我这样做是因为 我发现 Captcha 类型的系统确实让用户很恼火(你可以从他们的 由于试图破译扭曲的字母而眯起的眼睛......通常是 再次)。这对于使用 SendMail 或 SMTP 的脚本尤其重要 对于电子邮件,因为这些是您饥饿的垃圾邮件机器人的最爱。
总而言之,我会像对我妻子那样解释...您的服务器就像一个受欢迎的夜总会,您拥有的保镖越多,您遇到的麻烦就越少 在夜总会。我在门外有两个保镖(客户端验证和人工身份验证),一个在门内的保镖(检查有效的表单提交位置......'这真的是你在这个 ID 上吗'),还有几个保镖进来 靠近门(运行污染模式并使用良好的正则表达式来检查 用户数据)。
我知道这是一篇较旧的帖子,但我觉得这对任何在我访问这里后可能阅读它的人来说已经足够重要了,因为他们意识到他们在安全方面不是“灵丹妙药”,并且所有这些都需要相互配合才能使您的用户提供的数据安全。仅使用其中一种或两种方法实际上是毫无价值的,因为它们的力量只有在他们齐心协力时才会存在。
或者总而言之,就像我妈妈经常说的那样……“安全总比后悔好”。
更新:
这些天我正在做的另一件事是对我的所有数据进行 Base64 编码,然后对将驻留在我的 SQL 数据库中的 Base64 数据进行加密。以这种方式存储它需要大约三分之一的总字节数,但在我看来,安全性好处超过了数据的额外大小。
【讨论】:
感谢 :) 分享。我喜欢2分。即在提交表单之前检查来源。 锁定盲人用户不是最佳做法。 刚刚更新太棒了。 所以我猜“污点模式”是个玩笑,但您能否详细说明一下,以便我理解? 虽然我同意您的编辑@CommonSenseCode,但我将其回滚为:meta.***.com/questions/381780/…【参考方案3】:我的意见是尽可能在客户端和服务器端清理用户输入,我正在这样做
-
(客户端),允许用户
在字段中只输入特定的键。
(客户端),当用户使用 onblur 转到下一个字段时,测试他输入的输入
反对正则表达式,如果有什么不好,请通知用户。
(服务器端),再次测试输入,
if 字段应该是 INTEGER 检查(在 php 中你可以使用 is_numeric() ),
如果字段具有众所周知的格式
对照正则表达式检查它,所有
其他(如文本 cmets),只是
逃离他们。如果有任何可疑之处,请停止脚本执行并向用户返回他输入的数据无效的通知。
如果某些东西看起来确实是可能的攻击,脚本会向我发送邮件和短信,这样我就可以尽快检查并阻止它,我只需要检查我登录所有用户的日志输入,以及脚本在接受或拒绝输入之前执行的步骤。
【讨论】:
【参考方案4】:这取决于您正在进行哪种消毒。
为了防止 SQL 注入,不要对数据本身做任何事情。只需使用准备好的语句,这样,您就不必担心会弄乱用户输入的数据,并让它对您的逻辑产生负面影响。您必须稍微清理一下,以确保数字是数字,日期是日期,因为所有内容都是来自请求的字符串,但不要尝试进行任何检查以执行阻止关键字或任何操作之类的操作。
为了防止 XSS 攻击,在存储数据之前修复数据可能会更容易。但是,正如其他人所提到的,有时拥有用户输入内容的原始副本会很好,因为一旦您更改它,它就会永远丢失。几乎太糟糕了,没有一种万无一失的方法来确保您的应用程序只输出经过清理的 HTML,而您可以通过使用准备好的查询来确保您不会被 SQL 注入捕获。
【讨论】:
【参考方案5】:最重要的是在逃跑时始终保持一致。意外的双重消毒是蹩脚的,不消毒是危险的。
对于 SQL,只需确保您的数据库访问库支持自动转义值的绑定变量。任何手动将用户输入连接到 SQL 字符串的人都应该知道得更好。
对于 HTML,我更喜欢在最后一刻转义。如果您破坏了用户输入,您将永远无法取回它,如果他们犯了错误,他们可以稍后进行编辑和修复。如果你破坏了他们的原始输入,它就永远消失了。
【讨论】:
【参考方案6】:Perl 有一个 taint 选项,它认为所有用户输入“已被污染”,直到它被正则表达式检查。被污染的数据可以被使用和传递,但它会污染它接触到的任何数据,直到未被污染。例如,如果用户输入附加到另一个字符串,则新字符串也会受到污染。基本上,任何包含污染值的表达式都会输出污染结果。
被污染的数据可以随意乱扔(污染数据),但是一旦它被一个对外界有影响的命令使用,perl 脚本就会失败。因此,如果我使用受污染的数据创建文件、构建 shell 命令、更改工作目录等,Perl 将失败并出现安全错误。
我不知道另一种语言有“污点”之类的东西,但使用它让我大开眼界。如果您不立即清除受污染的数据,那么受污染的数据会以多快的速度传播开来,这真是令人惊讶。对程序员来说自然而正常的事情,例如根据用户数据设置变量或打开文件,在启用污染时似乎很危险和有风险。因此,完成工作的最佳策略是在从外部获得一些数据后立即清除污染。
我怀疑这也是其他语言的最佳方法:立即验证用户数据,这样错误和安全漏洞就不会传播得太远。此外,如果潜在的漏洞在一个地方,那么审计代码的安全漏洞应该会更容易。而且你永远无法预测哪些数据以后会用于什么目的。
【讨论】:
"立即验证用户数据" = 错误。您的最后一句话是正确的:“而且您永远无法预测哪些数据将用于以后的目的。”这就是为什么您需要在使用时对数据进行清理,而不是在创建数据时进行清理。 @csauve:哦,我认为您不应该在使用数据之前也检查数据的完整性。但是让我问您:如果您从用户那里收集数据,并且在以后发现无法使用,您如何提示用户更正问题?说实话,这个问题确实有点假二分法。 您应该根据自己的业务需求检查不可用数据,但有特殊字符的数据不一定不可用。您只需要对其插入的语言进行适当的编码(即 url 编码为 url,html 编码为 html)。【参考方案7】:早期是好的,绝对是在你尝试解析它之前。您稍后要输出的任何内容,或者特别是传递给其他组件(即 shell、SQL 等)的任何内容都必须进行清理。
但不要太过分——例如,密码在存储之前会经过哈希处理(对吗?)。哈希函数可以接受任意二进制数据。而且您永远不会打印出密码(对吗?)。所以不要解析密码 - 也不要清理它们。
此外,请确保您从受信任的进程中进行清理 - JavaScript/任何客户端都比无用的安全/完整性更糟糕。 (不过,尽早失败可能会提供更好的用户体验 - 只需在两个地方都这样做。)
【讨论】:
【参考方案8】:我会在对数据进行任何处理之前对其进行清理。我可能需要获取名字和姓氏字段并将它们连接到插入数据库的第三个字段中。我什至会在进行连接之前对输入进行清理,这样我就不会遇到任何类型的处理或插入错误。越早越好。即使在前端(在 Web 设置中)使用 Javascript 也是理想的,因为这将在没有任何数据进入服务器的情况下发生。
可怕的是,您甚至可能还想开始清理来自数据库的数据。最近流行的ASPROx SQL 注入攻击具有双重杀伤力,因为它会感染给定数据库中的所有数据库表。如果您的数据库托管在同一数据库中托管多个帐户的某个地方,您的数据会因为其他人的错误而损坏,但现在您已经加入了向访问者托管恶意软件的行列,因为您自己最初没有过错.
当然,这需要大量的前期工作,但如果数据很关键,那么这是一项值得投资的项目。
【讨论】:
【参考方案9】:假设所有用户都是恶意的。 尽快清理所有输入。 句号。
【讨论】:
【参考方案10】:用户是邪恶的!
也许并非总是如此,但我的方法是始终立即进行清理,以确保在我的后端附近没有任何风险。
额外的好处是,如果您在输入点进行清理,您可以向用户提供反馈。
【讨论】:
Martin,是我自己还是这充满了影射? :) 当我写它的时候我并没有打算这样,重新阅读它我必须同意你的观点:)【参考方案11】:在存储之前清理数据。一般来说,您不应该在没有首先清理输入的情况下执行 ANY SQL 操作。您不想让自己遭受 SQL 注入攻击。
我有点遵循这些基本规则。
-
仅通过 POST 执行修改 SQL 操作,例如 INSERT、UPDATE、DELETE。永远不要 GET。
逃离一切。
如果您希望用户输入是某事,请确保您检查它是否是某事。例如,您正在请求一个数字,然后确保它是一个数字。使用验证。
使用过滤器。清除不需要的字符。
【讨论】:
【参考方案12】:在进入应用程序的较低层之前,应始终将用户输入视为恶意输入。始终尽快处理净化输入,并且在检查恶意意图之前不应出于任何原因将其存储在您的数据库中。
【讨论】:
【参考方案13】:我喜欢尽早对其进行清理,这意味着清理会在用户尝试输入无效数据时进行。如果有一个适合他们年龄的文本框,并且他们输入的不是数字,我不会让字母的按键通过。
然后,无论正在读取数据(通常是服务器),我都会在读取数据时进行完整性检查,以确保不会因为用户更坚定(例如手动编辑文件,或甚至修改数据包!)
编辑:总体而言,尽早清理并清理任何时候您甚至一秒钟都看不到数据(例如文件保存 -> 文件打开)
【讨论】:
... 甚至是禁用 js 的用户:/ 这是个坏建议。你应该清理你的输出,而不是你的输入。 @csauve 是正确的。当很清楚这里的正确解决方案是清理输出时,不确定为什么这是公认的答案。不要试图“检测”“坏”或“恶意”输入。以这种方式解决问题最终将尝试实现几乎无限数量的启发式解决方案。请参阅此处了解更多信息:owasp.org/index.php/… 再次阅读问答后,Daniel 可能将“消毒”解释为“验证”。如果您想验证输入是否与字段的条件匹配(即必须是正整数),请在输入时执行此操作。如果您想清理以防止恶意值,请在输出时执行。【参考方案14】:我发现立即清洗它有两个好处。一,您可以对其进行验证并向用户提供反馈。二、不用担心在其他地方消费数据。
【讨论】:
以上是关于何时最好清理用户输入?的主要内容,如果未能解决你的问题,请参考以下文章