是否存在Django配置可阻止JS调用外部API并设置域名白名单?
实现Django外部资源域名白名单校验
当然可以通过在settings.py中定义白名单,来限制HTML模板中可加载的外部域名资源。下面提供两种实用方案,适配不同协作场景:
方案一:自定义模板标签(推荐,主动校验)
这种方式要求前端开发者通过自定义标签引用外部脚本,从源头控制非法资源加载,适合多人协作的项目场景。
- 在settings.py中配置白名单
# settings.py ALLOWED_EXTERNAL_SCRIPT_DOMAINS = [ "example.com", "cdn.jsdelivr.net", "cdnjs.cloudflare.com" ] # 可选:开启后允许白名单域名的所有子域名 ALLOW_EXTERNAL_SUBDOMAINS = True
- 创建自定义模板标签
在你的Django应用下新建templatetags/external_scripts.py文件:
from django import template from django.conf import settings from urllib.parse import urlparse register = template.Library() @register.simple_tag def allowed_script(src): # 相对路径或本地资源直接放行 if not src.startswith(('http://', 'https://')): return f'<script src="{src}"></script>' parsed_url = urlparse(src) domain = parsed_url.netloc.lower() # 校验域名是否在白名单内 allowed = False for allowed_domain in settings.ALLOWED_EXTERNAL_SCRIPT_DOMAINS: allowed_domain = allowed_domain.lower() if settings.ALLOW_EXTERNAL_SUBDOMAINS: if domain.endswith(f'.{allowed_domain}') or domain == allowed_domain: allowed = True break else: if domain == allowed_domain: allowed = True break if allowed: return f'<script src="{src}"></script>' else: # 可选:返回空内容或抛出模板语法错误 return '' # 也可改为 raise template.TemplateSyntaxError(f"Domain {domain} not allowed")
- 在模板中使用
先加载标签库,再用自定义标签替代原生script标签:
{% load external_scripts %} {% allowed_script "https://example.com/script.js" %} {% allowed_script "https://unallowed.com/script.js" %} {# 该标签会输出空内容,即资源被阻止 #}
方案二:响应中间件(被动校验,无需修改模板)
如果不想改动现有模板写法,可以通过中间件在响应返回前扫描HTML内容,自动移除非法的script标签。
- 安装HTML解析依赖
pip install beautifulsoup4
- 编写中间件
在项目的middleware.py中添加:
from django.conf import settings from urllib.parse import urlparse from bs4 import BeautifulSoup class ExternalScriptWhitelistMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): response = self.get_response(request) # 仅处理HTML类型的响应 if 'text/html' not in response.get('Content-Type', ''): return response soup = BeautifulSoup(response.content, 'html.parser') scripts = soup.find_all('script', src=True) for script in scripts: src = script['src'] if not src.startswith(('http://', 'https://')): continue parsed_url = urlparse(src) domain = parsed_url.netloc.lower() allowed = False for allowed_domain in settings.ALLOWED_EXTERNAL_SCRIPT_DOMAINS: allowed_domain = allowed_domain.lower() if settings.ALLOW_EXTERNAL_SUBDOMAINS: if domain.endswith(f'.{allowed_domain}') or domain == allowed_domain: allowed = True break else: if domain == allowed_domain: allowed = True break if not allowed: script.decompose() # 移除非法的script标签 response.content = str(soup).encode(response.charset) return response
- 注册中间件
在settings.py的MIDDLEWARE列表中添加中间件路径:
MIDDLEWARE = [ # ... 其他已有中间件 'your_app.middleware.ExternalScriptWhitelistMiddleware', ]
额外注意事项
- 若需要校验CSS等其他外部资源,可扩展上述逻辑,处理
<link>标签的href属性。 - 本地开发时,建议将
localhost、127.0.0.1加入白名单,避免影响调试。 - 方案二的中间件会增加响应处理耗时,高流量场景优先选择方案一。
内容的提问来源于stack exchange,提问作者jxw
相关产品推荐
相关产品推荐

