美文网首页工作生活
防止sql注入的方法和原理

防止sql注入的方法和原理

作者: 赵帅_a4fe | 来源:发表于2019-07-08 17:47 被阅读0次
sql注入
  • 前端过滤,输入框中过滤特殊字符,或者限制输入的字符集合

  • 代码层手动过滤,同上

  • 利用连接池组件过滤,比如druid的WallFilter

  • 使用预编译,比如 jdbc 的preparedStatement

  • 使用存储过程,只传入参数

这里详细说下mysql jdbc防止sql注入的原理
1.代码层手动过滤
首先看下com.mysql.jdbc.PreparedStatement的setString 源码,可以看到setString对参数做了一定的处理,比如增加单引号,对换行符等一些字符做了转义处理,也就是说mysql jdbc 封装了一些代码层手动过滤,
经过setString 方法对输入字符串参数进行处理后,消除了一部分sql注入的威胁

public void setString(int parameterIndex, String x) throws SQLException {
        synchronized(this.checkClosed().getConnectionMutex()) {
            if (x == null) {
                this.setNull(parameterIndex, 1);
            } else {
                this.checkClosed();
                int stringLength = x.length();
                StringBuffer buf;
                if (this.connection.isNoBackslashEscapesSet()) {
                    boolean needsHexEscape = this.isEscapeNeededForString(x, stringLength);
                    Object parameterAsBytes;
                    byte[] parameterAsBytes;
                    if (!needsHexEscape) {
                        parameterAsBytes = null;
                        buf = new StringBuffer(x.length() + 2);
                        buf.append('\'');
                        buf.append(x);
                        buf.append('\'');
                        if (!this.isLoadDataQuery) {
                            parameterAsBytes = StringUtils.getBytes(buf.toString(), this.charConverter, this.charEncoding, this.connection.getServerCharacterEncoding(), this.connection.parserKnowsUnicode(), this.getExceptionInterceptor());
                        } else {
                            parameterAsBytes = StringUtils.getBytes(buf.toString());
                        }

                        this.setInternal(parameterIndex, parameterAsBytes);
                    } else {
                        parameterAsBytes = null;
                        if (!this.isLoadDataQuery) {
                            parameterAsBytes = StringUtils.getBytes(x, this.charConverter, this.charEncoding, this.connection.getServerCharacterEncoding(), this.connection.parserKnowsUnicode(), this.getExceptionInterceptor());
                        } else {
                            parameterAsBytes = StringUtils.getBytes(x);
                        }

                        this.setBytes(parameterIndex, parameterAsBytes);
                    }

                    return;
                }

                String parameterAsString = x;
                boolean needsQuoted = true;
                if (this.isLoadDataQuery || this.isEscapeNeededForString(x, stringLength)) {
                    needsQuoted = false;
                    buf = new StringBuffer((int)((double)x.length() * 1.1D));
                    buf.append('\'');

                    for(int i = 0; i < stringLength; ++i) {
                        char c = x.charAt(i);
                        switch(c) {
                        case '\u0000':
                            buf.append('\\');
                            buf.append('0');
                            break;
                        case '\n':
                            buf.append('\\');
                            buf.append('n');
                            break;
                        case '\r':
                            buf.append('\\');
                            buf.append('r');
                            break;
                        case '\u001a':
                            buf.append('\\');
                            buf.append('Z');
                            break;
                        case '"':
                            if (this.usingAnsiMode) {
                                buf.append('\\');
                            }

                            buf.append('"');
                            break;
                        case '\'':
                            buf.append('\\');
                            buf.append('\'');
                            break;
                        case '\\':
                            buf.append('\\');
                            buf.append('\\');
                            break;
                        case '¥':
                        case '₩':
                            if (this.charsetEncoder != null) {
                                CharBuffer cbuf = CharBuffer.allocate(1);
                                ByteBuffer bbuf = ByteBuffer.allocate(1);
                                cbuf.put(c);
                                cbuf.position(0);
                                this.charsetEncoder.encode(cbuf, bbuf, true);
                                if (bbuf.get(0) == 92) {
                                    buf.append('\\');
                                }
                            }
                        default:
                            buf.append(c);
                        }
                    }

                    buf.append('\'');
                    parameterAsString = buf.toString();
                }

                buf = null;
                byte[] parameterAsBytes;
                if (!this.isLoadDataQuery) {
                    if (needsQuoted) {
                        parameterAsBytes = StringUtils.getBytesWrapped(parameterAsString, '\'', '\'', this.charConverter, this.charEncoding, this.connection.getServerCharacterEncoding(), this.connection.parserKnowsUnicode(), this.getExceptionInterceptor());
                    } else {
                        parameterAsBytes = StringUtils.getBytes(parameterAsString, this.charConverter, this.charEncoding, this.connection.getServerCharacterEncoding(), this.connection.parserKnowsUnicode(), this.getExceptionInterceptor());
                    }
                } else {
                    parameterAsBytes = StringUtils.getBytes(parameterAsString);
                }

                this.setInternal(parameterIndex, parameterAsBytes);
                this.parameterTypes[parameterIndex - 1 + this.getParameterIndexOffset()] = 12;
            }

        }
    }

