@Component
@WebFilter(urlPatterns = {"/*"})
public class ResponseHeaderWebFilter implements Filter {
@Override
public void doFilter(
ServletRequest request,
ServletResponse response, FilterChain chain
) throws IOException, ServletException {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:;");
httpServletResponse.setHeader("Strict-Transport-Security", "max-age=31536000 ; includeSubDomains ; preload");
httpServletResponse.setHeader("Permissions-Policy", "geolocation 'self'; payment 'none'");
httpServletResponse.setHeader("X-Content-Type-Options", "nosniff");
httpServletResponse.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
httpServletResponse.setHeader("X-Frame-Options", "DENY");
chain.doFilter(request, response);
}
}
|
Default Sources
private String getDefaultSources() {
String tiktok = "https://analytics.tiktok.com/";
String facebook = "https://www.facebook.com/ https://connect.facebook.net/";
String doubleClick = "https://stats.g.doubleclick.net/ https://11141660.fls.doubleclick.net/ https://googleads.g.doubleclick.net/";
String google = "https://www.google.com/ https://www.google.com.my/ https://analytics.google.com/ https://www.googletagmanager.com/ https://www.google-analytics.com/";
String[] sources = {DEFAULT_SRC, SELF, UNSAFE_INLINE, UNSAFE_EVAL, google, facebook, doubleClick, tiktok, BLOB_DATA};
String defaultSources = String.join(SOURCE_DELIMITER, sources);
return getFilteredSources(defaultSources, DEFAULT_SRC);
}
|
Content Security Policy
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ajax.googleapis.com https://ssl.google-analytics.com https://assets.zendesk.com https://connect.facebook.net; img-src 'self' https://ssl.google-analytics.com https://s-static.ak.facebook.com https://assets.zendesk.com; style-src 'self' 'unsafe-inline' https://assets.zendesk.com; font-src 'self' https://fonts.gstatic.com https://themes.googleusercontent.com; frame-src https://player.vimeo.com https://assets.zendesk.com https://www.facebook.com https://s-static.ak.facebook.com https://tautt.zendesk.com; object-src 'none'
Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/ https://www.google.com.my/ https://analytics.google.com/ https://www.googletagmanager.com/ https://www.google-analytics.com/ https://www.facebook.com/ https://connect.facebook.net/ https://stats.g.doubleclick.net/ https://11141660.fls.doubleclick.net/ https://googleads.g.doubleclick.net/ https://analytics.tiktok.com/ data: blob:
Content-Security-Policy: default-src *; style-src 'self' 'unsafe-inline'; font-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' stackexchange.com
Content-Security-Policy: default-src *; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://www.google.com;
content-security-policy: default-src 'self' * 'unsafe-inline' 'unsafe-eval' data: blob:
Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:
content-security-policy: default-src * 'unsafe-inline' 'unsafe-eval' data: blob:
Content-Security-Policy: default-src 'self' cdn.chorke.org
|
Content Security Policy » Nginx
server {
server_name academia.chorke.org;
# …more…
add_header X-Frame-Options "SAMEORIGIN";
add_header Referrer-Policy "same-origin";
add_header X-Content-Type-Options "nosniff";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
add_header Content-Security-Policy "frame-ancestors https://shahed.biz; default-src * 'unsafe-inline' 'unsafe-eval' data: blob:";
add_header Permissions-Policy "camera=('none'), microphone=('none'), geolocation=('none'), payment=('none')";
# …more…
location / {
return 301 https://academia.chorke.org$request_uri;
}
}
|
nginx -t
systemctl restart nginx
|
Content Security Policy » Nginx » Ingress
|
Nginx » Ingress
|
|
Service » academia
|
cat <<'YML'| \
kubectl apply -n academia -f -
---
apiVersion: v1
kind: ConfigMap
metadata:
name: academia-csp
namespace: academia
data:
default.conf: |
server {
listen 80;
server_name _;
# php -i|grep upload_max
client_max_body_size 2M;
keepalive_timeout 10;
# header » forward » client
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
# header » remove » security
proxy_hide_header X-Powered-By;
proxy_hide_header X-Frame-Options;
proxy_hide_header Referrer-Policy;
proxy_hide_header Permissions-Policy;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header Content-Security-Policy;
proxy_hide_header Strict-Transport-Security;
# header » append » security
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "same-origin" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:" always;
add_header Permissions-Policy "camera=('none'), microphone=('none'), geolocation=('none'), payment=('none')" always;
location / {
proxy_pass http://academia:80/;
proxy_redirect off;
}
}
---
apiVersion: v1
kind: Service
metadata:
name: academia-csp
namespace: academia
labels:
app.kubernetes.io/instance: academia-csp
app.kubernetes.io/managed-by: kubectl
app.kubernetes.io/name: academia-csp
app.kubernetes.io/version: 1.0.0
spec:
type: ClusterIP
ports:
- port: 80
name: http
protocol: TCP
targetPort: http
selector:
app.kubernetes.io/instance: academia-csp
app.kubernetes.io/name: academia-csp
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: academia-csp
namespace: academia
labels:
app.kubernetes.io/instance: academia-csp
app.kubernetes.io/managed-by: kubectl
app.kubernetes.io/name: academia-csp
app.kubernetes.io/version: 1.0.0
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/instance: academia-csp
app.kubernetes.io/name: academia-csp
template:
metadata:
labels:
app.kubernetes.io/instance: academia-csp
app.kubernetes.io/name: academia-csp
spec:
containers:
- name: academia-csp
image: nginx:1.27-alpine-slim
ports:
- name: http
protocol: TCP
containerPort: 80
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 128Mi
cpu: 100m
volumeMounts:
- mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
name: default-conf
volumes:
- name: default-conf
configMap:
name: academia-csp
items:
- key: default.conf
path: default.conf
YML
|
|
Ingress » nginx
|
cat << YML | \
kubectl apply -n academia -f -
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: academia
namespace: academia
labels:
app.kubernetes.io/name: academia
app.kubernetes.io/version: 1.0.0
app.kubernetes.io/instance: academia
app.kubernetes.io/managed-by: kubectl
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0"
spec:
ingressClassName: nginx
rules:
- host: academia.chorke.org
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: academia-csp
port:
number: 80
YML
|
|
Permissions Policy
Permissions-Policy: camera=(), microphone=(), geolocation=()
Permissions-Policy: camera=('none'), microphone=('none'), geolocation=('none'), payment=('none')
Permissions-Policy: geolocation=("https://advertiser.example.com" "https://analytics.example.com")
permissions-policy: accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), interest-cohort=()
|
References