summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-02-15 22:14:03 +0100
committerArmand Philippot <git@armandphilippot.com>2022-02-15 22:25:12 +0100
commit9eae4703c97c50e82d959a3e0859fe1553889b15 (patch)
tree46605bbd1911ef370cc460d6710ad0ff87782e73
parent4dc0005999c72b78d195bc05193926328030fe78 (diff)
feat: add HTTP security headers
I also renamed and changed the format of some environment variables so I can reuse them inside the CSP security header.
-rw-r--r--.env.example29
-rw-r--r--next-sitemap.js2
-rw-r--r--next.config.js77
-rw-r--r--src/utils/config.ts4
-rw-r--r--src/utils/helpers/rss.ts6
5 files changed, 97 insertions, 21 deletions
diff --git a/.env.example b/.env.example
index 8572b45..3200101 100644
--- a/.env.example
+++ b/.env.example
@@ -1,19 +1,22 @@
-FRONTEND_URL="https://www.frontend.com"
-BACKEND_URL="https://www.backend.com"
-GRAPHQL_ENDPOINT="/graphql"
+APP_FRONTEND_DOMAIN="www.frontend.com"
+APP_BACKEND_DOMAIN="www.backend.com"
+APP_PROTOCOL="https"
+APP_GRAPHQL_ENDPOINT="/graphql"
+APP_AUTHOR_NAME="Your Name"
+APP_AUTHOR_EMAIL="your@email.com"
+APP_AUTHOR_URL="https://www.yourWebsite.com/"
+APP_FEED_DESCRIPTION="What you want..."
-AUTHOR_NAME="Your Name"
-AUTHOR_EMAIL="your@email.com"
-AUTHOR_URL="https://www.yourWebsite.com/"
-FEED_DESCRIPTION="What you want..."
-
-NEXT_PUBLIC_FRONTEND_URL="$FRONTEND_URL"
-NEXT_PUBLIC_GRAPHQL_API="$BACKEND_URL$GRAPHQL_ENDPOINT"
-
-NEXT_PUBLIC_MATOMO_SITE_ID=0
-NEXT_PUBLIC_MATOMO_URL="https://matomoUrl.com"
+NEXT_PUBLIC_APP_DOMAIN="$APP_FRONTEND_DOMAIN"
+NEXT_PUBLIC_APP_PROTOCOL="$APP_PROTOCOL"
+NEXT_PUBLIC_GRAPHQL_API="https://$APP_BACKEND_DOMAIN$APP_GRAPHQL_ENDPOINT"
+NEXT_PUBLIC_MATOMO_SITE_ID=1
+NEXT_PUBLIC_MATOMO_DOMAIN="www.analyticsDomain.com"
# Use this only in development mode. It prevents "unable to verify the first
# certificate" error when using a local domain with mkcert certificate for
# backend.
#NODE_TLS_REJECT_UNAUTHORIZED=0
+
+# Set node environment to not install dev dependencies.
+#NODE_ENV=production
diff --git a/next-sitemap.js b/next-sitemap.js
index 9f7346d..c9cd443 100644
--- a/next-sitemap.js
+++ b/next-sitemap.js
@@ -1,7 +1,7 @@
/** @type {import('next-sitemap').IConfig} */
module.exports = {
- siteUrl: process.env.NEXT_PUBLIC_FRONTEND_URL,
+ siteUrl: `${process.env.NEXT_PUBLIC_APP_PROTOCOL}://${process.env.NEXT_PUBLIC_APP_DOMAIN}`,
generateRobotsTxt: true,
changefreq: null,
priority: null,
diff --git a/next.config.js b/next.config.js
index 4a3ca39..f3a3b2c 100644
--- a/next.config.js
+++ b/next.config.js
@@ -1,12 +1,85 @@
const path = require('path');
-const backendDomain = process.env.BACKEND_URL.split('//')[1];
+const backendDomain = process.env.APP_BACKEND_DOMAIN;
+const frontendDomain = process.env.APP_FRONTEND_DOMAIN;
+const matomoDomain = process.env.NEXT_PUBLIC_MATOMO_DOMAIN;
+
+const contentSecurityPolicy = `
+ default-src 'self' ${backendDomain};
+ child-src: 'self' *.${frontendDomain.replace('www.', '')};
+ connect-src 'self' ${backendDomain} api.github.com;
+ font-src 'self';
+ frame-src 'self' ${matomoDomain};
+ img-src 'self' ${backendDomain} secure.gravatar.com data:;
+ media-src 'self' data:;
+ script-src 'self' ${matomoDomain} 'unsafe-inline';
+ style-src 'self' 'unsafe-inline';
+`;
+
+const contentSecurityPolicyDev = `
+ default-src 'self' ${backendDomain};
+ child-src: 'self' *.${frontendDomain.replace('www.', '')};
+ connect-src 'self' ${backendDomain} api.github.com;
+ font-src 'self';
+ frame-src 'self' ${matomoDomain};
+ img-src 'self' ${backendDomain} secure.gravatar.com data:;
+ media-src 'self' data:;
+ script-src 'self' ${matomoDomain} 'unsafe-inline' 'unsafe-eval';
+ style-src 'self' 'unsafe-inline';
+`;
+
+const securityHeaders = [
+ {
+ key: 'X-DNS-Prefetch-Control',
+ value: 'on',
+ },
+ {
+ key: 'Strict-Transport-Security',
+ value: 'max-age=31536000; includeSubDomains; preload',
+ },
+ {
+ key: 'X-XSS-Protection',
+ value: '1; mode=block',
+ },
+ {
+ key: 'X-Frame-Options',
+ value: 'SAMEORIGIN',
+ },
+ {
+ key: 'Permissions-Policy',
+ value: 'camera=(), microphone=(), geolocation=(), interest-cohort=()',
+ },
+ {
+ key: 'X-Content-Type-Options',
+ value: 'nosniff',
+ },
+ {
+ key: 'Referrer-Policy',
+ value: 'strict-origin-when-cross-origin',
+ },
+ {
+ key: 'Content-Security-Policy',
+ value:
+ process.env.NODE_ENV !== 'development'
+ ? contentSecurityPolicy.replace(/\s{2,}/g, ' ').trim()
+ : contentSecurityPolicyDev.replace(/\s{2,}/g, ' ').trim(),
+ },
+];
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
scrollRestoration: true,
},
+ async headers() {
+ return [
+ {
+ // Apply these headers to all routes in your application.
+ source: '/:path*',
+ headers: securityHeaders,
+ },
+ ];
+ },
i18n: {
locales: ['fr'],
defaultLocale: 'fr',
@@ -30,7 +103,7 @@ const nextConfig = {
path.join(__dirname, 'node_modules'),
],
},
- webpack: (config, { dev }) => {
+ webpack: (config) => {
config.module.rules.push(
{
test: /\.pdf/,
diff --git a/src/utils/config.ts b/src/utils/config.ts
index 07d42a3..86701fe 100644
--- a/src/utils/config.ts
+++ b/src/utils/config.ts
@@ -14,10 +14,10 @@ export const settings = {
supported: ['en', 'fr'],
},
matomo: {
- urlBase: process.env.NEXT_PUBLIC_MATOMO_URL || '',
+ urlBase: `https://${process.env.NEXT_PUBLIC_MATOMO_DOMAIN}` || '',
siteId: process.env.NEXT_PUBLIC_MATOMO_SITE_ID || '0',
},
postsPerPage: 10,
twitterId: '@ArmandPhilippot',
- url: process.env.NEXT_PUBLIC_FRONTEND_URL,
+ url: `${process.env.NEXT_PUBLIC_APP_PROTOCOL}://${process.env.NEXT_PUBLIC_APP_DOMAIN}`,
};
diff --git a/src/utils/helpers/rss.ts b/src/utils/helpers/rss.ts
index 8a1c801..e10c7f3 100644
--- a/src/utils/helpers/rss.ts
+++ b/src/utils/helpers/rss.ts
@@ -15,10 +15,10 @@ const getAllPosts = async (): Promise<ArticlePreview[]> => {
};
export const generateFeed = async () => {
- const websiteUrl = process.env.FRONTEND_URL ? process.env.FRONTEND_URL : '';
+ const websiteUrl = `${process.env.NEXT_PUBLIC_APP_PROTOCOL}://${process.env.NEXT_PUBLIC_APP_DOMAIN}`;
const author = {
name: settings.name,
- email: process.env.AUTHOR_EMAIL,
+ email: process.env.APP_AUTHOR_EMAIL,
link: websiteUrl,
};
const copyright = `${settings.name} CC BY SA ${settings.copyright.startYear} - ${settings.copyright.endYear}`;
@@ -27,7 +27,7 @@ export const generateFeed = async () => {
const feed = new Feed({
author,
copyright,
- description: process.env.FEED_DESCRIPTION,
+ description: process.env.APP_FEED_DESCRIPTION,
feedLinks: {
json: `${websiteUrl}/feed/json`,
atom: `${websiteUrl}/feed/atom`,