2.预编译功能
预编译的功能是在服务端开启的,mysql预编译功能默认是关闭的,通过对jdbc url 追加以下参数来开启 useServerPrepStmts=true&cachePrepStmts=true

参数名称 说明
useServerPrepStmts 开启预编译功能,开启后客户端会发送prepare语句到l服务端
cachePrepStmts 服务端缓存预编译结果,不开启的话,即使相同的sql每次都需要重新预编译,开启后有助于性能提升

开启上述两个功能之后,服务端预编译完成后会存储预编译结果,并且将预编译结果对应的key回传到jdbc客户端,jdbc客户端将key进行缓存,使用时发现相同的sql,就会直接使用缓存的key,将key和参数发送到服务端,从而实现相同的sql不需要编译多次

开启预编译以后,含有占位符的sql会被先发送到服务端,进行语法,词法等分析,生成预编译结果,预编译结果就类似方法一样,方法内部的查询哪张表,使用哪个字段查询,这些逻辑已经确定了,只需要传入响应的参数即可,这个预编译结果就是防止注入的关键所在,由于已经经过了语法词法分析,sql的执行路径已经完全确定,当含有注入风险的参数传入时,参数只当作数据来处理,不会对参数进行语法词法分析,也就不能影响sql的执行路径,因此能够完全的防止sql的注入

相关文章

  • 防止sql注入的方法和原理

    前端过滤,输入框中过滤特殊字符,或者限制输入的字符集合 代码层手动过滤,同上 利用连接池组件过滤,比如druid的...

  • idea搭建 mybatis框架

    {}方式能够很大程度防止sql注入,${}方式无法防止Sql注入。 SELECT deptno,dname,loc...

  • php面试题

    防止SQL注入简单回答:防止——利用即有的应用、功能,将(恶意)SQL命令发送到到后台数据库引擎。 防止SQL注入...

  • 谈谈sql注入之防范(四)

    了解了SQL注入的方法后,如何能防止SQL注入?如何进一步防范SQL注入的泛滥?怎么通过一些合理的操作和配置来降低...

  • 防止sql注入的方法

  • Mybatis如何防止SQL注入

    Mybatis如何防止SQL注入 什么是SQL注入 sql注入是一种代码注入技术,将恶意的sql插入到被执行的字段...

  • sql注入风险

    SQL注入攻防入门详解 如何从根本上防止 SQL 注入 教您使用参数化SQL语句 参数化查询为什么能够防止SQL注...

  • JDBC进阶 & BeanUtils组件简介

    预编译sql处理(防止sql注入) Statement : 执行SQL命令CallableStatement : ...

  • 防止黑客SQL注入的方法

    一、SQL注入简介 SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编...

  • BUUCTF-Web-随便注(三种解题思路)

    知识点:SQL注入-堆叠注入,sql预处理语句,巧用contact()函数绕过 堆叠注入原理: 在SQL中,分号(...

网友评论

    本文标题:防止sql注入的方法和原理

    本文链接:https://www.haomeiwen.com/subject/qmddhctx.html