pythonsql注入详解,Python中幸免sql注入的法子详解

get是因此U奥迪Q7L传参,不难暴光,简单被sql注入,避免sql注入的章程之一是:加intval($_pythonsql注入详解,Python中幸免sql注入的法子详解。GET[‘id’]);

Python中预防sql注入的方式详解,pythonsql注入详解

前言

世家应该都精通以后web漏洞之首莫过于sql了,不管选拔哪个种类语言进行web后端开发,只要利用了关系型数据库,恐怕都会遇上sql注入攻击难点。那么在Python
web开发的进度中sql注入是怎么冒出的啊,又是怎么去化解那些难点的?

本来,作者这里并不想谈谈别的语言是怎么防止sql注入的,网上有关PHP(博主注:传闻是社会风气上最屌的语言)防注入的各个办法都有,Python的法子其实类似,那里自身就举例来说说。

起因

漏洞爆发的由来最广泛的就是字符串拼接了,当然,sql注入并不只是东拼西凑一种状态,还有像宽字节注入,特殊字符转义等等很三种,那里就说说最广泛的字符串拼接,那也是低档程序员最简单犯的失实。

首先大家定义1个类来处理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数据库的脚本自个儿都会写进去那个类,那么那几个类有有失水准态啊?
pythonsql注入详解,Python中幸免sql注入的法子详解。答案是:有!

以此类是有通病的,很简单造成sql注入,上面就说说为啥会暴发sql注入。

为了表明难点的诚实,那里就写2个格局来调用上面的充裕类里面的法子,固然出现错误会向来抛出十一分。

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
来顶替要传播的参数,看起来是否13分像预编译的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的防注入方法里面都有,对特殊字符举行转义可能过滤。

第2种方案就是选择其中方法,类似于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语句,第2、个是相应的实际上的参数值,函数内部会对传播的参数值举行对应的处理预防sql注入,实际行使的法子如下

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

如此那般就足以幸免sql注入,传入2个列表之后,MySQLdb模块内部会将列表连串化成三个元组,然后进行escape操作。

总结

我事先的片段本子中行使了设有sql注入漏洞的代码会逐年改过来,好了,以上就是这篇文章的全体内容了,希望本文的始末对咱们的求学恐怕办事能带来一定的帮扶,假使有问号大家可以留言交换。

www.5929.com,
前言
我们应该都知晓以后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注入,鲜明它并不曾那么傻。比如说有如下一张表:

www.5929.com 1

www.5929.com 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>

第2大家定义1个类来处理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语句,不荒谬的操作应该是在前者页面输入3个名字,并询问结果,如:传入参数为:张三,则对应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}
之后又会怎么呢?固然传入的参数为:张3、则结果很举世瞩目跟上边第二回的是千篇一律的,那假若将盛传参数变为:张三’
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注入的最首要,代码中通过2个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
执行的时候传出五个参数,第3个是参数化的sql语句,第三个是应和的实际的参数值,函数内部会对传播的参数值举行对应的拍卖预防sql注入,实际运用的章程如下

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

如此就可以预防sql注入,传入贰个列表之后,MySQLdb模块内部会将列表体系化成2个元组,然后进行escape操作。

总结

作者事先的有的本子中选取了存在sql注入漏洞的代码会日渐改过来,好了,以上就是那篇文章的全体内容了,希望本文的始末对我们的求学只怕工作能牵动一定的资助。

【编辑推荐】

Leave a Comment.