Python代码审计实战案例总结之SQL和ORM注入
字数 1695 2025-08-18 11:39:11
Python代码审计实战:SQL注入与ORM注入详解
1. SQL注入基础与Python案例
1.1 SQL注入原理
SQL注入属于OWASP TOP 10安全风险之一,当不受信任的数据作为命令或查询的一部分发送到解析器时,攻击者的恶意数据可以诱使解析器在没有适当授权的情况下执行非预期命令或访问数据。
1.2 Python中常见SQL注入风险
在Python中,直接拼接SQL语句是高风险行为:
# 危险示例:字符串拼接SQL
sql = "select id,name from user_table where id = %s and name = %s" % (id, name)
cur.execute(sql)
1.3 实际案例解析
import urllib
import MySQLdb
import SocketServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
class MyHandler(SimpleHTTPRequestHandler):
def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_GET(self):
print("got get request %s" % (self.path))
hql = urllib.splitquery(self.path)[1]
uri_c = str(hql)
print('cmd===%s' % (uri_c))
sql = "select id from user_table where id = %s" % uri_c
db = MySQLdb.connect("localhost", "testuser", "test123", "TESTDB", charset='utf8')
cursor = db.cursor()
cursor.execute(sql)
data = cursor.fetchone()
self.wfile.write(data)
def start_server():
httpd = SocketServer.TCPServer(("127.0.0.1", 8090), MyHandler)
print('Starting httpd...')
httpd.serve_forever()
if __name__ == "__main__":
start_server()
漏洞分析:
- 使用
urllib.splitquery获取GET请求参数 - 直接将参数值
uri_c拼接到SQL语句中 - 执行未经过滤的SQL查询
修复建议:
- 使用参数化查询而非字符串拼接
- 对输入进行严格的验证和过滤
2. ORM注入详解
2.1 ORM注入原理
ORM注入是SQL注入的特殊情况,ORM模块将SQL语句模板化,使得传统的字符串搜索方法失效。关键在于ORM模块是否对输入进行了妥善过滤或转义。
2.2 SQLAlchemy ORM注入案例(CNVD-2019-17301)
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
import sqlalchemy
print("sqlalchemy_version:",sqlalchemy.__version__)
engine = create_engine('mysql://root:123456@192.168.56.101:3306/mysql?charset=utf8')
DB_Session = sessionmaker(bind=engine)
session = DB_Session()
session.execute('use mysql;')
print(session.execute("""select * from user where User='root' and 1=1;""").fetchall())
风险点:
session.execute()直接执行原始SQL语句- 开发者误认为ORM自动防御所有SQL注入
- 其他潜在注入点:
order_by注入- 利用
limit和offset关键词向select()函数传递注入
防御措施:
- 避免直接执行原始SQL
- 使用ORM提供的安全查询方法
- 对用户输入进行严格验证
2.3 Django JSON SQL注入(CVE-2019-14234)
2.3.1 背景知识
PostgreSQL中JSON数据查询主要使用:
- ArrayField
- JSONField
- HStoreField
Django查询PostgreSQL的两种方式:
Json.objects.filter()QuerySet.filter()
2.3.2 正常查询示例
# 查询data数据下名称为test的内容为'user'的整个字段
Json.objects.filter(data__test='user')
# 或
Json.objects.filter(**{"data__test":'user'})
2.3.3 漏洞分析
- 通过
self.key_name传递查询参数 KeyTransformFactory调用KeyTransform并传入self.key_name- 最终进行字符串拼接:
class KeyTextTransform(KeyTransform): operator = '->>' # 字符串拼接 (%s %s %s)" % (lhs, self.operator, lookup)
2.3.4 注入示例
# 使用注入
# 拼接补全SQL语法
Json.objects.filter(**{"""data__breed'='"a"') OR 1=1 OR('d""":'x',})
3. 防御策略总结
3.1 通用防御措施
- 参数化查询:始终使用参数化查询而非字符串拼接
- 输入验证:对用户输入进行严格的白名单验证
- 最小权限原则:数据库账户应仅具有必要权限
- ORM安全使用:
- 了解ORM的安全特性与限制
- 避免直接执行原始SQL
- 使用ORM提供的安全查询方法
3.2 Python特定防御
-
使用DB-API的参数化查询:
# 安全示例 sql = "SELECT * FROM users WHERE username = %s AND password = %s" cursor.execute(sql, (username, password)) -
Django防御:
- 使用Django ORM的标准查询方法
- 避免使用
extra()和raw()等可能引入风险的方法 - 及时更新Django版本以修复已知漏洞
-
SQLAlchemy防御:
- 使用
text()函数时绑定参数 - 避免直接字符串拼接
- 使用ORM层而非直接SQL层
- 使用
4. 审计方法论
4.1 SQL注入审计要点
-
查找字符串拼接模式:
%格式化.format()- f-string
+连接
-
追踪用户输入流向:
- GET/POST参数
- Cookie值
- HTTP头
- 数据库读取值
-
检查数据库操作:
execute()executemany()callproc()
4.2 ORM注入审计要点
-
识别ORM模块使用:
- SQLAlchemy
- Django ORM
- Peewee
- PonyORM等
-
检查不安全用法:
- 直接SQL执行
- 原始SQL片段
- 动态查询构建
-
关注特殊功能:
- 排序(
order_by) - 分页(
limit/offset) - 聚合函数
- JSON/数组操作
- 排序(
5. 工具推荐
-
静态分析工具:
- Bandit
- Semgrep
- PyCharm安全插件
-
动态测试工具:
- SQLMap
- Burp Suite
- OWASP ZAP
-
ORM特定工具:
- Django安全检查框架
- SQLAlchemy审计插件
通过系统化的审计方法和防御策略,可以有效预防和发现Python应用中的SQL注入和ORM注入漏洞。