Python中防止sql注入的方法详解

by admin on 2019年1月31日

get是经过URL传参,容易暴光,简单被sql注入,幸免sql注入的法门之一是:加intval($_GET[‘id’]);

Python中预防sql注入的措施详解,pythonsql注入详解

前言

Python中防止sql注入的方法详解。我们应该都晓得现在web漏洞之首莫过于sql了,不管选用哪类语言进行web后端开发,只要利用了关系型数据库,可能都会碰着sql注入攻击难题。那么在Python
web开发的历程中sql注入是怎么出现的吧,又是怎么去化解那些难题的?

本来,我那边并不想谈谈其余语言是怎么防止sql注入的,网上关于PHP(博主注:据说是社会风气上最屌的语言)防注入的各样方法都有,Python的措施其实看似,那里自己就举例来说说。

起因

bf88必发唯一官网 ,漏洞发出的原由最常见的就是字符串拼接了,当然,sql注入并不只是拼接一种情状,还有像宽字节注入,特殊字符转义等等很两种,那里就说说最常见的字符串拼接,那也是中低档程序员最不难犯的一无所长。

先是大家定义一个类来拍卖mysql的操作

class Database:
 aurl = '127.0.0.1'
 user = 'root'
 password = 'root'
 db = 'testdb'
 charset = 'utf8'

 def __init__(self):
  self.connection = MySQLdb.connect(self.aurl, self.user, self.password, self.db, charset=self.charset)
  self.cursor = self.connection.cursor()

 def insert(self, query):
  try:
   self.cursor.execute(query)
   self.connection.commit()
  except Exception, e:
   print e
   self.connection.rollback()

 def query(self, query):
  cursor = self.connection.cursor(MySQLdb.cursors.DictCursor)
  cursor.execute(query)
  return cursor.fetchall()

 def __del__(self):
  self.connection.close()

那段代码在自我前面很多剧本里面都会看见,涉及到Python操作mysql数据库的脚本我都会写进去那个类,那么那几个类有标题吧?
答案是:有!

以此类是有弱点的,很简单导致sql注入,下边就说说为什么会发生sql注入。

为了表达难题的忠实,那里就写一个格局来调用上面的卓殊类里面的不二法门,要是现身谬误会一贯抛出格外。

def test_query(articleurl):
 mysql = Database()
 try:
  querySql = "SELECT * FROM `article` WHERE url='" + articleurl + "'"
  chanels = mysql.query(querySql)
  return chanels
 except Exception, e:
  print e

其一措施相当简单,一个最广大的select查询语句,也利用了最简便易行的字符串拼接组成sql语句,很通晓传入的参数
articleurl
可控,要想举办注入测试,只必要在articleurl的值前边加上单引号即可开展sql注入测试,这一个不多说,肯定是存在注入漏洞的,脚本跑一次,看甚结果

(1064, "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''t.tips''' at line 1")

回显报错,很熟谙的荒唐,那里自己传入的测试参数是

t.tips'

下边再说一种导致注入的情况,对上边的不二法门举办多少修改后

def test_query(articleurl):
 mysql = Database()
 try:
  querySql = ("SELECT * FROM `article` WHERE url='%s'" % articleurl)
  chanels = mysql.query(querySql)
  return chanels
 except Exception, e:
  print e

其一法子里面没有直接行使字符串拼接,而是接纳了 %s
来代替要传播的参数,看起来是否老大像预编译的sql?那那种写法能无法防备sql注入呢?测试一下便知道,回显如下

(1064, "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''t.tips''' at line 1")

Python中防止sql注入的方法详解。和上边的测试结果一致,所以那种格局也是卓殊的,而且那种方法并不是预编译sql语句,那么如何是好才能防患sql注入呢?

解决

三种方案

     1> 对传播的参数进行编码转义

     2> 使用Python的MySQLdb模块自带的点子

率先种方案其实在广大PHP的防注入方法里面都有,对特殊字符举办转义或者过滤。

第三种方案就是应用其中方法,类似于PHP里面的PDO,那里对上边的数码库类举行简单的改动即可。

修改后的代码

class Database:
 aurl = '127.0.0.1'
 user = 'root'
 password = 'root'
 db = 'testdb'
 charset = 'utf8'

 def __init__(self):
  self.connection = MySQLdb.connect(self.aurl, self.user, self.password, self.db, charset=self.charset)
  self.cursor = self.connection.cursor()

 def insert(self, query, params):
  try:
   self.cursor.execute(query, params)
   self.connection.commit()
  except Exception, e:
   print e
   self.connection.rollback()

 def query(self, query, params):
  cursor = self.connection.cursor(MySQLdb.cursors.DictCursor)
  cursor.execute(query, params)
  return cursor.fetchall()

 def __del__(self):
  self.connection.close()

