Content-Security-Policy 兼容性

简介

Content-Security-Policy(内容安全策略)是现代浏览器用于增强文档(或网页)安全性的 HTTP 响应头名称。Content-Security-Policy 标头允许您限制可以加载的资源(例如 JavaScript、CSS、图像等)以及它们可以加载的 URL。

使用 Content-Security-Policy (CSP) 可以防止诸如 跨站脚本 (XSS) 之类的注入攻击。

截至 2025 年,Jenkins (核心) UI 兼容允许防止此类注入攻击的 CSP 指令,但许多插件不兼容。本指南记录了如何识别与 CSP 规则不兼容的组件,以及如何以与 Jenkins 对其 UI 执行 CSP 保护兼容的方式编写和调整 UI 代码。

静态识别受影响的代码和插件

要检查插件的 CSP 兼容性,请查找以下代码模式

内联 <script>

引用文件以加载 JavaScript 是可以的,但内联块内容不行。所有不带 src<script> 标签都有问题。在 script-src CSP 指令中没有 'unsafe-inline' 的情况下,它们将无法正常工作。

带有不允许的 var 属性的独立 st:bind 标签

CSP 兼容的 <st:bind> 标签仅允许使用简单的 JavaScript 标识符。

内联事件处理程序定义

HTML 文件(例如 Jelly 和 Groovy 视图)中出现的任何 onclickonload 等内容都有问题。

旧版 checkUrl 定义

检查 checkUrl 属性的值是否为非简单 URL,而是 JavaScript 表达式。虽然底层支持是在 Jenkins 核心中实现的,而不是在插件中,但没有 'unsafe-eval'script-src CSP 指令中,这将导致失败。您可以轻松地通过在双引号的属性值中使用单引号(或极少数情况下的反之)来识别它们。

使用 eval

检查任何 JS 资源文件中的 eval 用法(可能在解决了前面列出的问题后)。在 script-src CSP 指令中没有 'unsafe-eval' 的情况下,这将导致失败。

使用带字符串参数的 setTimeoutsetInterval

检查 setTimeoutsetInterval 的用法,其中第一个参数是字符串。在 script-src CSP 指令中没有 'unsafe-eval' 的情况下,这将导致失败。

使用 FormApply#applyResponse

此方法期望将 JavaScript 片段作为参数执行。

f:validateButton 检查 URL 中使用 script HTTP 响应标头

此标头用于返回将在客户端执行的 JavaScript 代码。

从其他域加载的资源

从其他域加载的任何图像、脚本、样式等都会引起问题。资源应由 Jenkins 托管。

动态识别插件

在运行 Jenkins 时,您可以使用以下技术来识别损坏的功能及其定义的组件

Jenkins 2.539+ 内置 CSP 保护

在开发模式下(例如,mvn hpi:run)运行 Jenkins 2.539 或更高版本将默认启用内容安全策略保护。了解更多。

Content Security Policy Plugin 1.x

在 Jenkins 2.539 之前的版本中,Content Security Policy Plugin (1.x) 允许您定义一个应用于 Jenkins Web UI 的 Content-Security-Policy。它可以作为强制执行模式或仅收集报告模式运行。这两种模式在识别损坏的功能方面都很有用。

仅报告模式

当设置为仅报告问题而不强制执行时,UI 可以正常使用,同时让浏览器报告违规行为,从而快速收集大量发现,而不会被损坏的功能阻止。之后,可以审查报告的违规行为,从而识别受影响的功能。导航到管理 Jenkins » Content-Security-Policy Report 并在此处审查违规行为。

如果您在 UI 上遇到问题但报告为空,请务必检查浏览器的控制台是否有与报告 CSP 违规相关的错误。例如,某些浏览器扩展会禁用 CSP 报告。

强制执行模式

设置为强制执行规则时,如果正在访问的功能受到规则的影响,UI 将更容易损坏。

启用 Jelly 跟踪

在脚本控制台中运行以下脚本

org.kohsuke.stapler.jelly.JellyFacet.TRACE = true

此属性已在 视图 中记录,并在 Jelly 视图的渲染 HTML 中发出注释,让您更好地了解视图的组成方式,并可能更轻松地识别负责贡献违反 CSP 规则代码的组件。

开发指南

内联 JavaScript 块

一般性建议

不要在 Jenkins GUI 中使用内联 JavaScript (JS),即嵌入在 HTML 输出中的 JS。

这通常是通过 <script> 标签完成的,如下所示

<script type="text/javascript">
alert("Hello, world!");
</script>

关于 XSS 防护文档中的指南对于将参数传递给 JavaScript 或以其他方式动态控制其行为可能很有用。

通常可以使用 Stapler 插件 来加载与 UI 视图相关的​​文件,并确保它们仅加载一次。

一个例子是 jenkinsci/jenkins#6849

内联 st:bind 标签

如果您在内联 JavaScript 中使用 <st:bind> 标签,请添加 var 属性以将变量设置为指定名称,并在您自己的脚本中引用该变量。使用简单的 JavaScript 标识符(符合此正则表达式)作为 var 值,而不是更复杂的表达式。

确保在从内联 JS 中提取多个或重复执行的 <st:bind/> 标签时不要重复使用相同的 var 值。您可以使用 ${h.generateId()} 来生成唯一值。

一个例子是 warnings-ng-plugin#1862

带有不允许的 var 属性的独立 st:bind 标签

<st:bind> 的输出取决于其调用方式。有关为独立使用(而不是在内联 JS 中)添加 var 属性的建议,请参阅上一节。

除了添加 var 属性外,其值必须符合此正则表达式才能生成 CSP 兼容的 <script> 标签。

示例请参见 build-monitor-plugin#830

内联事件处理程序

