DjangoORM注入分享
字数 888 2025-08-22 18:37:22

Django ORM 注入安全研究

1. Django ORM 基础

1.1 ORM 概念与优势

Django ORM (Object-Relational Mapping) 是 Django 框架中用于处理数据库操作的机制,允许开发者使用 Python 代码描述数据库模式和执行查询,无需直接编写 SQL 语句。

传统 SQL 查询示例:

def search_articles(search_term: str) -> list[dict]:
    results = []
    with get_db_connection() as conn:
        with conn.cursor() as cursor:
            cursor.execute("SELECT title, body FROM articles WHERE title LIKE %s", (f"%{search_term}%",))
            rows = cursor.fetchall()
            for row in rows:
                results.append({
                    "title": row[0],
                    "body": row[1]
                })
    return results

ORM 方式示例:

# models/article.py
from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=255)
    body = models.TextField()
    
    class Meta:
        ordering = ["title"]

# views/article.py
class ArticleView(APIView):
    def post(self, request: Request, format=None):
        search_term = request.data.get("search", None)
        if search_term is not None:
            articles = Article.objects.filter(title__contains=search_term)
        else:
            articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

1.2 ORM 安全特性

Django ORM 通常能防止 SQL 注入,因为它会自动对查询参数进行转义和处理。但需要注意:

  1. 使用过滤器方法:

    # 安全的查询方式
    users = User.objects.filter(username=username)
    
  2. 避免使用 raw() 方法:

    # 不安全的方式
    users = User.objects.raw("SELECT * FROM auth_user WHERE username = '%s'" % username)
    
    # 安全的方式
    users = User.objects.raw("SELECT * FROM auth_user WHERE username = %s", [username])
    
  3. 使用 QuerySet API:

    # 安全的方式
    users = User.objects.filter(email__icontains='example.com')
    
  4. 参数化查询:

    from django.db import connection
    
    def get_user_by_username(username):
        with connection.cursor() as cursor:
            cursor.execute("SELECT * FROM auth_user WHERE username = %s", [username])
            row = cursor.fetchone()
        return row
    

2. Django ORM 注入漏洞

2.1 漏洞场景

当开发需要实现"按任意字段过滤"功能时,可能写出不安全代码:

# 不安全的实现方式
query = request.GET.dict()
books = Book.objects.filter(**query)

2.2 漏洞利用示例

靶场环境设置:

Book.objects.create(title='flagbook', author='flag book', 
                   published_date=date(2020, 4, 15), 
                   isbn='flag{secret}')

利用 startswith 进行盲注:

GET /books?title__startswith=f
# 返回数据包含flagbook

GET /books?title__startswith=x
# 不返回flagbook

通过这种方式可以逐步推断出敏感字段的值。

2.3 泄露条件总结

  1. 可以控制 filter 过滤列
  2. ORM 支持正则、startswith 等操作
  3. 表中存在隐藏的敏感字段

3. 多表关联注入

3.1 Django 关系字段类型

  1. OneToOneField: 一对一关系

    class UserProfile(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE)
        bio = models.TextField()
    
  2. ManyToManyField: 多对多关系

    class Author(models.Model):
        name = models.CharField(max_length=100)
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
        authors = models.ManyToManyField(Author)
    
  3. ForeignKey: 多对一关系

    class Publisher(models.Model):
        name = models.CharField(max_length=100)
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
        publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
    

3.2 多表注入示例

模型定义:

class Publisher(models.Model):
    name = models.CharField(max_length=100)
    address = models.TextField()

class Category(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.CharField(max_length=100)
    published_date = models.DateField()
    isbn = models.CharField(max_length=13, unique=True)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
    categories = models.ManyToManyField(Category)

class BookDetail(models.Model):
    book = models.OneToOneField(Book, on_delete=models.CASCADE)
    summary = models.TextField()
    number_of_pages = models.IntegerField()

注入方式:

  1. 一对一关系:

    GET /books?bookdetail__summary__startswith=T
    
  2. 多对一关系:

    GET /books?publisher__address__startswith=1
    
  3. 多对多关系:

    GET /books?categories__name__startswith=C
    

4. 防御措施

  1. 白名单过滤:

    ALLOWED_FILTERS = ['title', 'author', 'published_date']
    
    query = {k: v for k, v in request.GET.items() if k in ALLOWED_FILTERS}
    books = Book.objects.filter(**query)
    
  2. 限制关联查询深度:

    MAX_RELATION_DEPTH = 1
    
  3. 敏感字段排除:

    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
            exclude = ['isbn', 'sensitive_field']
    
  4. 输入验证:

    from django.core.validators import validate_slug
    
    def clean_username(value):
        validate_slug(value)  # 只允许字母、数字、下划线或连字符
    

5. 其他安全考虑

关于正则表达式攻击(ReDoS)的思考:

  • 数据库正则引擎通常使用有限状态机实现,不易受到ReDoS攻击
  • 但应用层正则处理仍需注意性能问题

示例:

# 几乎不会造成数据库ReDoS
created_by__user__password__regex=r'^(a+)+$'

6. 总结

Django ORM 虽然提供了良好的SQL注入防护,但不当使用仍可能导致数据泄露。特别是在:

  1. 允许用户控制过滤条件时
  2. 使用动态**kwargs参数时
  3. 存在敏感字段但未正确过滤时

开发者应当:

  • 限制可过滤字段
  • 控制关联查询深度
  • 排除敏感字段
  • 实施严格的输入验证
Django ORM 注入安全研究 1. Django ORM 基础 1.1 ORM 概念与优势 Django ORM (Object-Relational Mapping) 是 Django 框架中用于处理数据库操作的机制,允许开发者使用 Python 代码描述数据库模式和执行查询,无需直接编写 SQL 语句。 传统 SQL 查询示例 : ORM 方式示例 : 1.2 ORM 安全特性 Django ORM 通常能防止 SQL 注入,因为它会自动对查询参数进行转义和处理。但需要注意: 使用过滤器方法 : 避免使用 raw() 方法 : 使用 QuerySet API : 参数化查询 : 2. Django ORM 注入漏洞 2.1 漏洞场景 当开发需要实现"按任意字段过滤"功能时,可能写出不安全代码: 2.2 漏洞利用示例 靶场环境设置 : 利用 startswith 进行盲注 : 通过这种方式可以逐步推断出敏感字段的值。 2.3 泄露条件总结 可以控制 filter 过滤列 ORM 支持正则、startswith 等操作 表中存在隐藏的敏感字段 3. 多表关联注入 3.1 Django 关系字段类型 OneToOneField : 一对一关系 ManyToManyField : 多对多关系 ForeignKey : 多对一关系 3.2 多表注入示例 模型定义 : 注入方式 : 一对一关系 : 多对一关系 : 多对多关系 : 4. 防御措施 白名单过滤 : 限制关联查询深度 : 敏感字段排除 : 输入验证 : 5. 其他安全考虑 关于正则表达式攻击(ReDoS)的思考: 数据库正则引擎通常使用有限状态机实现,不易受到ReDoS攻击 但应用层正则处理仍需注意性能问题 示例: 6. 总结 Django ORM 虽然提供了良好的SQL注入防护,但不当使用仍可能导致数据泄露。特别是在: 允许用户控制过滤条件时 使用动态** kwargs参数时 存在敏感字段但未正确过滤时 开发者应当: 限制可过滤字段 控制关联查询深度 排除敏感字段 实施严格的输入验证