这边 execute
执行的时候传出四个参数,首个是参数化的sql语句,第一个是相应的实在的参数值,函数内部会对传播的参数值举办相应的拍卖预防sql注入,实际使用的主意如下

preUpdateSql = "UPDATE `article` SET title=%s,date=%s,mainbody=%s WHERE id=%s"
mysql.insert(preUpdateSql, [title, date, content, aid])

诸如此类就足以预防sql注入,传入一个列表之后,MySQLdb模块内部会将列表系列化成一个元组,然后开展escape操作。

总结

自己事先的有的剧本中利用了留存sql注入漏洞的代码会逐步改过来,好了,以上就是那篇小说的全体内容了,希望本文的内容对我们的就学或者办事能带来一定的救助,借使有疑问大家可以留言沟通。

前言
我们应该都精晓现在web漏洞之首莫过于sql了,不管接纳哪类语言进行web后端开发,只…

如今,在写程序时先河注目到sql注入的题材,由于此前写代码时不是很上心,有一些sql会设有被注入的危害,那么幸免sql注入的规律是怎样啊?大家先是通过PrepareStatement这一个类来学习一下呢!
用作一个IT业老婆士只要接触过数据库的人都应该通晓sql注入的定义及风险,那么如何叫sql注入呢?我在那边先给它来一个简短的概念:sql注入,简单的讲就是用户在前端web页面输入恶意的sql语句用来欲盖弥彰后端服务器去执行恶意的sql代码,从而导致数据库数据走漏或者受到攻击。
那就是说,当大家在运用数据库时,怎么着去防备sql注入的爆发吧?大家听其自然地就会想到在用JDBC进行连接时使用PreparedStatement类去顶替Statement,或者传播的标准化参数完全不行使String字符串,同样地,在用mybatis时,则尽量利用#{param}占位符的法门去幸免sql注入,其实jdbc和mybatis的原理是同一的。大家都知情当大家选用PreparedStatement去写sql语句时,程序会对该条sql首先进行预编译,然后会将盛传的字符串参数以字符串的款型去处理,即会在参数的两边自动抬高单引号(’param’),而Statement则是直接省略严酷地因这个人为的字符串拼接的办法去写sql,那那样就很简单被sql注入。
这就是说,借使PreparedStatement只是仅仅简单地经过把字符串参数两边加上引号的格局去处理,一样也很容易被sql注入,显明它并不曾那么傻。比如说有如下一张表:

bf88必发唯一官网 1

bf88必发唯一官网 2

create table user
(
    id  int4  PRIMARY KEY,
    name VARCHAR(50) not null,
    class VARCHAR(50)
)

 

前言

其中有如下几条数据:

大家应该都了解现在web漏洞之首莫过于sql了,不管选取哪个种类语言举行web后端开发,只要使用了关系型数据库,可能都会赶上sql注入攻击难题。那么在Python
web开发的进度中sql注入是怎么出现的吗,又是怎么去化解那一个题材的?

INSERT INTO `user` VALUES ('1', '张三', '1班');
INSERT INTO `user` VALUES ('2', '李四', '2班');
INSERT INTO `user` VALUES ('3', '王五', '3班');
INSERT INTO `user` VALUES ('4', '赵六', '4班');

本来,我那里并不想谈谈其余语言是怎么着幸免sql注入的,网上关于PHP(博主注:据说是社会风气上最屌的言语)防注入的各样方式都有,Python的办法其实类似,那里我就举例来说说。

此处我们运用mybatis的 {param} 和 #{param} 两个不等的占位符来作为示范解释 Statement 和 PreparedStatement (mybatis和jdbc的低层原理是一样的)。首先

起因

{}是不可能防备sql注入的,它亦可透过字符串拼接的款型来随便摆弄你的sql语句,而#{}则足以很大程度上地预防sql注入,上边是关于那些的一条sql:

漏洞爆发的原故最广大的就是字符串拼接了,当然,sql注入并不只是东拼西凑一种处境,还有像宽字节注入,特殊字符转义等等很二种,那里就说说最广泛的字符串拼接,那也是中低档程序员最不难犯的谬误。

<mapper namespace="com.sky.dao.UserMapper">
    <select id="query" parameterType="com.sky.model.User" resultType="com.sky.model.User">
        select * from user where name = '${name}'
    </select>
