Oracle 12c中SQL注入攻击的防范 Author: Bo Tang Meetup事件的原始链接:https://www.bagevent.com/event/1542303?from=timeline 现场灯箱路牌: 嘉宾介绍: 唐波 2018.7 福州 感谢:Arup Nanda,Brendan Tierney,Heli Helskyaho,Martin Widlake, Alex Nuijten,Chet Justice和盖总 www.acoug.org www.secoug.org
2. 输入字符串的清洁度 3. 减少SQL注入的可能性 您极有可能已经听说过SQL注入攻击(SQL injection attack)这个术语。这是一种方法:不怀好意的用户,通过这种方法让程序比要求它做的多做一些操作的方式向一个貌似无害的数据库程序传递进一个特别编 制的参数。这通常是针对SQL数据库,但是攻击也可能来自任何其他语言。 请注意:我们添加了一小段跟踪语句以显示程序中实际构建出来的动态语句。 让我们看一下:当用户输入错误的密码时会发生什么? 当然,它将无法匹配到存储于表中的密码。因此,该函数拒绝了请求。如果该用户传递了正确的密码,他将得到正确的结果: 现在,假设一名不怀好意的用户,他并不知晓密码(因此传递了一个错误的密码),但是他传递了像以下这样的参数: 这是一个巨大的安全漏洞,并且不能被忽略。令人欣慰的是,在本案例中,修补工作相对容易。因为我们就是要去检查密码,所以可以使用一个占位变量,并且在运行时给它绑定值。此处是该函数现在的样子: 现在,如果EXECUSER再次执行该函数,该用户将得不到任何结果: 为什么会这样?答案位于动态构建的字符串l_stmt中。明确显示出密码列要与一个值相匹配;谓词本身不会像在上一个例子中那样被扩展。此处是要与密码列作匹配的完整的值: 当然,它将无法匹配到存储于表中的密码。因此,该函数拒绝了请求。如果该用户传递了正确的密码,他将得到正确的结果: 就像所看到的那样,技巧就位于语句是如何被构建的方式之中。因为参数被用作绑定变量的值而不是被当作文本串,所以攻击者在谓词中传递的带有额外内容 的字符串根本不会改变查询;而是:查询接受整个参数作为密码的可能的值。这样当然匹配不到密码,因此会报告“密码无法匹配得到”。我们解决了这个安全问 题。 小贴士 任何时候,当在PL/SQL中构建要被PL/SQL程序执行的动态生成的语句时,只要有可能都要使用占位符。以避免发生字符串连接。 2. 输入字符串的清洁度 DBMS_ASSERT包的更多内容 注入的对象 日期注入 对匿名块注入 3. 减少SQL注入的可能性 在之前的案例中,在PL/SQL代码中使用了一个绑定值作为占位符。之所以能够这样去做是因为输入值相对比较简单,并且作为绑定变量来传递比较容易 被接受。但是在一些场景中,却不可行。从现在开始,将会遇到许多场景。在其中需要作为参数接受复杂得多的字符串,并且将位于要生成的SQL语句的许多部 分,而不是仅仅只是一个值。在这种场景中,不得不组合连接各个部分为一个整体去动态地构建SQL语句。在那些场景中,如果能够审查输入的参数值并确保其中 不包含有任何攻击元素,那么也能保护代码。 依靠人力检查会立即发现字符串已经被注入串所污染,但是在运行时对所有参数值进行人力检查是不可能的。可以编写复杂的程序在运行时进行输入检查。事 实上,那正是保证注入值不会生效的办法。对需要这样一个检查程序的事实,请不要感到沮丧。好消息是Oracle已经提供了这样的工具。它是一个名叫 DBMS_ASSERT的包。 当EXECUSER使用一些注入串来执行该函数时,会报错: 请注意输出中已经有了单引号围绕在两边。因此,当构建将要执行的语句时,不再需要添加单引号。 当使用正确的密码时, 他将如预期的那样得到正确的结果: 2. 输入字符串的清洁度 DBMS_ASSERT包的更多内容 注入的对象 日期注入 对匿名块注入 授予用户EXECUSER执行权限: 在那个方案中创建另一个表用以存储信用卡号: 让我们向表中插入一些数据行。只有少于20人被插入。 这是拒绝服务攻击(denial of service attack)的近亲。 为了避免这种风险,需要弄清楚所传递进来是单个实际对象的名字,而不是表、视图、函数或者更糟糕的是能返回结果的子查询的组合。在DBMS_ASSERT包中的另一个名叫sql_object_name的函数的帮助之下,这种检查相当容易进行。 类似地,DBMS_ASSERT包中的其他函数检查其他方面的东西。比如:schema_name函数检查数据库中有效的用户方案名。只要您愿意,可以自己探索这些。这里介绍的这两个函数是最经常用到的。 <--- 牛津现代英汉双解词典 ---> assert / E5s\:t; E`s[t/ v 1 [Tn] (a) make others recognize (sth) by behaving firmly and confidently 坚定而有信心地使别人认识到(某事物); 坚持: assert one's authority, independence, rights 坚持自己的权威性, 独立性, 权利. (b) ~ oneself behave in a confident manner that attracts attention and respect 表现出自信而受到注意和尊重: You're too timid you must try to assert yourself more. 你太畏缩了--要尽量增强自信心. 2 [Tn, Tf] state (sth) clearly and forcefully as the truth 清楚而有力地表明(某事物)为事实; 声称; 断言: She asserted her innocence/that she was innocent. 她坚称自己很清白[她是无辜的]. 作为SCHEMAUSER,创建一个类型来存放账号的列表: 授予EXECUSER用户执行它的权限: 现在,EXECUSER用户能够像以下这样 执行这个函数: 很明显位于被执行的语句中。 NLS设置以某种方式修改了日期的输入。 而这个输入本应该是: 很明显位于被执行的语句中。 NLS设置以某种方式修改了日期的输入。 而这个输入本应该是: 在查询中显式地使用日期格式强制该函数去拒绝在所输入的日期值中发生的改动(作为NLS设置的结果)。当在表中检查日期列时,这种检查将总是对格式化过的列来进行。 此处是输出: 看起来相当无害,不是吗?然而,一名不怀好意的开发人员能够利用它,让我们称为“有创意性地利用”,向参数中注入一些不需要的代码(加粗显示): 为了要保护这段代码,不得不去做与之前曾用dbms_assert.enquote_literal所做的一样的测试。请记住,该检查过程将生成一个有单引号围绕于周围的字符串。因此,当要构建用于执行的语句时,将不得不移除任何已经放置的单引号。此处是改动过的PL/SQL块:
3. 减少SQL注入的可能性 尽量避免在PL/SQL代码中动态地构建SQL语句。 因为可能会不可避免地要使用动态构建的SQL语句,所以当必须使用到它们时,无论什么时候,只要有可能都要使用绑定变量而不是使用字符连接。 充分使用DBMS_ASSERT包 当需要在应用中检查密码时,不要直接检查该列的值。而是应该写一个函数,该函数在密码是正确的时候返回布尔值。 在执行完检查之后,如果密码是错误的,不是报告“Password is incorrect,”而是应该报告“User ID or password is incorrect.” 对于DATE或者与时间戳相关的数据类型的参数而言,首选的倾向就是总是使用绑定变量。如果不可能那样做,那么请在代码中使用格式掩码。不要使用日期或者时间的默认格式。 谢谢 |
GMT+8, 2022-3-22 12:12 , Processed in 0.039158 second(s), 20 queries .