From b888c66ca29871be337d4302957ec3f8db0836a1 Mon Sep 17 00:00:00 2001
From: Ry0taK <49341894+Ry0taK@users.noreply.github.com>
Date: Sat, 11 Feb 2023 21:20:45 +0900
Subject: [PATCH] =?UTF-8?q?Content-Security-Policy-Report-Only=E3=82=92?=
=?UTF-8?q?=E8=BF=BD=E5=8A=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.config/docker_example.yml | 3 ++
.config/example.yml | 3 ++
gulpfile.js | 10 ++++-
packages/backend/src/config.ts | 2 +
.../src/server/web/ClientServerService.ts | 8 ++++
packages/backend/src/server/web/boot.js | 5 ++-
packages/backend/src/server/web/flush.js | 44 +++++++++++++++++++
.../backend/src/server/web/views/base.pug | 7 +--
.../backend/src/server/web/views/bios.pug | 3 +-
packages/backend/src/server/web/views/cli.pug | 3 +-
.../backend/src/server/web/views/flush.pug | 44 +------------------
11 files changed, 76 insertions(+), 56 deletions(-)
create mode 100644 packages/backend/src/server/web/flush.js
diff --git a/.config/docker_example.yml b/.config/docker_example.yml
index f8124bc9df..e51d7e8f5f 100644
--- a/.config/docker_example.yml
+++ b/.config/docker_example.yml
@@ -144,3 +144,6 @@ signToActivityPubGet: true
# Upload or download file size limits (bytes)
#maxFileSize: 262144000
+
+# Value of Content-Security-Policy header
+#contentSecurityPolicy: "script-src 'self' 'unsafe-eval' https://challenges.cloudflare.com https://hcaptcha.com https://*.hcaptcha.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.recaptcha.net/recaptcha/; base-uri 'self'; object-src 'self';"
diff --git a/.config/example.yml b/.config/example.yml
index a19b5d04e8..d9f6238219 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -145,3 +145,6 @@ signToActivityPubGet: true
# Upload or download file size limits (bytes)
#maxFileSize: 262144000
+
+# Value of Content-Security-Policy header
+#contentSecurityPolicy: "script-src 'self' 'unsafe-eval' https://challenges.cloudflare.com https://hcaptcha.com https://*.hcaptcha.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.recaptcha.net/recaptcha/; base-uri 'self'; object-src 'self';"
diff --git a/gulpfile.js b/gulpfile.js
index a04ab4c1ad..d08b04eae7 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -36,12 +36,18 @@ gulp.task('copy:frontend:locales', cb => {
});
gulp.task('build:backend:script', () => {
- return gulp.src(['./packages/backend/src/server/web/boot.js', './packages/backend/src/server/web/bios.js', './packages/backend/src/server/web/cli.js'])
+ const clientManifestExists = fs.existsSync('./built/_vite_/manifest.json');
+ const clientEntry = clientManifestExists ?
+ JSON.parse(fs.readFileSync('./built/_vite_/manifest.json', 'utf-8'))['src/init.ts'].file
+ : 'src/init.ts'
+
+ return gulp.src(['./packages/backend/src/server/web/boot.js', './packages/backend/src/server/web/bios.js', './packages/backend/src/server/web/cli.js', './packages/backend/src/server/web/flush.js'])
.pipe(replace('LANGS', JSON.stringify(Object.keys(locales))))
+ .pipe(replace('CLIENT_ENTRY', JSON.stringify(clientEntry)))
.pipe(terser({
toplevel: true
}))
- .pipe(gulp.dest('./packages/backend/built/server/web/'));
+ .pipe(gulp.dest('./built/_frontend_dist_/'));
});
gulp.task('build:backend:style', () => {
diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index aa98ef1d22..4309f67cb3 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -48,6 +48,8 @@ export type Source = {
allowedPrivateNetworks?: string[];
+ contentSecurityPolicy?: string;
+
maxFileSize?: number;
accesslog?: string;
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index c69ee33ea3..7f1a43792f 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -172,6 +172,14 @@ export class ClientServerService {
fastify.addHook('onRequest', (request, reply, done) => {
// クリックジャッキング防止のためiFrameの中に入れられないようにする
reply.header('X-Frame-Options', 'DENY');
+
+ // XSSが存在した場合に影響を軽減する
+ // (script-srcにunsafe-inline等を追加すると意味が無くなるので注意)
+ const csp = this.config.contentSecurityPolicy
+ ?? 'script-src \'self\' \'unsafe-eval\' ' +
+ 'https://challenges.cloudflare.com https://hcaptcha.com https://*.hcaptcha.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.recaptcha.net/recaptcha/; ' +
+ 'base-uri \'self\'; object-src \'self\';';
+ reply.header('Content-Security-Policy', csp);
done();
});
diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index c6cb25e43a..4b9565f4c7 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -154,7 +154,7 @@