我一直在尝试将 python3 插件 Flask_Talisman 实现到我当前的项目中。除了内容安全策略 (CSP) 之外,一切似乎都正常。问题是我需要从我的站点执行脚本(bootstrap、google reCaptcha 等),但它们被 CSP 阻止。
我的旧配置如下所示:
csp = {
'default-src': [
'\'self\''
],
'script-src': [
'\'self\'',
'unsafe-inline',
'https://cdn.jsdelivr.net/npm/',
'https://www.googletagmanager.com/gtag/',
'https://www.google.com/recaptcha/',
'https://www.gstatic.com/recaptcha/'
],
'frame-src': [
'unsafe-inline',
'https://www.google.com/recaptcha/',
'https://www.recaptcha.google.com.com/recaptcha/'
],
'style-src': [
'\'self\'',
'https://cdn.jsdelivr.net/npm/'
]
}
Talisman(app, content_security_policy=csp)
这不起作用。 所以我转而使用哈希,这是网上推荐的:
csp = {
'script-src': [
'\'sha256-pyVP/WTBWg0xeYb2xYVwRtZnbyqX6sWurV0uPjEvCN8=\'',
'\'sha256-fWJPdkYD1sLmgF1O8gp95DZx1j4F+AnK3vUr+ok3MQ8=\'',
'\'sha256-6WSoVabpxgBYdB7f22nyWr5xcn/Gy4BzHFxng1Z4Gvk=\'',
'\'sha256-wOhDwuiz1H/PNcPRBBYh/Udj5Jiune650+GU60FuIFI=\''
],
'frame-src': [
'\'sha256-6WSoVabpxgBYdB7f22nyWr5xcn/Gy4BzHFxng1Z4Gvk=\'',
'\'sha256-bwxrUkAWabRJI1ius9X3I0XsglPBvIQ5uU/V2h/wuhc=\''
],
'style-src': [
'\'self\'',
'https://cdn.jsdelivr.net/npm/'
],
'object-src': [
'\'none\''
]
}
Talisman(app, content_security_policy=csp)
我还尝试使用
unsafe-inline
标签,以及 unsafe-hashed
和许多其他标签,总是得到相同的结果:
Refused to load the stylesheet 'https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css' because it violates the following Content Security Policy directive:
在我的最后一次尝试中,我实现了一个随机数系统,它现在可以与一个函数一起使用;每次调用时它都会创建一个随机数,将其传递到我的index.html并告诉护身符它在哪里。 (我还尝试使用文档提供的
{{ csp_nonce() }}
Docs 示例 #6 函数,但这不起作用。)
所以:
def getNonce():
return os.urandom(16).hex()
然后在我的烧瓶路线中:
return render_template('index.html', message=message, nonce=getNonce())
配置:
csp = {
'script-src': '\'self\'',
'frame-src': '\'self\'',
'style-src': '\'self\'',
'object-src': '\'none\''
}
Talisman(app, content_security_policy=csp, content_security_policy_nonce_in=['script-src', 'style-src']
以及将
nonce={{ nonce }}
添加到我的 HTML 中的每个脚本标记中。
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX" nonce="{{ nonce }}"></script>
即使我一遍又一遍地尝试,我仍然停留在“相同”的错误消息上(只有当我在不安全内联、哈希或随机数之间切换时才会改变):
所以总是
Refused to execute inline script because it violates the following Content Security Policy directive:
如果您知道如何解决此问题,请告诉我,因为我不再知道了。网站本身运行没有问题,但一旦启用 Flask Talisman CSP,一切似乎都崩溃了。
再次阅读文档后,我读到了
Note that the CSP directive (script-src in the example) to which the nonce-... source should be added needs to be defined explicitly.
。因此,我尝试将 URL 重新添加回 CSP(不使用 unsafe-inline
)。新的配置现在看起来像这样:
newCsp = {
'default-src': '\'self\'',
'script-src': [
'https://cdn.jsdelivr.net/npm/',
'https://www.googletagmanager.com/gtag/',
'https://www.google.com/recaptcha/',
'https://www.gstatic.com/recaptcha/'
],
'frame-src': [
'https://www.google.com/recaptcha/',
'https://www.recaptcha.google.com.com/recaptcha/'
],
'style-src': [
'\'self\'',
'https://cdn.jsdelivr.net/npm/'
],
'object-src': '\'none\''
}
现在的问题是,虽然网站现在可以检索所有脚本(并且正在运行)并使用随机数,但它仍然会抛出此错误:
或以文字形式:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src https://cdn.jsdelivr.net/npm/ https://www.googletagmanager.com/gtag/ https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ 'nonce-bykkHFtoKjMIXmjxP4M28B6lSmJMa9XA'". Either the 'unsafe-inline' keyword, a hash ('sha256-h0FtDBdxOnu72uVQe4oUILMiGyexQqDT1jgToPa4Sog='), or a nonce ('nonce-...') is required to enable inline execution.
Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' https://cdn.jsdelivr.net/npm/". Either the 'unsafe-inline' keyword, a hash ('sha256-pyVP/WTBWg0xeYb2xYVwRtZnbyqX6sWurV0uPjEvCN8='), or a nonce ('nonce-...') is required to enable inline execution.
我无法解释,因为当再次删除 URL 时,我又回到了旧的错误。
在网上进行了更多搜索后,我发现了这篇文章。
由此,我尝试将随机数添加到 CSP 本身,并且它有效!所以现在我的代码看起来有点像这样:
def getCSP():
nonce = os.urandom(16).hex()
CSP = {'script-src': [f'nonce-{nonce}'],
'frame-src': ['https://www.google.com/recaptcha/', 'https://www.recaptcha.google.com.com/recaptcha/'],
'style-src': ['\'self\'', f'nonce-{nonce}'],
'object-src': '\'none\''}
return CSP, nonce
我使用此函数返回一个随机数和带有该随机数的新 CSP。
在我的 Flask 应用程序路径中,然后我使用该函数来获取随机数并将 CSP 设置为全局,以便 Flask_Talisman 可以读取它:
global CSP
CSP, nonce = getCSP()
return render_template('index.html', message=message, nonce=nonce)
然后在启动我的 Flask 应用程序之前:
CSP = {} # Intialize CSP before getting globalized
Talisman(app, content_security_policy=CSP, content_security_policy_nonce_in=['script-src', 'style-src'])
(注意,您仍然需要将
nonce={{ nonce }}
添加到每个 HTML <script >
标签)
完成所有这些后,我的网站终于备份并运行,控制台日志中没有任何错误消息!
我刚刚意识到设置
CSP={}
– 当然 – 只是禁用 CSP,因为它在启动时仅加载一次,并且不会更新。我稍后会处理这个问题。也许你们中的一些人有另一种解决方案。
我遇到了类似的问题,使用随机数是我的解决方案,也许这有帮助:
SELF = "'self'"
csp = {
'default-src': SELF,
'img-src': '*',
'script-src': [SELF,],
'style-src': [SELF,],
'font-src': [SELF,],
}
nonce_list = ['default-src', 'script-src']
talisman = Talisman(app, content_security_policy=csp, content_security_policy_nonce_in=nonce_list)
脚本如下:
<script nonce="{{ csp_nonce() }}" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>