美文网首页
基于pymysql的sql注入解决方案:escape_strin

基于pymysql的sql注入解决方案:escape_strin

作者: warmsirius | 来源:发表于2019-08-27 23:08 被阅读0次

写在前面

工作中,老大自己写的sqlalchemy方法中最后对拼接的sql写了escape_string操作,百度了很久,感觉没有太能明白,写篇博客记录一下。

一、什么是sql注入?

参考下面文章:

二、基于pymysql:sql注入问题举例

简单来说,举一个小🌰:

我现在tb1数据库,下面有个user表,结构如下:

mysql> desc user;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(10) | NO   |     | NULL    |                |
| pwd   | varchar(12) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

查看一下user表当前的数据:

mysql> select id, name, pwd from user;
+----+------+---------+
| id | name | pwd     |
+----+------+---------+
|  1 | jack | jack123 |
|  2 | rose | rose123 |
+----+------+---------+
2 rows in set (0.00 sec)

现在用pymysql来连接数据库,然后进行sql语句查询,一般的查询都是用户名=xxx and 密码=xxx,下面先来写一个函数,来连接数据库:

import pymysql

def query(sql):
    conn = pymysql.connect(host="127.0.0.1", port=3306, user='root', password='', db='tb1')
    cursor = conn.cursor()
    cursor.execute(sql)
    print('打印结果:', cursor.fetchall())
    cursor.close()
    conn.close()

case1:原生拼接sql

输入sql注入语句,不加任何额外操作:

if __name__ == "__main__":
    sql = "select * from user where name= %s and pwd=%s" % ("'' or 1=1 #", 'sdf')
    print(sql)
    query(sql)

输出

select id, name, pwd from user where name= '' or 1=1 # and pwd=sdf
打印结果: ((1, 'jack', 'jack123'), (2, 'rose', 'rose123'))

sql语句这样拼接,造成了sql注入,直接将数据库所有的数据查询出来了,这样如果数据量大会造成崩溃,另外就是泄露信息。这种做法很不推荐。

case2:将sql语句先escape_string,然后执行

if __name__ == "__main__":
    # sql = "select id, name, pwd from user where name= %s and pwd=%s" % ("'' or 1=1 #", 'sdf')
    # print(sql)

    sql = "select * from user where name= %s and pwd=%s" % ("'' or 1=1 #", 'sdf')
    escape_string_sql = pymysql.escape_string(sql)
    print(escape_string_sql)
    query(escape_string_sql)

输出如下:

escape_string_sql: select * from user where name= \'\' or 1=1 # and pwd=sdf

# 报错如下:
pymysql.err.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\\'\\' or 1=1 # and pwd=sdf' at line 1")

sql将里面的' '进行转义后再去查询,转义后sql语句查询会语法错误,完美解决sql注入问题。

三、pymysql.escape_string源码简析

下面摘取片段escape_string的源码:

_escape_table = [unichr(x) for x in range(128)] # 对128个unicode字符生成列表
_escape_table[0] = u'\\0' 
_escape_table[ord('\\')] = u'\\\\'
_escape_table[ord('\n')] = u'\\n'
_escape_table[ord('\r')] = u'\\r'
_escape_table[ord('\032')] = u'\\Z'
_escape_table[ord('"')] = u'\\"'
_escape_table[ord("'")] = u"\\'"
# 将Unicode某些字符进行替换

def _escape_unicode(value, mapping=None):
    """escapes *value* without adding quote.

    Value should be unicode
    """
    return value.translate(_escape_table) # 根据映射进行替换

