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