</mapper>

首先大家定义一个类来拍卖mysql的操作

同时,给出前端页面的简练的代码:

class Database:  aurl = '127.0.0.1'  user = 'root'  password = 'root'  db = 'testdb'  charset = 'utf8'    def __init__(self):   self.connection = MySQLdb.connect(self.aurl, self.user, self.password, self.db, charset=self.charset)   selfself.cursor = self.connection.cursor()    def insert(self, query):   try:    self.cursor.execute(query)    self.connection.commit()   except Exception, e:    print e    self.connection.rollback()    def query(self, query):   cursor = self.connection.cursor(MySQLdb.cursors.DictCursor)   cursor.execute(query)   return cursor.fetchall()    def __del__(self):   self.connection.close() 
<form action="<%=basePath%>query" method="get">
     <input type="input" placeholder="请输入姓名" name="name"/><input type="submit" value="查询"/>
</form>

那段代码在自己事先很多剧本里面都会映入眼帘,涉及到Python操作mysql数据库的脚本自己都会写进去那一个类,那么这几个类有标题呢?

前端页面通过form表单的款型输入查询条件并调用后端sql。鲜明对于地点这条sql语句,正常的操作应该是在前者页面输入一个名字,并询问结果,如:传入参数为:张三,则对应sql为:select
* from user where name =
‘张三’;那么,其结果就是:id=1;name=’张三’;classname=’1班’;可是,如果其扩散参数为:张三’
or 1=’1;则传出后台之后其对应的sql就变成:select * from user where name
= ‘张三’ or 1=’1’;那么,其出口的结果就是表中所有的数码。

答案是:有!

那么,若是大家我们将mybatis中的sql语句改为:select * from user where
name = #{name}
之后又会怎么样呢?借使传入的参数为:张三,则结果很分明跟上边第三遍的是平等的,那假若将盛传参数变为:张三’
or 1=’1
又会什么呢?实践声明,查询结果为空,很通晓它并不只是给字符串两端加了单引号那么粗略,否则我看成一个新手都不管就想获取的题材,那么多高智力的IT人员又怎会发现不了呢。那么它的原理又是如何啊?我带着那几个题材去寻觅答案,很强烈,寻找答案的最好措施就是去看源代码。于是,我找到了mysql-jdbc连接的源代码,查看了PreparedStatement类的源代码,其中setString()方法的源代码如下:

本条类是有缺点的,很简单导致sql注入,上边就说说为什么会生出sql注入。

