为什么占位符可以防止sql注入?

Posted GreatAnt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么占位符可以防止sql注入?相关的知识,希望对你有一定的参考价值。

先看下面用占位符来查询的一句话

String sql = "select * from administrator where adminname=?";
psm = con.prepareStatement(sql);

String s_name ="zhangsan\' or \'1\'=\'1";
psm.setString(1, s_name);

假设数据库表中并没有zhangsan这个用户名,

用plsql运行sql语句,可以查出来所有的用户名,但是在Java中并没有查出任何数据,这是为什么呢?

首先,setString()的源码中只有方法名字,并没有任何过程性处理,

那么答案肯定出现在Java到数据库这个过程中,也就是mysql和oracle驱动包中,在mysql驱动包中,PreparedStatement继承并实现了jdk中的setString方法,

也就是原因在于数据库厂商帮你解决了这个问题,下面就看看这个方法的具体实现:

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 {
            StringBuffer buf = new StringBuffer((int) (x.length() * 1.1));
            buf.append(\'\\\'\');

            int stringLength = x.length();

            //
            // 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;

                case \'"\': /* Better safe than sorry */
                    if (this.usingAnsiMode) {
                        buf.append(\'\\\\\');
                    }

                    buf.append(\'"\');

                    break;

                case \'\\032\': /* This gives problems on Win32 */
                    buf.append(\'\\\\\');
                    buf.append(\'Z\');

                    break;

                default:
                    buf.append(c);
                }
            }

            buf.append(\'\\\'\');

            String parameterAsString = buf.toString();

            byte[] parameterAsBytes = null;

            if (!this.isLoadDataQuery) {
                parameterAsBytes = StringUtils.getBytes(parameterAsString,
                        this.charConverter, this.charEncoding, this.connection
                                .getServerCharacterEncoding(), this.connection
                                .parserKnowsUnicode());
            } else {
                // Send with platform character encoding
                parameterAsBytes = parameterAsString.getBytes();
            }

            setInternal(parameterIndex, parameterAsBytes);
        }
    }

所以转义后的sql为\'zhangsan\\\' or \\\'1\\\'=\\\'1\';这个时候是查不出来的。

以上是关于为什么占位符可以防止sql注入?的主要内容,如果未能解决你的问题,请参考以下文章

#{} 和 ${} 的区别---使用 ${} 会有sql注入的问题

SQl语句中使用占位符的优点

占位符,SQL注入?

Jfinal数据库操作语句中占位符的使用

Sql语句占位符?的使用

Mybatis 中 sql 语句的占位符 #{} 和 ${}