窥探SQL预编译内幕

Author : kj021320

 

TEAM : I.S.T.O

 

Author_blog: http://blog.csdn.net/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;

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值