【澳门葡京备用网址】Python中预防sql注入的主意详解,Python中幸免sql注入的法门详解

get是因而URL传参,容易透露,容易被sql注入,制止sql注入的方法之一是:加intval($_GET[‘id’]);

Python中防止sql注入的主意详解,pythonsql注入详解

前言

世家应该都了然现在web漏洞之首莫过于sql了,不管接纳哪一类语言举办web后端开发,只要选取了关系型数据库,可能都会遇上sql注入攻击问题。那么在Python
web开发的长河中sql注入是怎么冒出的啊,又是怎么去解决这些问题的?

当然,我这里并不想谈谈其他语言是哪些避免sql注入的,网上关于PHP(博主注:据说是社会风气上最屌的言语)防注入的各类措施都有,Python的法子其实类似,这里我就举例来说说。

起因

漏洞发出的因由最广大的就是字符串拼接了,当然,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数据库的脚本自己都会写进去这么些类,那么那个类有问题啊?
答案是:有!

【澳门葡京备用网址】Python中预防sql注入的主意详解,Python中幸免sql注入的法门详解。以此类是有瑕疵的,很容易造成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")

和地点的测试结果一致,所以这种艺术也是可怜的,而且这种方式并不是预编译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注入,显然它并从未那么傻。比如说有如下一张表:

澳门葡京备用网址 1

澳门葡京备用网址 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注入漏洞的代码会逐步改过来,好了,以上就是这篇作品的全体内容了,希望本文的情节对我们的学习或者干活能拉动一定的提携。

【编辑推荐】

相关文章

发表评论

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

*
*
Website