Author : kj021320
TEAM : I.S.T.O
Author_blog: http://blog.youkuaiyun.com/kj021320
前言套话:
本来文章打算昨天就写出来,环境没搭好... 迟来的祝福~Mickey 生日快乐!
首先感谢PT007竭力帮我搭环境,可惜最后还是没能用上,还有ISTO团队的幕后成员,AMXSA,SUMMER等...还有我几位好朋友axis,suddy,larry 春节提前快乐!哈!
现在部分大企业 都采用DOTNET或者J2EE,迟点应该都是RubyOnRails,之后的技术越来越成熟,在SQL操作无疑都使用绑定变量,有些人叫预编译,也有些人叫同构SQL,参数追加...
像dotnet j2ee 上这样的使用几乎成了习惯,规范!
早期的ADODB中也一样有参数绑定的方式,只是少人使用Adodb.Command。
PHP后期也推出了MYSQL扩展了mysqli_prepare函数,而对Oracle OCI早早就有提供了
那么到底他们是如何进行操作,到底还会不会存在SQL注射的呢?
首先从数据库技术角度去看这个,几乎是百利而无一害的!(排除某些特殊情况)
OK先来思想教育一下~~
每个数据库中 都有自己的SQL引擎,为什么PostGreSQL MSSQL SYBASE可以执行多语句,而MYSQL ORACLE DB2就不行呢?就是因为他们引擎都有自己的实现方式,各有各的特点优势。
首先把SQL语句读入引擎,然后语法分析,有些是解析 有些是编译(预编译就是这里来)
解析我就不多说了!例如JET的,SQLite的...
那么编译呢?SQL引擎会把整个 语句的结构取出来,然后如果发现有参数的地方就会拿变量代替!整个结构编译为 该数据库能识别的执行指令,存储在SQL缓存池里面
例子
select * from ISTOMEMBER where membername=’kj021320’
这样的语句就好比 一般的C语言语句
if(ISTOMember.membername==”kj021320”)printf(“*”);
以上C代码非常不灵活,如果我换一个判断把kj021320改为amxsa,那你得从新编译生成EXE
现在用预编译,以变量的方式使用
select * from ISTOMEMBERN where membername=?
转为我们熟悉的C
if(ISTOMember.membername==name)println(“*”);
现在应该很好理解了吧?用预编译之后每次再使用同一个语句,只需要换一下条件就OK了,就是上述C语言代码里面的name变量。所以免去了语法分析,优化,编译,这些操作,使则数据库执行非常快...
那么到底我们提交SQL语句中,驱动是做了哪些手脚呢?
现在我来揭晓这个迷!本来打算DB2 PostGreSQL Informix那些数据库都拉上来分析的!后来因为机器环境问题,没下文了!........
我对MYSQL进行分析,第一个拿它开刀是因为他的驱动包开源,而且MYSQL在它们当中比较小
现在我先稿写一个简单调用... 然后跟踪进去
import java.io.*;
import java.sql.*;
public class SQLtrack {
public static void main(String[] args) throws Exception{
Class.forName("com.mysql.jdbc.Driver");
Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql", "kj021320", "I.S.T.O");
PreparedStatement ps=null ;
ps=con.prepareStatement("select * from user where username=?");
ps.setString(1, "hello kj"); //加入断点 跟踪进去
ps.executeQuery();
ps.close();
con.close();
}
}
package com.mysql.jdbc;
public class PreparedStatement extends com.mysql.jdbc.Statement implements
java.sql.PreparedStatement {
public void setString(int parameterIndex, String x) throws SQLException {
// if the passed string is null, then set this column to null
if (x == null) {
setNull(parameterIndex, Types.CHAR);
} else {
checkClosed();
int stringLength = x.length();
if (this.connection.isNoBackslashEscapesSet()) {
// Scan for any nasty chars
boolean needsHexEscape = false;
for (int i = 0; i < stringLength; ++i) {
char c = x.charAt(i);
switch (c) {
case 0: /* Must be escaped for 'mysql' */
needsHexEscape = true;
break;
case '/n': /* Must be escaped for logs */
needsHexEscape = true;
break;
case '/r':
needsHexEscape = true;
break;
case '//':
needsHexEscape = true;
break;
case '/'':
needsHexEscape = true;
break;
case '"': /* Better safe than sorry */
needsHexEscape = true;
break;
case '/032': /* This gives problems on Win32 */
needsHexEscape = true;
break;
}
if (needsHexEscape) {
break; // no need to scan more
}
}
if (!needsHexEscape) {
byte[] parameterAsBytes = null;
StringBuffer quotedString = new StringBuffer(x.length() + 2);
quotedString.append('/'');
quotedString.append(x);
quotedString.append('/'');
if (!this.isLoadDataQuery) {
parameterAsBytes = StringUtils.getBytes(quotedString.toString(),
this.charConverter, this.charEncoding,
this.connection.getServerCharacterEncoding(),
this.connection.parserKnowsUnicode());
} else {
// Send with platform character encoding
parameterAsBytes = quotedString.toString().getBytes();
}
setInternal(parameterIndex, parameterAsBytes);
} else {
byte[] parameterAsBytes = null;
if (!this.isLoadDataQuery) {
parameterAsBytes = StringUtils.getBytes(x,
this.charConverter, this.charEncoding,
this.connection.getServerCharacterEncoding(),
this.connection.parserKnowsUnicode());
} else {
// Send with platform character encoding
parameterAsBytes = x.getBytes();
}
setBytes(parameterIndex, parameterAsBytes);
}
return;
}
StringBuffer buf = new StringBuffer((int) (x.length() * 1.1));
buf.append('/'');
//
// Note: buf.append(char) is _faster_ than
// appending in blocks, because the block
// append requires a System.arraycopy()....
// go figure...
//
for (int i = 0; i < stringLength; ++i) {
char c = x.charAt(i);
switch (c) {
case 0: /* Must be escaped for 'mysql' */
buf.append('//');
buf.append('0');
break;
case '/n': /* Must be escaped for logs */
buf.append('//');
buf.append('n');
break;
case '/r':
buf.append('//');
buf.append('r');
break;
case '//':
buf.append('//');
buf.append('//');
break;
case '/'':
buf.append('//');
buf.append('/'');
break;