onclickonblur 等事件处理程序应在单独的文件中定义。

要使此功能正常工作,原本具有内联事件处理程序属性的元素需要一个类或 ID,以便从 JS 中查找它。

根据该元素添加到 UI 的方式,您将使用以下方法之一来添加事件处理程序

您可以使用 document.addEventListener('DOMContentLoaded', …) 来处理页面加载时存在的一个或多个元素。通过 ID 或类名等特征查找元素,然后对它们调用 #addEventListener。请注意 Jenkins 的可扩展性,因此请考虑在元素类名或 ID 中包含插件名称,以防止与其他插件发生意外冲突。

使用 Behaviour#specify 为可能动态添加到页面的元素添加事件处理程序,例如作为 AJAX 响应的一部分。常见的实例是配置表单:hetero-list 等常见表单元素使用 renderOnDemand 来仅在表单更改时加载页面的一部分。将内容动态添加到页面的 AJAX 响应代码需要对新添加的内容调用 Behaviour#applySubtree

对于曾经调用 return false 以阻止发生常规操作(例如链接导航)的 onclick 等事件处理程序,请在提供的 Event 参数上调用 Event.preventDefault()

此类示例包括: jenkinsci/jenkins#5514

旧版 JavaScript checkUrl 验证

不要使用“旧版”模式表单验证,该模式支持带有手动指定的 checkUrl 参数的内联 JS。它看起来像这样

<f:textbox checkUrl="'${rootURL}/${h.jsStringEscape(it.url)}checkText?value='+encodeURIComponent(this.value)+'"  />

这结合了内联 JS 和使用 Jelly 中的 JEXL 表达式构建字符串部分,并以不同的方式转义内容的各个部分以防止注入漏洞。

相反,使用现代 checkUrl 模式,它在 Jenkins 2.360 中要求设置 checkDependsOn 属性(但它可以是空字符串)。此模式将自动将当前表单元素的值添加为名为 value 的查询参数,因此上面的示例可以简化为以下内容

<f:textbox checkUrl="${rootURL}/${it.url}checkText" checkDependsOn=""  />

要传递其他值,请将相应的表单字段名称指定为 checkDependsOn 字符串的一部分。

如果您需要传递未表示为表单字段的参数,则自 Jenkins 2.360 起,存在以下选项

  • 定义一个新的表单验证端点。当值为布尔值时(2 个端点而不是 1 个),这可能是一个可行的选项。

  • 定义一个隐藏表单字段(将其包装在 f:invisibleEntry 中),并指定预期的 namevalue,并在 checkDependsOn 中指定。确保在其他地方忽略它。请参见 jenkinsci/jenkins#6859 示例。

eval 调用

不应使用 eval 将字符串解释为 JS 代码。

根据您的用例,可能有不同的解决方案。

要解析 JSON,请改用 JSON.parse。示例请参见 jenkinsci/jenkins#6868

要调用回调,请让调用者定义一个全局函数并将其名称作为参数传递。然后您的代码可以像这样调用回调

/* someone else provides this */
let callbackName = 'foo';
/* invoke it with arguments */
window[callbackName](args);

带字符串参数的 setTimeoutsetInterval

setTimeoutsetInterval 的第一个参数应该是函数,而不是字符串。以下代码片段显示了正确的用法。

/* If you don't need to pass arguments to the function: */
setTimeout(functionName, 1000);
/* Or, more generally: */
setTimeout(function() {
  // do something
}, 1000);

FormApply#applyResponse 调用

使用 FormApply#showNotification,它已在 Jenkins 2.482 中添加。

f:validateButton 检查 URL 的 script HTTP 响应标头

依赖 Jenkins 2.540 或更高版本,并改用 X-Jenkins-ValidateButton-Callback HTTP 响应标头。请参见 jenkinsci/jenkins#20345

或者,取决于您将此功能用于何种目的,完全可以在客户端解决。请参见 jenkinsci/gitee-plugin#149

从其他域加载的资源

将图像、脚本和样式移至您插件的资源中。这还将使 Jenkins 在没有互联网连接或互联网连接有限的环境中更好地工作。

任何动态确定的图像(例如,基于用户配置的“头像”图像,如 GitHub 组织或基于配置的安全领域的用户头像)可以通过以下方式处理

  • 让 Jenkins 请求(并可能缓存)这些图像,通过本地 URL 提供服务。对于图像,请考虑使用 SCM API 插件中的 jenkins.scm.impl.avatars.AvatarCache。否则,请注意不要允许参数化提供图像的 URL,使其接受任意参数值,从而导致代理任意 URL。

  • 为了与 Jenkins 2.539 及更高版本中的内容安全策略兼容,请实现 jenkins.security.csp.Contributor(或简单情况下的 jenkins.security.csp.SimpleContributor)。这将允许 Jenkins 用户浏览器从已知安全域加载图像。在这种情况下,请确保只有管理员最终可以配置可以从中加载图像的域。例如,普通 Jenkins 用户不应能够,例如,编辑其用户配置文件或以某种方式配置作业以允许他们选择的域。

测试

在 Jenkins 2.539 及更高版本中

Jenkins 2.539 及更高版本开箱即支持内容安全策略。有关如何设置的信息,请参阅文档

在开发模式下运行 Jenkins 默认会强制执行内容安全策略,因此插件维护者很可能会在自己的测试中遇到不兼容性。

在旧版 Jenkins 中

Content Security Policy Plugin (1.x) 允许您定义一个应用于 Jenkins Web UI 的 Content-Security-Policy。它可以作为强制执行模式或仅收集报告模式运行。这两种模式在识别损坏的功能方面都很有用。

© . This site is unofficial and not affiliated with The Linux Foundation.