再说SQL预编译:
最近用go语言时,学习了一下数据库连接的库,这里总结一下SQL预编译相关的知识。貌似网上都是建议使用预编译,我也觉得这种做法靠谱。
先谈 SQL注入:
SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过让原SQL改变了语义,达到欺骗服务器执行恶意的SQL命令。其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。
其实,反过来考虑,这也是SQL这类解释性语言本身的缺陷,安全和易用性总是相对的。类似的ShellShock也是一种注入攻击,可以看《》。
一个经典注入:
记得以前看Hack技术资料时,有一个经典的万能密码,’or’=’or’。
01 | ResultSet rs=sm.executeQuery( "select * from tb_usertable where name='" +u+ "'and password='" +p+ "'" ); |
05 | response.sendRedirect( "wel.jsp" ); |
09 | response.sendRedirect( "login.jsp" ); |
这是以前很常见的登录判断写法。这和直接在数据库客户端输入SQL没什么区别,如果SQL注入成功的利用SQL拼接改变了SQL的语义,极有可能中招。现有的程序都会严格的检查传入的参数。在这里,’or’=’or’发挥了它的作用。
我们来看验证页面里面的SQL查询。
1 | select * from tb_usertable where name = '"+u+"' and password = '"+p+"' " |
2 | --如果我们把[' or '1' = '1]作为password传入进来.用户名随意,看看会成为什么? |
3 | select * from tb_usertable where name='" +HEHE+"' and password = '1' or '1' = '1' |
这使得’or’=’or’成为了万能密码。
再说SQL预编译(PreparedStatement):
前面说了,SQL之所以能被注入,最主要的原因就是它的数据和代码(指令)是混合的。
其实我们想一下,C程序为什么从来没听说过注入这种说法,有的也是溢出。这是因为C是一种编译型语言,你没法在语义上欺骗它,语义解析这步提前做了,都生成二进制了。所以攻击C的方式大多是溢出,通过溢出让数据覆盖指令段。
数据库也提供了这种分离数据和代码(指令)的方式,就是SQL预编译。而SQL语句在程序运行前已经进行了预编译,在程序运行时第一次操作数据库之前,SQL语句已经被数据库分析,编译和优化,对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or ‘1=1’也数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令。
预编译语法:
1 | PREPARE stmt_name FROM preparable_stmt; |
2 | EXECUTE stmt_name [USING @var_name [, @var_name] ...]; |
3 | { DEALLOCATE | DROP } PREPARE stmt_name; |
预编译的优点:
PreparedStatement可以尽可能的提高访问数据库的性能,我们都知道数据库在处理SQL语句时都有一个预编译的过程,而预编译对象就是把一些格式固定的SQL编译后,存放在内存池中即数据库缓冲池,当我们再次执行相同的SQL语句时就不需要预编译的过程了,只需DBMS运行SQL语句。
所以当你需要执行Statement对象多次的时候,PreparedStatement对象将会大大降低运行时间,特别是的大型的数据库中,它可以有效的也加快了访问数据库的速度。
注意:
理论上,SQL预处理对注入的解决是釜底抽薪的。但貌似有的预处理依然是平凑的方式,仅仅是加了过滤。这还需要数据库支持。
转载请注明: »