我会建议一种不同的方式来看待与你在这里遇到的问题正交的问题(因此不是不兼容的,没有理由你不能同时检查它,以防你发现你错过了什么另一个)。
在任何验证中重要的两件事是:
- 您需要注意的事项。
- 您传递到另一层的东西保持不变。
现在,到目前为止,您提到的大部分内容都属于第一类。您忽略的 Cookie 适合第二个,如果您使用 Server.Execute 或类似方法传递给另一个处理程序,则会查询和发布信息。
第二类是最有争议的。
一方面,如果给定的处理程序(.aspx 页面、IHttpHandler 等)忽略了将来某个时间可能被另一个处理程序使用的 cookie,则主要由另一个处理程序来验证它。
另一方面,假设其他层存在安全漏洞并且您不应该相信它们是正确的,即使是您自己编写的(尤其是如果您自己编写的!),这种方法总是好的。
一个中间立场是,如果可能有 5 种不同的状态,一些持久数据可能有效地处于,但只有 3 种在特定代码被命中时才有意义,它可能会验证它处于其中一个状态3 州,即使这不会对该特定代码构成风险。
完成后,我们将专注于第一类。
查询字符串、表单数据、回发、标题和 cookie 都属于来自用户的同一类内容(无论他们是否知道)。事实上,它们有时是看待同一事物的不同方式。
其中,有一个子集我们会以任何方式实际处理。
其中每个此类项目都有一系列合法值。
其中,作为一个整体,存在一系列合法的值组合。
因此验证变成:
- 确定我们将根据哪些输入采取行动。
- 确保该输入的每个组件本身都是有效的。
- 确保组合有效(例如,不发送信用卡号可能有效,但不发送但将付款类型设置为“信用卡”则无效)。
现在,当我们谈到这一点时,通常最好不要尝试捕获某些攻击。例如,在将传递给 SQL 的值中避免使用 ' 并不是很好。相反,我们有三种可能性:
在值中包含 ' 是无效的,因为它不属于那里(例如,一个只能是“真”或“假”的值,或者来自一组值的列表,其中没有它们包含')。在这里,我们发现它不在合法值集中,并忽略了攻击的确切性质(因此也受到保护,免受我们甚至不知道的其他攻击!)。
它作为人工输入是有效的,但不是我们将使用的。这里的一个例子是一个很大的数字(在某些文化中' 用于分隔数千)。在这里,我们将 "123,456,789" 和 "123'456'789" 都规范化为 123456789 并且不关心之前的情况,只要我们可以有意义地这样做(输入不是“鱼”或超出了本案的法律价值范围)。
这是有效的输入。如果您的应用程序阻止名称字段中的撇号以试图阻止 SQL 注入,那么它就是错误的,因为那里有带撇号的真实姓名。在这种情况下,我们认为“d'Eath”和“O'Grady”是有效的输入,并通过正确转义处理'在SQL中的重要性(理想情况下,通过使用API进行数据访问)我们。
关于 ASP.NET 的第三点的一个经典示例是使用 < 和 > 阻止“可疑”输入的代码 - 这使得大量 ASP.NET 页面出现错误。诚然,不恰当地阻止它比不恰当地接受它更好,但默认值适用于那些没有考虑过验证并试图阻止他们严重伤害自己的人。由于您正在考虑验证,因此您应该考虑是否适合关闭自动验证,然后以适合您给定用途的方式处理 < 和 >。
还请注意,我还没有提到任何关于 javascript 的内容。我不验证 javascript(除非我实际上是接收它),我忽略它。我假装它不存在,然后我不会错过它的验证可能被篡改的情况。假装你的在这一层也不存在。最终,客户端验证是为了节省好人犯诚实错误的时间,而不是阻挠坏人。
出于类似原因,最好不要通过浏览器进行测试。使用 Fiddler 构建达到您要检查的验证点的请求。这样一来,所有客户端验证都被绕过了,并且您以与攻击者相同的方式查看服务器。
最后,请记住,100% 完美验证的页面不一定安全。例如。如果您的验证是完美的,但您的身份验证很差,那么有人可以向它发送“有效”代码,这将只是 - 也许更 - 像 XSS 代码的更经典的 SQL 注入一样令人讨厌。这涉及到其他问题的其他主题,除了这里讨论的验证只是难题的一部分。