Python基础:理解SQL注入问题的起因,掌握pymysql参数化防止黑客使用SQL注入浸入系统和拖库

2023年1月31日08:00:46

1、首先我们了解一下什么叫SQL注入问题

SQL注入是一个很古老的系统安全问题,黑客可以通过构造字符串,尝试改变实际执行的SQL语句,从而达到绕过系统认证,或者提取系统中自己没有权限的数据来脱库。出现这个问题的根本原因是开发者在使用SQL的时候,采用的是拼接字符串的方式来实现SQL语句的参数传值,当然这种问题在ORM框架盛行的今天其实出现概率已经很小了,具体例子如下:

  • SQL注入绕过认证原理,如下代码就存在绕过认证的安全问题:

  • 正常情况下,使用正确和错误的用户名和密码都能够限制认证

       数库数据:

# 建立数据库连接
def login(userName, userPwd):
    connection = pymysql.connect(host='127.0.0.1', user='root', password="root",
                                 database='newtest', port=3306, charset='utf8')
    # 获取游标对象
    cursor = connection.cursor()
    # sql = "select * from t_user where username='%s' and userpwd='%s'" % (userName, userPwd)
    sql = "select * from t_user where username='"+ userName +"' and userpwd='" + userPwd +"'"
    print(sql)
    # 执行查询操作
    result = cursor.execute(sql)
    connection.commit()
    userInfo = cursor.fetchone()
    connection.close() #
    return userInfo
# 普通登录,正确的用户名和密码登录成功
user = login('xiaojiejie', '123456')
if user:
    print("登录成功")

# 普通登录,错误的用户名或密码登录失败
user = login('xiaojiejie', '111111')
if user:
    print("登录成功")
else:
    print("登录失败")

如下,第一次正确的用户名和密码,提示成功,第二密码是错误的,提示失败

  • SQL注入情况,通过传入特殊构造的参数,从而达到改变实际执行SQL语句绕过认证的目的
# 构造特殊的字符串,达到SQL注入的目的
user = login("xiaojiejie", "1' or '1'='1")
if user:
    print("绕过认证,登录成功")
else:
    print("登录失败")

以上在传入参数的时候,密码特殊处理了,1' or '1'='1,从而使得密码和原始SQL在字符串拼接的时候,构成了如下的SQL

select * from t_user where username='xiaojiejie' and userpwd='1' or '1'='1'

引入 or '1'='1'是恒成真的,所以可以绕过认证。

问题的根本原因就在于SQL语句:

 sql = "select * from t_user where username='"+ userName +"' and userpwd='" + userPwd +"'" 

是通过字符串拼接出来的,这样就让一些别有用心的人有机可趁。

 

2、SQL语句参数化是解决这类问题的通用方案

SQL语句参数化是数据库技术里通用的解决SQL注入的最佳解决方案,其实就是在程序向数据库发送SQL执行的时候将SQL语句和参数分开传递,需要补充的动态参数在SQL语句中使用占位符占位,然后在数据库端在填入参数执行,这样既规避了SQL注入的问题,同时也一定程度提高了数据库执行SQL的效率。就像Java中JDBC支持preparedStatement,Python也支持SQL参数化。实现如下:

def loginParams(params=[]):
    connection = pymysql.connect(host='127.0.0.1', user='root', password="root",
                                 database='newtest', port=3306, charset='utf8')
    # 获取游标对象
    cursor = connection.cursor()
    sql = "select * from t_user where username=%s and userpwd=%s"
    print(sql)
    # 执行查询操作
    result = cursor.execute(sql, params)
    connection.commit()
    userInfo = cursor.fetchone()
    connection.close() #
    return userInfo
  • 再次尝试绕过认证:

# 构造特殊的字符串,参数化后无法达到SQL注入的目的
user = loginParams(["xiaojiejie", "1' or '1'='1"])
print(user)
if user:
    print("绕过认证,登录成功")
else:
    print("绕过认证,登录失败")

绕过失败:

3.最后介绍一个简单的利用SQL注入拖库的安全技术

通常Web系统都可以使用id来获得用户的个人信息,那么如果要看到别人的个人信息或者系统中所有用户的信息,就是一个拖库方法,SQL具有采用字符串拼接也会引起这样的安全问题,例如:

def profile(userId):
    connection = pymysql.connect(host='127.0.0.1', user='root', password="root",
                                 database='newtest', port=3306, charset='utf8')
    # 获取游标对象
    cursor = connection.cursor()
    sql = "select * from t_user where userid=" +str(userId)
    print(sql)
    # 执行查询操作
    result = cursor.execute(sql)
    connection.commit()
    userInfo = cursor.fetchall()
    connection.close()  #
    return userInfo
    pass

userInfo = profile(1)
print(userInfo)

根据个人userid能够读取自己的信息

对于一个普通用户来说,他并不具读取其他用户的个人信息的权限,我们改一下参数:

# 越权读取了表中全部用户的信息
userInfo = profile("1 or 1=1")
print(userInfo)

以上可以看到执行的通过传入的参数,实际执行的SQL是:

select * from t_user where userid=1 or 1=1

从而实现了拖库,是不是很危险

综上所述,大家在开发代码的过程中一定不要使用拼接字符串的方式拼接SQL语句,避免留下安全漏洞,以上内容主要是写给新手程序猿。

  • 作者:猿说猿道
  • 原文链接:https://blog.csdn.net/nosprings/article/details/106915908
    更新时间:2023年1月31日08:00:46 ,共 2775 字。