org.kohsuke.stapler.jelly.JellyFacet.TRACE = true
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 视图)中出现的任何 onclick、onload 等内容都有问题。
checkUrl 定义检查 checkUrl 属性的值是否为非简单 URL,而是 JavaScript 表达式。虽然底层支持是在 Jenkins 核心中实现的,而不是在插件中,但没有 'unsafe-eval' 在 script-src CSP 指令中,这将导致失败。您可以轻松地通过在双引号的属性值中使用单引号(或极少数情况下的反之)来识别它们。
eval检查任何 JS 资源文件中的 eval 用法(可能在解决了前面列出的问题后)。在 script-src CSP 指令中没有 'unsafe-eval' 的情况下,这将导致失败。
setTimeout 和 setInterval检查 setTimeout 和 setInterval 的用法,其中第一个参数是字符串。在 script-src CSP 指令中没有 'unsafe-eval' 的情况下,这将导致失败。
FormApply#applyResponse此方法期望将 JavaScript 片段作为参数执行。
f:validateButton 检查 URL 中使用 script HTTP 响应标头此标头用于返回将在客户端执行的 JavaScript 代码。
从其他域加载的任何图像、脚本、样式等都会引起问题。资源应由 Jenkins 托管。
在运行 Jenkins 时,您可以使用以下技术来识别损坏的功能及其定义的组件
在开发模式下(例如,mvn hpi:run)运行 Jenkins 2.539 或更高版本将默认启用内容安全策略保护。了解更多。
在 Jenkins 2.539 之前的版本中,Content Security Policy Plugin (1.x) 允许您定义一个应用于 Jenkins Web UI 的 Content-Security-Policy。它可以作为强制执行模式或仅收集报告模式运行。这两种模式在识别损坏的功能方面都很有用。
在脚本控制台中运行以下脚本
org.kohsuke.stapler.jelly.JellyFacet.TRACE = true
此属性已在 视图 中记录,并在 Jelly 视图的渲染 HTML 中发出注释,让您更好地了解视图的组成方式,并可能更轻松地识别负责贡献违反 CSP 规则代码的组件。
不要在 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。
onclick 或 onblur 等事件处理程序应在单独的文件中定义。
要使此功能正常工作,原本具有内联事件处理程序属性的元素需要一个类或 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
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 中),并指定预期的 name 和 value,并在 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);
setTimeout 和 setIntervalsetTimeout 和 setInterval 的第一个参数应该是函数,而不是字符串。以下代码片段显示了正确的用法。
/* If you don't need to pass arguments to the function: */
setTimeout(functionName, 1000);
/* Or, more generally: */
setTimeout(function() {
// do something
}, 1000);
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 默认会强制执行内容安全策略,因此插件维护者很可能会在自己的测试中遇到不兼容性。 |
Content Security Policy Plugin (1.x) 允许您定义一个应用于 Jenkins Web UI 的 Content-Security-Policy。它可以作为强制执行模式或仅收集报告模式运行。这两种模式在识别损坏的功能方面都很有用。