如何使用内容安全策略确保 Node.js 应用程序的安全

作者选择了自由软件基金会作为 写给捐款计划的一部分接受捐款。

介绍

当浏览器加载页面时,它执行大量的代码来渲染内容. 代码可能来自与根文档相同的来源,或不同的来源. 默认情况下,浏览器不会区分两者之间,并执行页面要求的任何代码,而不论来源。 攻击者使用此利用程序来恶意向页面注入脚本,然后执行,因为浏览器没有办法确定内容是否有害。

一个 CSP 是一个 HTTP 标题,它提供了一层额外的安全性,防止代码注入攻击,如 跨网站脚本(XSS), clickjacking和其他类似的利用。它有助于创建一个可信内容的可用列表,并阻止从允许列表中不存在的来源执行代码。它还向您选择的 URL 报告任何政策违规,以便您可以保持潜在的安全攻击。

使用 CSP 标题,您可以指定网站上内容的批准来源,浏览器可以加载。 任何不来自批准来源的代码将被阻止执行,这使得攻击者难以注入内容和数据。

在本教程中,您将通过实施一个CSP标题(例如Node.js应用程序)(https://github.com/do-community/csp-demo)来审查提供的不同保护,您还将收集CSP违规的JSON报告,以便快速捕捉问题并修复剥削。

前提条件

要遵循本教程,您将需要以下内容:

  • 最新版本的 Node.js 已安装在您的计算机上. 按照相关的 如何安装 Node.js 教程的步骤为您的操作系统设置 Node.js 开发环境。

您还应该使用最近的浏览器版本,最好是 Chrome,因为它在写这篇文章时(2020年11月)对 CSP 级别 3 指令提供了最佳的支持。

步骤 1 – 设置 Demo 项目

为了展示创建内容安全政策的过程,我们将通过为此(LINK0)的演示项目(https://github.com/finallyayo/csp-demo)的整个实施过程进行工作。 它是一个单页的网站,具有多种内容,大致相当于典型的网站或应用程序。 它包括一个小型的 Vue.js应用程序, YouTube 嵌入,以及一些来自 Unsplash的图像。 它还使用 Google 字体Bootstrap 框架,这是在 [内容交付网络(CDN)]上加载的(https://en.wikipedia.org/wiki/Content_delivery_network)。

在此步骤中,您将在测试服务器或本地机器上设置演示项目,并在浏览器中查看。

首先,使用以下命令将项目克隆到您的文件系统:

1git clone https://github.com/do-community/csp-demo

一旦您的项目目录已设置,请使用以下命令更改它:

1cd csp-demo

接下来,用下一个命令安装package.json文件中指定的依赖性,您使用express包来设置 Web 服务器,而nodemon帮助自动重新启动节点应用程序,当它在目录中检测到文件更改时:

1npm install

一旦依赖已安装依赖,请输入以下命令以在端口 5500 上启动 Web 服务器:

1npm start

您现在可以访问您的浏览器中的your_server_ip:5500localhost:5500,以查看演示页面.您将在页面上找到文本 Hello World!,YouTube嵌入版和一些图像。

CSP Demo

在下一节中,我们将实施一个仅涵盖最基本的保护的 CSP 策略,然后在下一节中我们将建立在这一基础上,因为我们揭示了我们需要在页面上允许的所有合法资源。

第2步:实施基本CSP

让我们继续写一个 CSP 策略,限制字体、图像、脚本、风格和嵌入式仅源于当前主机。

1Content-Security-Policy: default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self';

以下是本标题中的政策指令的解释:

  • font-src 定义了可以从哪里加载字体的来源。
  • img-src 定义了允许加载图像的来源。
  • script-src 控制了网页上的任何脚本加载权限。
  • style-src 是允许风格表来源的指令。
  • frame-src 定义了框架嵌入的允许来源。

在本示例中,所有指定的指令在其源列表中都被分配为自我的关键字,这表明只有来自当前主机的资源(包括 URL 方案和端口号码)才能执行,例如,script-src允许从当前主机执行脚本,但它阻止了所有其他脚本源。

让我们继续向我们的 Node.js 项目添加标题。

让您的应用程序运行,并打开一个新的终端窗口,以便与您的 server.js 文件工作:

1nano server.js

接下来,从 Express 中间件层中的示例中添加 CSP 标题,以确保您在服务器的每个响应中都包含标题:

 1[label server.js]
 2const express = require('express');
 3const bodyParser = require('body-parser');
 4const path = require('path');
 5const app = express();
 6
 7app.use(function (req, res, next) {
 8  res.setHeader(
 9    'Content-Security-Policy',
10    "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self'"
11  );
12  next();
13});
14
15app.use(bodyParser.json());
16app.use(express.static(path.join(__dirname)));
17
18app.get('/', (req, res) => {
19  res.sendFile(path.join(__dirname + '/index.html'));
20});
21
22const server = app.listen(process.env.PORT || 5500, () => {
23  const { port } = server.address();
24  console.log(`Server running on PORT ${port}`);
25});

保存文件并在浏览器中重新加载项目. 你会注意到页面完全破坏。

我们的 CSP 标题按预期工作,我们在页面上列出的所有外部来源都被阻止加载,因为它们违反了定义的策略,但是,这不是测试全新的策略的理想方式,因为在发生违规时可能会破坏网站。

Broken page after adding basic CSP

这就是为什么存在内容安全政策仅报告标题的原因。您可以使用它而不是内容安全政策,以防止浏览器执行该策略,同时仍然报告发生的违规行为,这意味着您可以改进该策略而不会危及您的网站。

继续,在您的 server.js 文件中替换内容安全政策标题为内容安全政策报告仅:

1nano server.js

添加以下突出的代码:

 1[label server.js]
 2. . .
 3app.use(function (req, res, next) {
 4  res.setHeader(
 5    'Content-Security-Policy-Report-Only',
 6    "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self'"
 7  );
 8  next();
 9});
10. . .

保存文件并在浏览器中重新加载页面. 该页面返回工作状态,但浏览器控制台仍然报告 CSP 违规行为。

Report only mode is now active

在本节中,我们创建了CSP的初始实现,并将其设置为仅报告模式,以便我们可以改进它而不会导致网站破坏。

步骤三:纠正政策违规行为

我们在上一节中实施的策略引发了多个违规行为,因为我们仅限制了所有资源的来源 - 然而,我们在页面上有几个第三方资产。

修复 CSP 违规的两种方法是:批准策略中的来源,或删除触发违规的代码. 由于合法资源正在触发所有违规,我们将在本节中主要关注前者选项。

打开浏览器控制台. 它会显示 CSP 的所有当前违规行为. 让我们解决这些问题。

允许风格表

控制台中的前两次侵犯来自于 Google 字体和 Bootstrap 风格表,您分别从 https://fonts.googleapis.comhttps://cdn.jsdelivr.net 加载,您可以通过 style-src 指令在页面上允许两者:

1style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net;

这规定了从原始主机的CSS文件, https://fonts.googleapis.com,和 https://cdn.jsdelivr.net,应该在页面上执行. 此政策是相当广泛的,因为它允许从允许列表域的任何风格表(不仅仅是你目前正在使用的)。

我们可以更具体地使用我们希望允许的确切文件或目录:

1style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css;

现在,它只允许准确的,指定的样式表执行. 它将阻止所有其他样式表,即使它们来自 https://cdn.jsdelivr.net

您可以更新 CSP 标题,如下所示,更新了 style-src 指令. 到您重新加载页面时,两种违规都将得到解决:

 1[label server.js]
 2. . .
 3app.use(function (req, res, next) {
 4  res.setHeader(
 5    'Content-Security-Policy-Report-Only',
 6    "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self';"
 7  );
 8  next();
 9});
10. . .

允许图像来源

您在页面上使用的图像来自一个单一的来源: https://images.unsplash.com. 让我们通过以下的 img-src 指令来允许:

 1[label server.js]
 2. . .
 3app.use(function (req, res, next) {
 4  res.setHeader(
 5    'Content-Security-Policy-Report-Only',
 6    "default-src 'self'; font-src 'self'; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self'"
 7  );
 8  next();
 9});
10. . .

允许YouTube嵌入

您可以通过frame-src指令允许嵌入式浏览环境的有效源,其中使用<iframe>等元素,如果这个指令不存在,浏览器将寻找child-src指令,然后返回默认-src指令。

让我们将https://www.youtube.com添加到允许列表,以便CSP不会阻止YouTube嵌入式的下载,一旦我们执行政策:

1frame-src 'self' https://www.youtube.com;

如果您有从 https://youtube.com 嵌入的域名,则将根据本政策被阻止,除非您还将 https://youtube.com 添加到允许列表中:

1frame-src 'self' https://www.youtube.com https://youtube.com;

以下是更新后的 CSP 标题. 如下修改frame-src指令:

 1[label server.js]
 2. . .
 3app.use(function (req, res, next) {
 4  res.setHeader(
 5    'Content-Security-Policy-Report-Only',
 6    "default-src 'self'; font-src 'self'; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
 7  );
 8  next();
 9});
10. . .

允许字体文件

Google fonts violations

Google 字体风格表中包含来自 https://fonts.gstatic.com 的几个字体文件的引用,您会发现这些文件目前违反了定义的 font-src 政策(目前是 self),因此您需要在修订的政策中考虑这些文件:

1font-src 'self' https://fonts.gstatic.com;

更新 CSP 标题中的font-src指令如下:

 1[label server.js]
 2. . .
 3app.use(function (req, res, next) {
 4  res.setHeader(
 5    'Content-Security-Policy-Report-Only',
 6    "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
 7  );
 8  next();
 9});
10. . .

重新加载页面后,控制台将不再报告 Google 字体文件的违规情况。

启用 Vue.js 脚本

CDN上加载的 Vue.js 脚本正在页面顶部渲染 Hello world! 文本. 我们将允许通过script-src指令在页面上执行它。

1script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js;

在本示例中,您只允许该 URL 中的准确脚本在页面上执行. 如果您想在开发和生产构建之间切换,则需要将其他脚本 URL 添加到允许列表中,或者您可以允许在/dist位置中存在的所有资源同时覆盖两种情况:

1script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/;

以下是更新后的 CSP 标题,包括相关的更改:

 1[label server.js]
 2. . .
 3app.use(function (req, res, next) {
 4  res.setHeader(
 5    'Content-Security-Policy-Report-Only',
 6    "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
 7  );
 8  next();
 9});
10. . .

此时,我们已成功允许我们页面依赖的所有外部文件和脚本,但我们仍然有另一个 CSP 违规问题要解决,因为页面上有内线脚本。

步骤4 - 处理内部来源

虽然您可以使用不安全的内线关键字在 CSP 中批准内线代码(如<script>标签中的JavaScript代码),但它不建议,因为它大大增加了代码注入攻击的风险。

此示例策略允许在页面上执行任何内行脚本,但由于上述原因,这并不安全。

1script-src 'self' 'unsafe-inline' https://unpkg.com/[email protected]/dist/;

避免使用unsafe-inline的最佳方法是将内线代码移动到外部文件并以这种方式引用它,这是缓存,缩小和可维护的更好的方法,并使CSP在未来更容易修改。

但是,如果您绝对必须使用线程代码,有两种主要方法可以安全地将它们添加到您的许可名单中。

选择1 - 使用一个哈希

此方法要求您计算基于脚本本身的 SHA 哈希,然后将其添加到script-src指令中。在最近的 Chrome 版本中,您甚至不需要自己生成该哈希,因为它已经包含在控制台中的 CSP 违规错误中:

1[Report Only] Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' https://unpkg.com/[email protected]/dist/". Either the 'unsafe-inline' keyword, a hash ('sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='), or a nonce ('nonce-...') is required to enable inline execution.

这里突出的部分是您需要在script-src指令中添加的精确的 SHA256 哈希,以允许执行触发违规的特定内线脚本。

复制哈希并将其添加到您的 CSP 如下:

1script-src 'self' https://unpkg.com/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM=';

这种方法的缺点是,如果脚本的内容发生变化,生成的哈希将是不同的,这将引发违规行为。

选项2 - 使用一个子

允许执行线程代码的第二种方法是使用 nonce. 这些是随机字符串,您可以使用它们来允许一个完整的代码块,无论其内容如何。

以下是使用 nonce 值的例子:

1script-src 'self' https://unpkg.com/[email protected]/dist/ 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

CSP 中的 nonce 值必须匹配脚本上的nonce属性:

1<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
2  // Some inline code
3</script>

每当页面被加载时,Nonces 必须是不可磨灭的和动态生成的,以便攻击者无法使用它们执行恶意脚本. 如果您决定实施此选项,则可以使用crypto包来生成一个Nonce:

1const crypto = require('crypto');
2let nonce = crypto.randomBytes(16).toString('base64');

我们将在本教程中选择哈希方法,因为它对我们的使用案例来说更实用。

在 CSP 标题中更新script-src指令,以如下所示,包括单个内线脚本的 SHA256 哈希:

 1[label server.js]
 2. . .
 3app.use(function (req, res, next) {
 4  res.setHeader(
 5    'Content-Security-Policy-Report-Only',
 6    "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://unpkg.com/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
 7  );
 8  next();
 9});
10. . .

这将删除内线脚本从控制台中触发的最终 CSP 违规错误。

在下一节中,我们将监测我们的CSP在生产环境中的影响。

步骤五:监控违规行为

例如,如果您忘记允许一个合法的来源在生产中,或者当攻击者试图利用XSS攻击引擎(您需要立即识别并停止)。

没有某种形式的积极报告,没有办法知道这些事件,这就是为什么报告到指令存在. 它指定一个位置,浏览器应该发布一个JSON格式的违规报告,如果它必须采取行动基于CSP。

要使用此指令,您需要添加一个额外的标题来指定 报告 API的终端点:

1Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"http://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}

一旦设置,请在报告到指令中指定组名称如下:

1report-to csp-endpoint;

以下是server.js文件的更改部分,请确保在Report-To标题中用您的实际服务器 IP 地址更换<your_server_ip>位置:

 1[label server.js]
 2. . .
 3app.use(function (req, res, next) {
 4  res.setHeader(
 5    'Report-To',
 6    '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://<your_server_ip>:5500/__cspreport__"}],"include_subdomains":true}'
 7  );
 8  res.setHeader(
 9    'Content-Security-Policy-Report-Only',
10    "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint;"
11  );
12  next();
13});
14. . .

报告到指令旨在取代现已废弃的报告到指令,但大多数浏览器尚未支持它(截至2020年11月)。因此,为了与当前浏览器的兼容性,同时确保与未来的浏览器发布支持的兼容性,您应该在您的 CSP 中指定报告到报告到

 1[label server.js]
 2. . .
 3app.use(function (req, res, next) {
 4  res.setHeader(
 5    'Report-To',
 6    '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}'
 7  );
 8  res.setHeader(
 9    'Content-Security-Policy-Report-Only',
10    "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint; report-uri /__cspreport__;"
11  );
12  next();
13});
14. . .

/__cspreport__路径也需要存在于服务器上;添加到您的文件中如下:

 1[label server.js]
 2. . .
 3app.get('/', (req, res) => {
 4  res.sendFile(path.join(__dirname + '/index.html'));
 5});
 6
 7app.post('/__cspreport__', (req, res) => {
 8  console.log(req.body);
 9});
10. . .

有些浏览器将报告的内容类型发送为应用程序/csp-report,而其他浏览器则使用应用程序/json

要计算所有可能的内容类型值,您必须在您的快递服务器上设置一些配置:

1[label server.js]
2. . .
3app.use(
4  bodyParser.json({
5    type: ['application/json', 'application/csp-report', 'application/reports+json'],
6  })
7);
8. . .

在此时,任何 CSP 违规行为将被发送到 /__cspreport__ 路线,并随后登录到终端。

您可以通过添加不符合当前 CSP 的源资源来尝试,或者如下所示修改index.html文件中的内线脚本:

 1[label index.html]
 2. . .
 3<script>
 4  new Vue({
 5    el: '#vue',
 6    render(createElement) {
 7      return createElement('h1', 'Hello World!');
 8    },
 9  });
10  console.log("Hello")
11</script>
12. . .

这会引发违规,因为脚本的哈希现在与您在CSP标题中所包含的不同。

以下是使用report-uri的浏览器的典型违规报告:

 1{
 2  'csp-report': {
 3    'document-uri': 'http://localhost:5500/',
 4    referrer: '',
 5    'violated-directive': 'script-src-elem',
 6    'effective-directive': 'script-src-elem',
 7    'original-policy': "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-uri /__cspreport__;",
 8    disposition: 'report',
 9    'blocked-uri': 'inline',
10    'line-number': 58,
11    'source-file': 'http://localhost:5500/',
12    'status-code': 200,
13    'script-sample': ''
14  }
15}

本报告的部分是:

  • document-uri:侵权发生的页面.
  • referrer: 该页面的引用者.
  • blocked-uri: 侵犯该页面策略的资源(在这种情况下是 inline 脚本)。
  • line-number: 内线代码开始的行编号.
  • violated-directive: 被侵犯的特定指令. (在这种情况下是 script-src-elem) original-policy: 页面的完整政策。

如果浏览器支持报告到指令,则实用负载应具有与以下相似的结构. 注意它与报告到实用负载如何不同,同时仍然携带相同的信息:

 1[{
 2    "age": 16796,
 3    "body": {
 4    	"blocked-uri": "https://vimeo.com",
 5    	"disposition": "enforce",
 6    	"document-uri": "https://localhost:5500/",
 7    	"effective-directive": "frame-src",
 8    	"line-number": 58,
 9    'original-policy': "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-uri /__cspreport__;",
10    	"referrer": "",
11    	"script-sample": "",
12    	"sourceFile": "https://localhost:5500/",
13    	"violated-directive": "frame-src"
14    },
15    "type": "csp",
16    "url": "https://localhost:5500/",
17    "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
18}]

<$>[注] :报告到指令仅在安全环境中得到支持,这意味着您需要用有效的 HTTPS 证书设置 Express 服务器,否则您将无法测试或使用它。

在本节中,我们成功地在我们的服务器上设置了CSP监控,以便我们能够快速检测和修复问题。

步骤 6 – 发布最终政策

一旦您确信 CSP 已正确设置(理想情况下,在仅在报告模式下运行几天或几周后),您可以通过将 CSP 标题从内容安全政策报告仅更改为内容安全政策来执行。

除了报告违规行为外,这还会阻止未经授权的资源在页面上运行,从而为您的访问者提供更安全的体验。

以下是「server.js」文件的最终版本:

 1[label server.js]
 2const express = require('express');
 3const bodyParser = require('body-parser');
 4const path = require('path');
 5const app = express();
 6
 7app.use(function (req, res, next) {
 8  res.setHeader(
 9    'Report-To',
10    '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"http://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}'
11  );
12  res.setHeader(
13    'Content-Security-Policy',
14    "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/[email protected]/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint; report-uri /__cspreport__;"
15  );
16  next();
17});
18
19app.use(
20  bodyParser.json({
21    type: [
22      'application/json',
23      'application/csp-report',
24      'application/reports+json',
25    ],
26  })
27);
28app.use(express.static(path.join(__dirname)));
29
30app.get('/', (req, res) => {
31  res.sendFile(path.join(__dirname + '/index.html'));
32});
33
34app.post('/__cspreport__', (req, res) => {
35  console.log(req.body);
36});
37
38const server = app.listen(process.env.PORT || 5500, () => {
39  const { port } = server.address();
40  console.log(`Server running on PORT ${port}`);
41});

<$>[注] 浏览器支持 CSP 标题是 在所有浏览器中支持 除了 Internet Explorer,它使用非标准的X-Content-Security-Policy标题。

最新版本的 CSP 规范( 3 级)(https://www.w3.org/TR/CSP3/)也引入了一些较新的指令,这些指令目前没有得到很好的支持。 示例包括script-src-elemprefetch-src指令。

结论

在本文中,您为 Node.js 应用程序设置了有效的内容安全策略,并监控了违规行为. 如果您有较旧或更复杂的网站,则需要更广泛的策略设置,涵盖所有基础。

您可以找到本教程的最终代码在 这个GitHub存储库

有关安全的更多文章,请参阅我们的 安全主题页面。如果您想了解有关使用 Node.js 的更多信息,您可以阅读我们的 如何在 Node.js 系列中编码

Published At
Categories with 技术
Tagged with
comments powered by Disqus