1.什么是sql注入
释义:
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息
参考文章
2.基于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()
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语句查询会语法错误,完美解决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注入问题。
3.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了;
2.将128个转换后的特殊字符用ord()找到特殊字符顺序,然后进行重新赋值(转义),生成最终的ASCII字符对照表(已转义)。
3.定义了_escape_unicode函数,函数传入一个参数为value,可以理解为我们传入的sql语句,返回的是将值的每个ASCII字符对应位置进行转化。
4.判断当前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,然后进行操作。
5.然后如果是PY3,PY3所有str都是unicode类型了,所以_escape_string=_escape_unicode方法了。