You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中如何通过校验RawSQL阻止删除、更新等操作?

拦截Django中RawSQL的删除/更新操作的方案

方案一:自定义安全RawSQL子类

直接继承Django原生的RawSQL,在初始化阶段添加SQL校验逻辑,替换原有RawSQL的使用场景即可。

from django.db.models.expressions import RawSQL
import re

class SafeRawSQL(RawSQL):
    def __init__(self, sql, params=None, output_field=None):
        cleaned_sql = self._clean_sql(sql)
        # 定义禁止执行的SQL语句前缀
        forbidden_prefixes = ('DELETE', 'UPDATE', 'INSERT INTO', 'ALTER TABLE', 'DROP TABLE')
        
        if any(cleaned_sql.startswith(prefix) for prefix in forbidden_prefixes):
            raise ValueError("禁止执行数据修改类SQL语句")
        
        super().__init__(sql, params, output_field)
    
    @staticmethod
    def _clean_sql(sql):
        # 移除SQL中的单行/多行注释
        sql = re.sub(r'--.*?$', '', sql, flags=re.MULTILINE)
        sql = re.sub(r'/\*.*?\*/', '', sql, flags=re.DOTALL)
        # 合并多余空格并转为大写,统一校验规则
        return ' '.join(sql.strip().split()).upper()

使用时,把代码中所有RawSQL(...)替换为SafeRawSQL(...),一旦传入修改类SQL,会直接抛出异常终止执行。

方案二:全局拦截数据库游标执行

通过自定义数据库后端包装器,拦截所有SQL执行请求,从底层阻止修改类语句。这种方式能覆盖所有SQL执行场景,包括RawSQL、ORM自动生成的SQL(可按需针对性排除)。

1. 自定义数据库后端

以SQLite为例,创建自定义包装器:

from django.db.backends.sqlite3.base import SQLiteDatabaseWrapper
from django.db.utils import OperationalError
import re

class SafeSQLiteWrapper(SQLiteDatabaseWrapper):
    def create_cursor(self, name=None):
        cursor = super().create_cursor(name)
        # 包装原生execute和executemany方法
        original_execute = cursor.execute
        original_executemany = cursor.executemany

        def safe_execute(sql, params=None):
            self._validate_sql(sql)
            return original_execute(sql, params)

        def safe_executemany(sql, params=None):
            self._validate_sql(sql)
            return original_executemany(sql, params)

        cursor.execute = safe_execute
        cursor.executemany = safe_executemany
        return cursor

    def _validate_sql(self, sql):
        cleaned_sql = self._clean_sql(sql)
        forbidden_prefixes = ('DELETE', 'UPDATE', 'INSERT INTO', 'ALTER TABLE', 'DROP TABLE')
        
        if any(cleaned_sql.startswith(prefix) for prefix in forbidden_prefixes):
            raise OperationalError("数据库禁止执行数据修改类SQL语句")
    
    @staticmethod
    def _clean_sql(sql):
        # 移除注释并格式化SQL
        sql = re.sub(r'--.*?$', '', sql, flags=re.MULTILINE)
        sql = re.sub(r'/\*.*?\*/', '', sql, flags=re.DOTALL)
        return ' '.join(sql.strip().split()).upper()

如果使用MySQL或PostgreSQL,只需替换对应的父类(比如django.db.backends.mysql.base.DatabaseWrapper)。

2. 配置使用自定义后端

在settings.py的DATABASES中指定自定义引擎:

DATABASES = {
    'default': {
        'ENGINE': 'your_app_name.backends.safe_sqlite',  # 替换为你的自定义包装器实际路径
        'NAME': BASE_DIR / 'db.sqlite3',
        # 其他数据库配置参数...
    }
}

方案三:使用装饰器管控RawSQL调用

如果只需要管控特定函数中的RawSQL调用,可以用装饰器包裹RawSQL的调用逻辑:

import re

def block_modify_sql(func):
    def wrapper(*args, **kwargs):
        # 假设第一个参数是SQL语句,可根据实际调用结构调整
        sql = args[0] if args else kwargs.get('sql')
        if sql:
            cleaned_sql = _clean_sql(sql)
            forbidden_prefixes = ('DELETE', 'UPDATE', 'INSERT INTO')
            if any(cleaned_sql.startswith(prefix) for prefix in forbidden_prefixes):
                raise ValueError("禁止执行修改类SQL语句")
        return func(*args, **kwargs)
    
    def _clean_sql(sql):
        sql = re.sub(r'--.*?$', '', sql, flags=re.MULTILINE)
        sql = re.sub(r'/\*.*?\*/', '', sql, flags=re.DOTALL)
        return ' '.join(sql.strip().split()).upper()
    
    return wrapper

使用示例:

from django.db.models.expressions import RawSQL

@block_modify_sql
def execute_raw_sql(sql, params=None):
    return RawSQL(sql, params)

# 调用时会自动校验
# execute_raw_sql("DELETE FROM my_table")  # 会抛出异常

额外建议

上述方案是应用层的防护,建议同时配置数据库层面的权限控制:给Django使用的数据库账号仅分配SELECT权限,即使应用层校验被绕过,数据库也会直接拒绝修改操作,形成双重安全保障。

内容的提问来源于stack exchange,提问作者Jay Jain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 02:30:44