if PY2:# 在python2中
    def escape_string(value, mapping=None):
        """escape_string escapes *value* but not surround it with quotes.

        Value should be bytes or unicode.
        """
        if isinstance(value, unicode):
            return _escape_unicode(value)
        assert isinstance(value, (bytes, bytearray)) # 如果是字节的话
        value = value.replace('\\', '\\\\')
        value = value.replace('\0', '\\0')
        value = value.replace('\n', '\\n')
        value = value.replace('\r', '\\r')
        value = value.replace('\032', '\\Z')
        value = value.replace("'", "\\'")
        value = value.replace('"', '\\"')
        return value # 将字节里面进行转义

    def escape_bytes_prefixed(value, mapping=None):
        assert isinstance(value, (bytes, bytearray))
        return b"_binary'%s'" % escape_string(value)

    def escape_bytes(value, mapping=None):
        assert isinstance(value, (bytes, bytearray))
        return b"'%s'" % escape_string(value)

else:
    escape_string = _escape_unicode # 在python3中unicode_string = escape_string

分析:

    1. 对128个ASCII码,在PY2中用unichr()函数转换为对应字符,并按顺序生成列表。

在PY3中用chr(),unichr已经不适用PY3了;

    1. 将128个转换后的特殊字符用ord()找到特殊字符顺序,然后进行重新赋值(转义),生成最终的ASCII字符对照表(已转义)。
    1. 定义了_escape_unicode函数,函数传入一个参数为value,可以理解为我们传入的sql语句,返回的是将值的每个ASCII字符对应位置进行转化。
    1. 判断当前python环境
    • 如果是python2,escape_string判断value值是不是unicode类型,如果是,直接调用_escape_unicode方法
    • 如果是bytes类型,那么就针对bytes类型的特殊字符进行转义操作即可,然后返回转义后的value

为什么PY2中要进行类型判断?

这需要从PY2和PY3的编码变化来说

  • PY3:所有str类型都是Unicode对象
  • PY2:默认编码是ascii,但是有2中数据模型来支持字符串这种数据类型,分别为str(bytes类型)和unicode,所以要进行判断str到底是bytes,然后进行操作。
    1. 然后如果是PY3,PY3所有str都是unicode类型了,所以_escape_string=_escape_unicode方法了。

四、总结

搞了一个半小时吧,终于搞懂了,个人觉得还是值得的,至少下一次让你来写转义,还可以参考人家的源码做法,谢谢百度。

相关文章

  • 基于pymysql的sql注入解决方案:escape_strin

    写在前面 工作中,老大自己写的sqlalchemy方法中最后对拼接的sql写了escape_string操作,百度...

  • Python MySQL数据库4:MySQL与Python交互

    总体内容 1.1、SQL语句的强化 1.2、python安装pymysql 1.3、参数化(sql防注入) 一、S...

  • 09-ADO.NET进阶

    一、sql注入风险及解决方案 SQL注入是指在事先定义好的SQL语句中注入额外的SQL语句,从此来欺骗数据库服务器...

  • Less1

    基于报错的sql注入 字符型注入 数字型 括号型推测sql语句 **union ** 联合查询的方法,主要利用my...

  • pymysql模块

    pymysql模块 一.创建连接库 二.建立游标 游标的属性 三.提交sql语句 普通提交 防注入提交 四.查看内...

  • Python 高级 16 数据库Pymysql

    数据库编程概述、pymysql基本操作方法总结、参数化列表防止SQL注入总结 2.6 Python数据库编程 学习...

  • sqlmap使用

    环境准备: 假设存在sql注入的url是 一、基于mysql注入 a、sqlmap注入爆数据库 如下图显示,即爆出...

  • web常见漏洞的成因和修复

    1.SQL注入 漏洞描述:SQL 注入攻击( SQL Injection ),简称注入攻击、SQL 注入,主要用于...

  • SQL注入之基于DNS的注入

    0x00前言 前段时间在做盲注 分别是基于时间和基于布尔型的 说真的 这两种盲注真的太繁琐了 都需要一个个的通过a...

  • MySQL注入常用函数

    注入的分类 仁者见仁,智者见智。 基于从服务器接收到的响应▲基于错误的 SQL 注入▲联合查询的类型▲堆查询注射▲...

网友评论

      本文标题:基于pymysql的sql注入解决方案:escape_strin

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