Skip to content

Commit

Permalink
build(docker): add security headers to NGINX
Browse files Browse the repository at this point in the history
Set the following headers:

- Content-Security-Policy
- Cross-Origin-Embedder-Policy
- Cross-Origin-Opener-Policy
- Cross-Origin-Resource-Policy
- Origin-Agent-Cluster
- Referrer-Policy
- X-Content-Type-Options
- X-Download-Options
- X-Frame-Options
- X-XSS-Protection

Specifically, the combination of Cross-Origin-Opener-Policy and
Referrer-Policy takes the role of setting `rel="noreferrer noopener"` on
external links, which was removed in a prior commit replacing the HTML
sanitizer used for Markdown rendering.

* Generate hashes of JS files during the build process
* Template hashes into CSP
* Include headers in NGINX configuration
  • Loading branch information
sybereal committed Apr 16, 2024
1 parent cc278f9 commit 3d762fc
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 1 deletion.
9 changes: 9 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ COPY --from=npm-install /app/node_modules /app/node_modules
COPY ./ /app/
RUN npm run ng build --no-progress --configuration=production

RUN ./docker/generate-csp-hash.mjs dist/edc-demo-client/*.js > script-src.txt

# Stage 3: Serve app with nginx
FROM docker.io/nginxinc/nginx-unprivileged:1.25-alpine3.18

Expand All @@ -28,6 +30,13 @@ COPY docker/default.conf.template etc/nginx/templates/default.conf.template
# so that the automatic envsubst templating is not disabled.
COPY docker/99-generate-app-config.sh /docker-entrypoint.d/99-generate-app-config.sh

# Mount the template from the build context and the hash list from the previous stage
# instead of copying them, as they are not needed in the final image.
RUN --mount=type=bind,from=build,source=/app/script-src.txt,target=/tmp/script-src.txt \
--mount=type=bind,source=/docker/headers.include.template,target=/tmp/headers.include.template \
env SCRIPT_SRC_EXTRA="$(cat /tmp/script-src.txt)" \
envsubst '$$SCRIPT_SRC_EXTRA' < /tmp/headers.include.template > /etc/nginx/headers.include

RUN ln -sf /tmp/app-config.json /usr/share/nginx/html/assets/config/app-config.json \
# Nginx is configured to reject symlinks that point to a file owned by a different user, for security reasons
&& chown --no-dereference nginx:root /usr/share/nginx/html/assets/config/app-config.json
Expand Down
6 changes: 5 additions & 1 deletion docker/default.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ server {
root /usr/share/nginx/html;

location / {
add_header Cache-Control "public, immutable, max-age=604800";
include headers.include;

index do-not-use-me.html;
try_files $uri @index;
}

location @index {
add_header Cache-Control no-cache;
add_header Cache-Control "no-cache";
include headers.include;
expires 0;
index index.html;
try_files /index.html =404;
Expand Down
38 changes: 38 additions & 0 deletions docker/headers.include.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Documentation of individual directives:
# - default-src: fallback if a more specific directive is not given; 'self' allows resources from the same origin
# - base-uri: restricts the URLs that can be used in a document's <base> element; 'self' allows resources from the same origin
# - script-src: controls <script> tags and inline event handlers
# - 'strict-dynamic' allows propagating trust from a script protected by a hash or nonce to further scripts it loads
# - ${SCRIPT_SRC_EXTRA} should contain the hashes of all scripts present in the initial HTML
# - style-src: controls <style> tags and inline styles
# - 'unsafe-inline' is required for Angular
# - img-src: controls <img> tags
# - https: allows images from any HTTPS source; required to correctly render embedded images in Markdown
# - frame-src: controls <frame> and <iframe> tags; 'none' disallows them
# - object-src: controls legacy <object> and <embed> tags
# - worker-src: controls web workers and service workers
# - form-action: controls <form> tags; disallow all native form submissions, as this is a single-page application
# - frame-ancestors: controls the ancestors of a document that can embed it in an iframe; 'none' disallows embedding
# - require-trusted-types-for and trusted-types: enforces explicit sanitization when assigning to innerHTML and the like
# - upgrade-insecure-requests: forces the browser to use HTTPS for all URL fetches, even if specified as plain HTTP
add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; script-src 'self' 'strict-dynamic' ${SCRIPT_SRC_EXTRA}; style-src 'self' 'unsafe-inline'; img-src 'self' https:; frame-src 'none'; object-src 'none'; worker-src 'none'; form-action 'none'; frame-ancestors 'none'; require-trusted-types-for 'script'; trusted-types angular angular#bundler; upgrade-insecure-requests;" always;
# Prevent loading of cross-origin resources unless explicitly permitted by the target origin
# Non-CORS requests are permitted, but have their credentials, e.g., cookies, stripped
# Less strict than require-corp, but allows loading images from servers that do not support CORP, which may happen with Markdown
add_header Cross-Origin-Embedder-Policy "credentialless" always;
# Ensure browsing context isolation from new windows/tabs on different origins created by this origin
add_header Cross-Origin-Opener-Policy "same-origin" always;
# Ensure other origins cannot access resources on this origin
add_header Cross-Origin-Resource-Policy "same-origin" always;
# Ensure browsing context isolation from different origins on the same site
add_header Origin-Agent-Cluster "?1" always;
# Disable Referer [sic] header entirely
add_header Referrer-Policy "no-referrer" always;
# Disable MIME type sniffing
add_header X-Content-Type-Options "nosniff" always;
# Prevent downloads being interpreted as HTML by legacy browsers
add_header X-Download-Options "noopen" always;
# Prevent embedding in iframes; legacy counterpart to CSP frame-ancestors directive
add_header X-Frame-Options "DENY" always;
# Disable XSS "protection" that only makes things worse
add_header X-XSS-Protection "0" always;

0 comments on commit 3d762fc

Please sign in to comment.