如何解决SQL注入漏洞和SQL优化

摘要

过滤敏感字符的思路非常简单,由于恶意攻击者一般需要在输入框中输入的文本一般含有 or、and、select、delete 之类的字符串片段,所以在拼接 SQL 之前检查用户提交的文本中是否含有这些敏感

最近读了程序员的SQL金典这本书,觉得里面的SQL注入漏洞和SQL优化总结得不错,下面简单讨论下SQL注入漏洞和SQL优化。

1、SQL注入漏洞

由于“’1’=’1’”这个表达式永远返回 true,而 true 与任何布尔值的 or 运算的结果都是 true,那么无论正确密码是什么“Password=’1′ or ‘1’=’1’”的计算值永远是 true,这样恶意攻击者就可以使用任何帐户登录系统了。这样的漏洞就被称作“SQL 注入漏洞(SQL Injection)”。

对付 SQL 注入漏洞有两种方式:过滤敏感字符和使用参数化 SQL。

1) 过滤敏感字符

过滤敏感字符的思路非常简单,由于恶意攻击者一般需要在输入框中输入的文本一般含有 or、and、select、delete 之类的字符串片段,所以在拼接 SQL 之前检查用户提交的文本中是否含有这些敏感字符串,如果含有则终止操作。

2) 使用参数化SQL

为运行时才能确定的用户名和密码设置了占位符,然后在运行时再设定占位符的值,在执行时 Java、C#会直接将参数化 SQL 以及对应的参数值传递给 DBMS,在 DBMS 中会将参数值当成一个普通的值来处理而不是将它们拼接到参数化 SQL 中,因此从根本上避免了 SQL 注入漏洞攻击。

2、SQL 优化

在使用 DBMS 时经常对系统的性能有非常高的要求:不能占用过多的系统内存和CPU 资源、要尽可能快的完成的数据库操作、要有尽可能高的系统吞吐量。如果系统开发出来不能满足要求的所有性能指标,则必须对系统进行调整,这个工作被称为优化。

SQL 优化的基本原则

“二八原理”是一个普遍的真理,特别是在计算机的世界中表现的更加明显,那就是 20%的代码的资源消耗占用了 80%的总资源消耗。SQL 语句也是一种代码,因此它也符合这个原理。在进行 SQL 优化的时候应该把主要精力放到这 20%的最消耗系统资源的 SQL 语句中,不要想把所有的 SQL 语句都调整到最优状态。

索引是数据库优化的最根本的优化方法。

常用的SQL优化方法:

1) 创建必要的索引

2) 使用预编译查询

程序中通常是根据用户的输入来动态执行 SQL 语句,这时应该尽量使用参数化SQL,这样不仅可以避免 SQL 注入漏洞攻击,最重要数据库会对这些参数化 SQL 执行预编译。

3) 调整 WHERE 子句中的连接顺序

DBMS 一般采用自下而上的顺序解析 WHERE 子句,根据这个原理,表连接最好写在其他 WHERE 条件之前,那些可以过滤掉最大数量记录。

比如下面的 SQL 语句性能较差:

SELECT * FROM T_Person WHERE FSalary > 50000 AND FPosition='MANAGER' AND 25 < (SELECT COUNT(*) FROM T_Manager WHERE FManagerId=2);

我们将子查询的条件放到最前面,下面的 SQL 语句性能比较好:

SELECT * FROM T_Person WHERE 25 < (SELECT COUNT(*) FROM T_Manager WHERE FManagerId=2) AND FSalary > 50000 AND FPosition='MANAGER';

4) SELECT 语句中避免使用’*’

SELECT  *比较简单,但是除非确实需要检索所有的列,否则将会检索出不需要的列,这回增加网络的负载和服务器的资源消耗;即使确实需要检索所有列,也不要使用SELECT *,因为这是一个非常低效的方法,DBMS 在解析的过程中,会将*依次转换成所有的列名,这意味着将耗费更多的时间。

5) 尽量将多条 SQL 语句压缩到一句 SQL 中

每次执行 SQL 的时候都要建立网络连接、进行权限校验、进行 SQL 语句的查询优化、发送执行结果,这个过程是非常耗时的,因此应该尽量避免过多的执行 SQL 语句,能够压缩到一句 SQL 执行的语句就不要用多条来执行。

6) 用 Where 子句替换 HAVING 子句

避免使用 HAVING 子句,因为 HAVING  只会在检索出所有记录之后才对结果集进行过滤。如果能通过 WHERE 子句限制记录的数目,那就能减少这方面的开销。HAVING  中的条件一般用于聚合函数的过滤,除此而外,应该将条件写在 WHERE 子句中。

7) 使用表的别名

当在 SQL 语句中连接多个表时,请使用表的别名并把别名前缀于每个列名上。这样就可以减少解析的时间并减少那些由列名歧义引起的语法错误。

8) 用 EXISTS 替代 IN

在查询中,为了满足一个条件,往往需要对另一个表进行联接,在这种情况下,使用 EXISTS 而不是 IN 通常将提高查询的效率,因为 IN 子句将执行一个子查询内部的排序和合并。

9) 用表连接替换 EXISTS

通常来说,表连接的方式比 EXISTS 更有效率,因此如果可能的话尽量使用表连接替换 EXISTS。

10) 避免在索引列上使用计算

在 WHERE 子句中,如果索引列是计算或者函数的一部分,DBMS 的优化器将不会使用索引而使用全表扫描。

11) 用 UNION ALL 替换 UNION

当 SQL 语句需要 UNION 两个查询结果集合时,即使检索结果中不会有重复的记录,如果使用 UNION 这两个结果集同样会尝试进行合并,然后在输出最终结果前进行排序。 因此,如果检索结果中不会有重复的记录的话,应该用 UNION ALL 替代 UNION,这样效率就会因此得到提高。

12) 避免隐式类型转换造成的全表扫描

13) 防止检索范围过宽

如果 DBMS 优化器认为检索范围过宽,那么它将放弃索引查找而使用全表扫描。下面是几种可能造成检索范围过宽的情况: 使用 IS NOT NULL 或者不等于判断,可能造成优化器假设匹配的记录数太多。 使用 LIKE 运算符的时候,”a%”将会使用索引,而”a%c”和”%c”则会使用全表扫描,因此”a%c”和”%c”不能被有效的评估匹配的数量。

转载请注明:程序猿 » 读书笔记之SQL注入漏洞和SQL优化


IT家园
IT家园

网友最新评论 (0)