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

如何为Flask Admin生成的内联JS添加Nonce以适配CSP?

在Flask Admin中为自动生成的JS添加CSP Nonce(无需手动覆盖所有模板)

我正尝试将CSP(内容安全策略)集成到Flask Admin框架中,但一直遇到问题。由于框架会自动生成JS代码,我想知道有没有不用手动覆盖所有模板的简便方法,给所有生成的脚本加上Nonce。

已实现的Nonce生成代码

@app.before_request
def renew_nonce():
    nonce = secrets.token_hex(32)
    g.nonce = nonce


@app.context_processor
def get_nonce():
    # 返回包含nonce值的字典
    nonce = g.nonce
    return {'nonce': nonce}

@app.after_request
def apply_security_headers(response):
    """设置安全头"""
    # 获取生成的随机nonce值
    nonce = g.nonce
    print("nonce after request: ", nonce)
    response.headers['Strict-Transport-Security'] = 'max-age=63072000; includeSubDomains'
    response.headers['Content-Security-Policy'] = f"default-src 'self'; \
        script-src 'self' https://cdnjs.cloudflare.com https://ajaxorg.github.io 'nonce-{nonce}'; \
        style-src 'self' 'unsafe-inline'; \
        img-src 'self' api.mapbox.com data:; \
        connect-src 'self'; \
        worker-src 'self' blob:;"
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'SAMEORIGIN'
    response.headers['X-XSS-Protection'] = '1; mode=block'
    return response

这段代码能在我自定义的模板里正常使用Nonce,但我不知道怎么把Nonce附加到Flask Admin框架自动生成的JS上。下面是我在Jinja2模板中使用Nonce的示例:

<script nonce="{{ nonce }}">
    // 业务代码
</script>

无需全量覆盖模板的解决方案

方案一:响应阶段修改HTML添加Nonce

通过在after_request钩子中解析HTML响应,为所有未带nonce的<script>标签添加属性,无需修改任何模板:

from flask import g
from bs4 import BeautifulSoup

@app.after_request
def add_nonce_to_flask_admin_scripts(response):
    # 仅处理HTML类型的响应
    if response.content_type.startswith('text/html'):
        soup = BeautifulSoup(response.get_data(), 'html.parser')
        nonce = g.get('nonce', '')
        # 遍历所有无nonce属性的script标签,添加nonce
        for script in soup.find_all('script', attrs={'nonce': False}):
            script['nonce'] = nonce
        # 更新响应内容
        response.set_data(soup.prettify())
    return response

使用前需要安装依赖:pip install beautifulsoup4。这种方式对现有代码侵入极小,直接在输出阶段统一处理所有脚本标签。

方案二:局部覆盖Flask Admin模板块

Flask Admin的模板采用继承机制,只需覆盖加载脚本的关键模板块,不用替换全部模板文件:

  1. 在你的模板目录下创建admin/base.html,内容如下:
{% extends 'admin/base.html' %}

{% block tail %}
    {% for script in admin_view.scripts %}
        <script src="{{ script }}" nonce="{{ nonce }}"></script>
    {% endfor %}
    {{ super() }}
{% endblock %}

{% block head %}
    {{ super() }}
    {% for script in admin_view.head_scripts %}
        <script src="{{ script }}" nonce="{{ nonce }}"></script>
    {% endfor %}
{% endblock %}
  1. 初始化Flask Admin时指定自定义基础模板:
from flask_admin import Admin

admin = Admin(app, template_mode='bootstrap4', base_template='admin/base.html')

这种方式更贴合Flask Admin的模板设计,性能比HTML解析更好,适合需要精细控制的场景。

注意事项

  • 确保CSP头中的'nonce-{nonce}'值和模板/响应中添加的nonce完全一致,否则脚本会被浏览器拦截。
  • 方案一的HTML解析方式对大型页面有轻微性能影响,但后台管理系统的页面通常不会有性能问题。
  • 你的上下文处理器已经将nonce注入全局模板上下文,所以两种方案都能直接获取到nonce值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 14:12:53