/**
     * Set a parameter to a Java String value. The driver converts this to a SQL
     * VARCHAR or LONGVARCHAR value (depending on the arguments size relative to
     * the driver's limits on VARCHARs) when it sends it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1...
     * @param x
     *            the parameter value
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    public void setString(int parameterIndex, String x) throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
            // 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 = isEscapeNeededForString(x, stringLength);

                    if (!needsHexEscape) {
                        byte[] parameterAsBytes = null;

                        StringBuilder quotedString = new StringBuilder(x.length() + 2);
                        quotedString.append('\'');
                        quotedString.append(x);
                        quotedString.append('\'');

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

                        setInternal(parameterIndex, parameterAsBytes);
                    } else {
                        byte[] parameterAsBytes = null;

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

                        setBytes(parameterIndex, parameterAsBytes);
                    }

                    return;
                }

                String parameterAsString = x;
                boolean needsQuoted = true;

                if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) {
                    needsQuoted = false; // saves an allocation later

                    StringBuilder buf = new StringBuilder((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;

                            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;

                            case '\u00a5':
                            case '\u20a9':
                                // escape characters interpreted as backslash by mysql
                                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) == '\\') {
                                        buf.append('\\');
                                    }
                                }
                                buf.append(c);
                                break;

                            default:
                                buf.append(c);
                        }
                    }

                    buf.append('\'');

                    parameterAsString = buf.toString();
                }

                byte[] parameterAsBytes = null;

                if (!this.isLoadDataQuery) {
                    if (needsQuoted) {
                        parameterAsBytes = StringUtils.getBytesWrapped(parameterAsString, '\'', '\'', this.charConverter, this.charEncoding,
                                this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), getExceptionInterceptor());
                    } else {
                        parameterAsBytes = StringUtils.getBytes(parameterAsString, this.charConverter, this.charEncoding, this.connection.getServerCharset(),
                                this.connection.parserKnowsUnicode(), getExceptionInterceptor());
                    }
                } else {
                    // Send with platform character encoding
                    parameterAsBytes = StringUtils.getBytes(parameterAsString);
                }

                setInternal(parameterIndex, parameterAsBytes);

                this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.VARCHAR;
            }
        }
    }

为了求证难题的真实性,那里就写一个方法来调用上边的要命类里面的方法,如若出现谬误会直接抛出相当。

那段代码的职能是将java中的String字符串参数传到sql语句中,并经过驱动将其转换成sql语句并到数据库中推行。那段代码中前面一部分做了部分是或不是须要对字符串举行转义的论断,那里不开展讲。后边一部分则是哪些有效防护sql注入的根本,代码中经过一个for循环,将字符串参数通过提取每一位上的char字符进行遍历,并通过switch()….case
条件语句进行判定,当出现换行符、引号、斜杠等特殊字符时,对那几个特殊字符举行转义。那么,此时难题的答案就出去了,当大家运用PreparedStatement举办传参时,若传入参数为:张三’
or 1 = ‘1 时,经进度序后台举行转义后,真正的sql其实变成了: select *
from user where name = ‘张三\’ or 1 =
\’1’;分明那样查询出来的结果必然为空。

def test_query(articleurl):  mysql = Database()  try:   querySql = "SELECT * FROM `article` WHERE url='" + articleurl + "'"   chanels = mysql.query(querySql)   return chanels  except Exception, e:   print e 

以上,就是眼前自我对此幸免sql注入的片段领会,由于入行不深,对于一些难题的精通还不够透彻,希望有错误的地方,请各位大神见谅,并跪求指正,谢谢!

这些主意卓殊简单,一个最广大的select查询语句,也应用了最简便易行的字符串拼接组成sql语句,很明朗传入的参数
articleurl
可控,要想拓展注入测试,只必要在articleurl的值后边加上单引号即可开展sql注入测试,这几个不多说,肯定是存在注入漏洞的,脚本跑四次,看甚结果

(1064, "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''t.tips''' at line 1") 

回显报错,很眼熟的一无所能,那里我传入的测试参数是

t.tips' 

下边再说一种导致注入的动静,对地点的法子开展多少修改后

def test_query(articleurl):  mysql = Database()  try:   querySql = ("SELECT * FROM `article` WHERE url='%s'" % articleurl)   chanels = mysql.query(querySql)   return chanels  except Exception, e:   print e 

本条方式里面没有向来采取字符串拼接,而是使用了 %s
来取代要传播的参数,看起来是或不是丰裕像预编译的sql?这这种写法能无法防备sql注入呢?测试一下便驾驭,回显如下

(1064, "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''t.tips''' at line 1") 

和方面的测试结果一律,所以那种措施也是特其余,而且那种艺术并不是预编译sql语句,那么怎么办才能防备sql注入呢?

解决

两种方案

1> 对传播的参数进行编码转义

2> 使用Python的MySQLdb模块自带的法子

先是种方案其实在许多PHP的防注入方法里面都有,对特殊字符进行转义或者过滤。

其次种方案就是行使其中方法,类似于PHP里面的PDO,那里对地方的数据库类举行简短的修改即可。

修改后的代码

class Database:  aurl = '127.0.0.1'  user = 'root'  password = 'root'  db = 'testdb'  charset = 'utf8'    def __init__(self):   self.connection = MySQLdb.connect(self.aurl, self.user, self.password, self.db, charset=self.charset)   selfself.cursor = self.connection.cursor()    def insert(self, query, params):   try:    self.cursor.execute(query, params)    self.connection.commit()   except Exception, e:    print e    self.connection.rollback()    def query(self, query, params):   cursor = self.connection.cursor(MySQLdb.cursors.DictCursor)   cursor.execute(query, params)   return cursor.fetchall()    def __del__(self):   self.connection.close() 

那里 execute
执行的时候传出五个参数,第三个是参数化的sql语句,第二个是呼应的莫过于的参数值,函数内部会对传播的参数值举行相应的拍卖预防sql注入,实际应用的艺术如下

preUpdateSql = "UPDATE `article` SET title=%s,date=%s,mainbody=%s WHERE id=%s" mysql.insert(preUpdateSql, [title, date, content, aid]) 

诸如此类就足以预防sql注入,传入一个列表之后,MySQLdb模块内部会将列表连串化成一个元组,然后开展escape操作。

总结

本人以前的一部分剧本中动用了存在sql注入漏洞的代码会日益改过来,好了,以上就是那篇小说的全体内容了,希望本文的始末对我们的就学或者工作能拉动一定的声援。

【编辑推荐】

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图