从用户控制的来源构建的 SQL 查询¶
ID: py/sql-injection
Kind: path-problem
Security severity: 8.8
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-089
Query suites:
- python-code-scanning.qls
- python-security-extended.qls
- python-security-and-quality.qls
如果数据库查询(例如 SQL 或 NoSQL 查询)是从用户提供的数据构建的,并且没有足够的清理,用户可能能够运行恶意数据库查询。
这还包括在 [SQLAlchemy](https://pypi.ac.cn/project/SQLAlchemy/)
PyPI 包中使用 TextClause
类,该类用于表示文字 SQL 片段,并在使用 ORM 构建的查询中使用时直接插入到最终的 SQL 中。
建议¶
大多数数据库连接器库提供了一种通过查询参数或预备语句安全地将不可信数据嵌入到查询中的方法。
示例¶
在以下代码片段中,用户使用三种不同的查询从数据库中获取。
在第一种情况下,查询字符串是通过直接使用来自用户提供的请求参数的字符串格式化构建的。该参数可能包含引号字符,因此此代码容易受到 SQL 注入攻击。
在第二种情况下,用户提供的请求属性使用查询参数传递给数据库。数据库连接器库将负责根据需要进行转义和插入引号。
在第三种情况下,SQL 字符串中的占位符已被手动加引号。由于大多数数据库连接器库将插入自己的引号,因此自己这样做会使代码容易受到 SQL 注入攻击。在这个例子中,如果 username
是 ; DROP ALL TABLES --
,最终的 SQL 查询将是 SELECT * FROM users WHERE username = ''; DROP ALL TABLES -- ''
from django.conf.urls import url
from django.db import connection
def show_user(request, username):
with connection.cursor() as cursor:
# BAD -- Using string formatting
cursor.execute("SELECT * FROM users WHERE username = '%s'" % username)
user = cursor.fetchone()
# GOOD -- Using parameters
cursor.execute("SELECT * FROM users WHERE username = %s", username)
user = cursor.fetchone()
# BAD -- Manually quoting placeholder (%s)
cursor.execute("SELECT * FROM users WHERE username = '%s'", username)
user = cursor.fetchone()
urlpatterns = [url(r'^users/(?P<username>[^/]+)$', show_user)]
参考资料¶
维基百科:SQL 注入.
OWASP:SQL 注入预防速查表.
常见弱点枚举:CWE-89.