我知道单引号很流行对于SQL注入攻击,我强烈不同意不应将其作为有效输入。我主张通过使用准备好的语句(正确地引用值)来真正防止SQL注入,而不是过滤掉看起来像是SQL片段的任何内容。
我的情况:
人名可以包含单引号(例如O'Reilly)
文本字段可以包含单引号(例如我很确定)
数字字段可以包含单引号(EUR 1'000'000)
还有更多
我见过其他一些情况,其中应用SQL注入预防规则出于最简单的原因将有效数据丢弃(名称“ Andreas”被拒绝,因为它包含一个AND,并且纯文本字段中的各种常见单词都被拒绝,因为它们包含关键字“ select”,“ insert”,“ update”或“ delete”)。
安全专业人员的立场是什么在那件事上?
我会因为我说的原因而拒绝对单引号实施输入验证吗?
#1 楼
您应该将输入验证实现为纵深防御方法。因此,输入验证不应成为防范SQL注入的主要手段,而应该是准备好的语句。作为额外的防御措施,应该限制允许的输入。这永远都不应限制功能。如果有合法的用例在输入中包含撇号,则应允许使用。因此,您应该在名称字段,描述,密码中使用单引号,但在数字字段,用户名字段,车牌字段中不允许使用单引号。
在所有输入中均禁止使用单引号是很疯狂的。这破坏了应用程序的功能,甚至不是针对SQL注入的正确解决方案。
请考虑您误解了渗透测试公司的可能性。如果这是他们的严重建议,那么这对渗透测试公司会造成严重影响,我建议您寻找渗透测试合作伙伴,以帮助适当保护您的软件,而不是使其无法使用。
评论
评论不作进一步讨论;此对话已移至聊天。
–Rory Alsop♦
19年2月6日在20:40
+1但是,密苏里州的车牌可能带有撇号。数字不应该存储为文本,或者如果存储为数字(例如,“ comments”字段的一部分),OP会在文化中显示带撇号的数字字符串。假设时要小心。
– Zach Mierzejewski
19-2-13在15:12
#2 楼
在注入攻击的上下文中,这显然是错误的-您的数据库层是否正确处理了字符串,或者没有正确处理。由于撇号在名称和自由文本中都是有效的,所以完全禁止使用撇号会破坏应用程序,而选择性地阻止使用它们不会解决注入问题。但是严格的输入验证在一般原则上是好的做法,并且在撇号不是合法值的情况下,过度放任是没有意义的。您给出
EUR 1'000'000
的示例,它是一种特定于语言环境的格式(仅瑞士,AFAIK)-但是,将该格式作为值的一部分是没有意义的。如果用户输入1,500
,您的应用程序应该按原样存储吗?您是否必须在每次处理时都决定将其解释为1.5还是1500?在客户端处理特定于区域设置的表示,并在内部以规范形式处理数字值会更有意义。因此,此处的答案取决于审计是否在抱怨特定的设置有意义的字段,或建议全面禁止使用撇号。如果是前者,那是合理的观点。如果是后者,则它们是愚蠢的,并且可能会盲目地遵循清单。
评论
请注意,在特定于语言环境的格式情况下,最好的答案可能不是不允许它,而是以某种方式解析它并生成没有它的数字。
– jpmc26
19年2月9日在6:58
@ jpmc26数字很容易:用户提供的数字输入字符串可以包含整数或十进制数字。对于第一个,只需删除所有非数字并继续。对于小数,我通常会从右到左查找第一个点或逗号,然后将其余字符删除,然后将其替换为需要的小数点分隔符并继续。
– beppe9000
19年2月9日在17:25
@ beppe9000甚至更容易使用解析库。但是,它可能需要是前端才能使用用户的语言环境。
– jpmc26
19年2月9日在22:32
@ jpmc26是的,本地化属于前端。数据获取只关心规范化:服务页面时,可以使用js库根据用户的偏好设置字符串格式(无论是存储在db中还是作为cookie存储)。
– beppe9000
19年2月10日14:56
@ beppe9000那么,如何在没有语言环境信息的情况下解释“ 1,500”呢?一千五百,还是一个半?
–古兰经
19年2月13日在9:02
#3 楼
步骤1)参数化SQL。步骤2)确保使用SQL DB连接库设置参数值,而不仅仅是设置它们的内联。这是针对SQL注入的实际防御措施。
步骤3)不要在SQL中进行查询构建。
步骤4)添加一个配置开关,将错误一直传播到用户。在测试过程中将其打开。
步骤5)告诉渗透测试人员找到一种生成带有单引号奇数或关闭的SQL错误的方法。
评论
为第3步+1)。绝对的真理。
–bytepusher
19年2月12日在20:22
#4 楼
准备好的语句(带参数的查询)非常有用,只需确保正确实现即可。我已经看到了“准备好的语句”实现,它们都一样脆弱。对于实现细节的讨论,我建议堆栈溢出。纵深防御也没有错(在这种情况下为输入验证),但是做得很好...拒绝所有单引号可能不是最佳实践:)
评论
评论不作进一步讨论;此对话已移至聊天。
–Rory Alsop♦
19年2月9日,11:40
#5 楼
我不是安全人员。我是一个必须维护安全代码的程序员。这就是我所谓的“脆性”练习。入口点分散在整个典型项目中。找到并清理所有这些问题仅是解决单个问题的大量工作,需要进行大量的精心维护和麻烦以确保其在代码更改时仍然有效,并且充满了使之无效的假设。而是使用更易于维护,分层,关联和解决大量问题的实践。这样一来,您就不需要昂贵的,过于宽泛的过滤。
如果您不知道输入将如何使用,就无法保证输入的安全。
假设您通过从所有输入中去除所有单引号来“保护”您的系统。太好了,您可以抵御一种类型的SQL注入攻击。如果在...中使用该输入...
允许双引号的MySQL查询
文件系统操作
Shell命令
网络查询
>方法名称
类名称
eval
每个方法都有不同的特殊字符,转义序列,引用规则和安全性惯例。您可能无法预测输入内容将如何使用。试图删除所有特殊字符是疯狂的,只能“解决”一类攻击。
如果用户是允许输入页数限制。该限制在参数化查询中应有尽有;没有SQL注入,是的!用户输入
9999999999
,现在您就容易受到DOS攻击。必须在执行可能不安全的操作时应用适当的安全措施。这考虑到了操作独特的许多因素。清理输入字符只是一个。
,只要您这样做,还可以参数化查询。这样一来,就不再需要做所有的工作和覆盖剥皮引号的损坏。
很难过滤所有输入。
在给定项目中有很多很多方法来获取和传递输入:
形式输入
urls
文件名
文件内容<数据库查询
网络读取
环境变量
这些通常是非常自由的形式,可以使用许多不同的库。我不知道有任何静态分析工具可以验证所有潜在的易受攻击的输入是否已通过过滤。某些语言具有
taint
系统,但很难有效使用。即使您过滤了所有输入,如果没有静态分析工具,未过滤的输入也会随着开发的进行而泄漏回来。要维护不完整,昂贵的结果会付出很多努力,这会妨碍功能。相反,项目中通常只有一种执行SQL的方法。存在静态和运行时工具来自动检测潜在的SQL注入。您甚至可以完全禁止使用字符串,并要求所有查询均为SQL查询对象。这些良好的做法易于维护,并且越来越多地嵌入到工具和SQL库中。
“防火墙”导致宽松的安全性。
类似于某些办公网络的非常不安全的做法因为“我们有防火墙”,团队就有可能因为“输入安全”而懒于保护代码。输入的内容绝对是不安全的。
机会成本
有人可能会说“为什么不能同时使用?”您只有很多时间来从事一个项目。低效率,高维护实践是很麻烦的。实施和维护它会花费您有限的时间来避免更高效,更易于维护的实践。在最坏的情况下,您将花费大量时间使用输入打怪,以及过分过滤导致的后续问题,以致您将永远没有时间采取适当的安全措施。
简而言之,输入过滤昂贵,泄漏,难以维护,无法解决问题,并且可能使情况变得更糟。
#6 楼
就像您自己说的那样,如果您使用的是参数化查询,那么单引号就不成问题。在这种情况下,您可以拒绝他们的建议。如果这样做,请突出显示您正在使用参数化查询的事实,这也有助于提高可用性(使用先前的示例)。#7 楼
如果您100%确保始终在所有地方都防止SQL注入,这确实是胡说八道。但是,SQL注入是最常见的安全风险之一,即使您确定自己已经如果正确编写了应用程序以使用参数,那么草率的DBA可能会执行查询,该查询可能会受到二阶SQL注入的威胁。它甚至可能不会存储在任何地方,而可能只是复制表的查询。
二阶攻击更难以执行,但更难以防范。防范二阶攻击意味着需要检查在数据库上运行的具有写权限的每个动态SQL语句是否存在SQL注入的风险,不仅是处理来自不受信任来源的输入的SQL语句。
不允许到处都有引号是对二阶攻击的草率保护,但确实使它们不太可能。在理想的世界中,这不是必须的,但是不幸的是我们并不生活在一个世界中。
如果许多用户对数据库具有任何形式的写访问权限,并且能够编写自己的SQL语句,则这可能是明智的安全措施。如果您的应用程序是访问数据库的唯一方法,并且只有非常有知识的用户可以执行具有写访问权的自己的查询,则通常没有必要。
评论
诚然,我一般并不精通渗透测试或PL / SQL(我认为这是关于)的,但是二阶SQL注入似乎真的很可怕。如果我们共同了解到,在用户输入上使用eval()不是一个好主意,那么为什么在PL / SQL中使用这种构造呢?我希望在链接后面有另一种方法可以执行该查询,该查询确实尊重代码和数据之间的差异。
–涂药
19年2月5日,9:53
@tomsmeding在大多数SQL方言中都可以进行二阶注入(T-SQL通过sp_executesql,PL / SQL通过EXECUTE IMMEDIATE,MySQL通过EXECUTE)。它们都支持参数,因此您可以正确地进行操作,但是存在局限性。例如,我已经将Access SQL枢纽查询迁移到T-SQL,并且在T-SQL中执行此操作的唯一方法是使用字符串连接来实现动态字段名称,就像Access一样(带有转义字符,但是转义字符是不好的) ,并且我遇到了没有它们的示例脚本),因为您不能使用参数作为字段名称。
– Erik A
19年2月5日在10:03
@tomsmeding:除非情况有所变化,否则没有任何好方法可以执行“ select * where id is in [list]中的形式”而不使用动态生成的SQL来指定列表。我不会尝试使用列表中用户指定的项目进行这种操作,但是使用数字数据类型处理数字ID时,我看不到任何注入风险。
–超级猫
19年2月5日在19:10
PostgreSQL提供了更好的防御措施,可以防止注入到EXECUTE IMMEDIATE查询中:一组可以正确引用输入的函数。它们是quote_ident,quote_literal和quote_nullable。显然,避免使用EXECUTE IMMEDIATE是首选方法,但是如果您必须使用EXECUTE IMMEDIATE,那么这些方法在杀菌方面更胜一筹。
– jpmc26
19年2月6日在21:58
@supercat我不特别了解psycopg2,但我确实知道某些客户端库将数组扩展为某些数据库的单个参数,例如IN(:p1,:p2,:p3 ...)。所以这至少是一种技术。
– jpmc26
19年2月6日在22:01
#8 楼
虽然我不知道您的应用程序的细节,但是我会遵循您的说法。有些字段不需要包含某些字符。在这些字段中,您可以使用输入验证来过滤单引号(和双引号以及其他内容)。
如果转义不能正确运行,输入验证可能是一种缓解策略,但是可以(正确地)准备好的语句应该是减轻SQL注入风险的首选方法。
#9 楼
如果这是真正的渗透测试的结果,那么他们应该能够为您提供一个值得证明的价值,以证明这是一个可利用的问题。如果他们不能,那么我建议您进行适当的渗透测试,以证明它们可以被利用。但是,如果这是通用漏洞扫描的结果,那么我希望这样的模糊通用响应,那只是标记能够插入单引号。在这种情况下,如果您对没有问题感到满意,那么您可以很高兴地忽略该结果。
评论
这个答案实际上并没有解决这个问题。问题不在于测试方法,而是如何过滤。请确保您的答案直接解决了该问题。我们喜欢不同的观点和信息,但这似乎是切线。
– schroeder♦
19年2月5日,12:14
@schroeder:他用两种方式说:“所以您可以输入单引号,现在漏洞在哪里?”第二个挑战是过滤掉单引号对安全性有好处的框架。
–约书亚
19年2月5日在16:36
@schroeder问题是“对于渗透测试该报告我该怎么办?”这个答案是“检查它是否是真正的漏洞,否则,将其视为误报”。我不确定为什么会切线。
–IMSoP
19年2月8日在14:28
@IMSoP根本不是问题,这是问题的元抽象。如果这实际上是问题,那么OP会自行回答(OP会概述此答案)。
– schroeder♦
19年2月8日在14:33
@Joshua OP专门以非常详细的方式来挑战框架,因此,我不确定这是如何提供的。
– schroeder♦
19年2月8日在14:34
#10 楼
从前Web开发人员到现在的笔测试人员,我都不希望限制用户的输入,但这可能是一个主要问题。我知道我自己就是使用这种技术来破坏Web应用程序和数据库的。我的看法是检查您的数据库安装和Web语言(php)配置以处理转义字符,然后对模块进行编码)以迭代输入以确保其格式正确。
撇号可以是有效输入,但也可以避免传递数据库语句并引入攻击向量。 DB和Web语言具有可以处理这些类型实例的模块,但是编写自己的模块进行仔细检查仍然是一个好主意。
评论
OP已正确地对所有参数进行了参数化,因此转义字符不应在任何地方使用。而且,即使正确实施排序,它们也不总是提供安全性。
– Erik A
19年2月4日在15:54
同意,根据您使用的堆栈和框架,进行其他完整性检查可能会很有用。就我而言,我在带有DataParameters的JPQL查询的Spring Data JPA上将Java / JDBC与Prepared Statements一起使用-这些东西几乎是牢不可破的。
– Peter Walser
19年2月4日在16:08
#11 楼
我认为这是一个观点问题。您的系统具有应首先满足的功能要求。这并不意味着不值得考虑输入验证的问题。顺便说一句,两者并不直接矛盾。我不建议这样做,但是可以在前端进行编码和解码,并让SQL存储和查询使用编码形式。没有提到的事情。您可以尝试让他们澄清。他们可能有很好的理由,但可能没有。#12 楼
我会与大多数其他当前答案相矛盾。您的渗透测试者几乎可以肯定100%正确。已发现您的应用程序在没有有效的撇号的情况下接受并回了撇号。
您的用户名,电话号码,域名和日期可能都应具有特定的格式和字符范围。所有人都应该没有撇号。但是,相反,对于所有这些字段,您只接受它们给您的任何旧字符串。
如果这个假设是正确的并且在输入中撇号将无效,那么:
是的,您应该拒绝无效的完全输入。
您不应该允许人们使用损坏的数据来污染数据库。
您不应尝试在显示时清理该数据,其数量应超出您的期望。尝试清理
<script>
,因为攻击者只会找到一种利用清理算法的方法,例如<script >
或<scr<script>ipt>
或+ADw-script+AD4-
或其他。您肯定有例外,但是应该将它们明确定义为特殊情况:
除非您的要求明确要求处理带有撇号的瑞士地区货币, “ 1'000'000”的整数不再是“ 1〜000〜000”或“ 1banana000apple000”的有效整数。拒绝它。不要尝试清除“ 1'000”,您不知道它们的意思是1,000还是1.000或14000还是一个英尺或一个度数或完全不同的东西。
查询参数化仅避免大多数类SQL注入:不是所有可能的滥用无效数据。
但是依赖于用户名符合公司标准的所有其他系统又如何呢?你刚刚把它们弄坏了。
用户是否有主目录?那会是个问题。
他们的名字会被记录在任何地方吗?
他们曾经被显示吗?
用户名是否曾经在系统调用的命令参数中使用?
他们曾经吗?发送AJAX或XML数据吗?
是否有对批处理用户名运行的批处理模式操作,例如名称从A到M的一天开始,从N到Z的第二天开始,第三天的0-9,然后重复?这些批次永远不会对您的用户不利。
在某些情况下冒充另一个用户对某人有害或有用,因此您希望他们所有人都具有唯一的名称吗?但是通过注册JohnͿο𝗁ո𝗁ոоհ𝗇或like之类的东西,他们可以冒充用户John。
所有使用该名称的系统都不能拒绝您提供的值。相反,它们将不得不处理无效的输入,尝试清除或转义它们,即使我们已经表明这是不安全的并且注定要失败。但是,由于该用户已经注销很长时间了,因此他们不会看到要求输入新电子邮件地址的任何错误。
您的DBA现在在他的数据库中有废话。这不仅使他在日常工作中,而且在他不得不迁移数据时,都会给他带来很多痛苦,因为目标系统中任何更严格的约束都会破坏旧数据。
您的同事和其他消费者您的数据现在必须验证他们从数据库读取的所有内容,因为他们甚至不相信您已执行了完全记录的标准。
您的用户现在使用的是安全性较低的系统。
您现在必须去做所有这些输入,以确保您不再将垃圾输入数据库。
查询参数化不是正确地清理输入的替代方法。
评论
让我们继续聊天中的讨论。
– Dewi Morgan
19年2月6日在18:56
看来我的评论在大规模清理中被删除了,因此对于那些不清楚为什么这个答案会被否决的人,我将重申:清理输入并不能替代正确处理输出数据。在此答案给出的示例中,只有模拟情况才可以在仅输入时实施。只有在处理数据时转义或过滤数据,才能正确处理其他每个数据。输入验证对于向用户提供反馈很有用,但是只有在其他地方做错了时,它才会提高安全性。
–IMSoP
19年2月9日在14:38
查询参数化,以及在任何其他使用数据的地方都等效的参数,是清除输入的一种替代方法。的确,如果您的目标是安全性,那么它是一个更好的选择。说“您的用户名一定不能包含撇号”可能会让您感到放心,但是对攻击者说的就是“他们必须在某个地方的原始查询中使用此名称,我应该找到一种方法诱骗他们创建带有用户名的用户。撇号”。除非您可以100%确保您的验证能够解决所有问题,并且无法绕过它,否则您在使用时必须防范不良数据。
–IMSoP
19年2月11日在17:47
从根本上说,将输入清理作为安全措施的问题在于没有“安全数据”之类的东西。安全性取决于您使用它的上下文。由于您无法预测数据生命周期中将要拥有的所有可能的上下文,因此可以通过将每个字段限制为A-Z,a-z,0-9来限制功能和可用性。或者,您也可以将清理工作视为一项不完整的措施,并在遇到每种情况时将其写入“真实”安全性作为后盾。
–IMSoP
19年2月11日在18:08
我看不到范围批处理问题将如何“不受其他两个轴的影响”。如果您知道您可以100%相信所有输入都是字母数字,那么所描述的算法是可以接受的;但是在极不可能的情况下,您需要添加一个警告操作员的断言或“及其他”子句。因此,最安全的解决方案是最接近处理的解决方案,而不是十年前信任某个输入形式的已预测方案的解决方案。
–IMSoP
19年2月11日在22:43
评论
评论不作进一步讨论;此对话已移至聊天。如果您的测试人员认为阻止用户提交撇号会以某种方式阻止sql注入攻击,我会感到担心...
我同意你的逻辑。我会让他们尝试在UAT / QA环境上执行sql注入攻击。他们还需要了解用户输入并不危险,这是因为它的处理方式使其具有潜在的危险。