From 47dd9c5432da760e3b780a50ae339ae385b16b7c Mon Sep 17 00:00:00 2001 From: Aurélien Bompard Date: Dec 11 2024 12:11:55 +0000 Subject: Add configuration for the `frame-ancestors` value of CSP Add a configuration option to set the domains allowed to display Ipsilon in a frame / iframe / embed using Content Security Policy. Also, `X-Frame-Options` is deprecated and replaced by Content Security Policy, remove it. Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2235532 Signed-off-by: Aurélien Bompard --- diff --git a/examples/ipsilon.conf b/examples/ipsilon.conf index b76ac45..10ecffe 100644 --- a/examples/ipsilon.conf +++ b/examples/ipsilon.conf @@ -6,6 +6,7 @@ db.conn.log = False base.mount = "/idp" base.dir = "/usr/share/ipsilon" +csp.frame-ancestors = "'none'" admin.config.db = "/var/lib/ipsilon/adminconfig.sqlite" user.prefs.db = "/var/lib/ipsilon/userprefs.sqlite" diff --git a/ipsilon/util/endpoint.py b/ipsilon/util/endpoint.py index 53a5e54..fbb4faf 100644 --- a/ipsilon/util/endpoint.py +++ b/ipsilon/util/endpoint.py @@ -9,13 +9,12 @@ from functools import wraps def allow_iframe(func): """ - Remove the X-Frame-Options and CSP frame-ancestors deny headers. + Remove the CSP frame-ancestors deny headers. """ @wraps(func) def wrapper(*args, **kwargs): result = func(*args, **kwargs) for (header, value) in [ - ('X-Frame-Options', 'deny'), ('Content-Security-Policy', 'frame-ancestors \'none\'')]: if cherrypy.response.headers.get(header, None) == value: cherrypy.response.headers.pop(header, None) @@ -29,11 +28,11 @@ class Endpoint(Log): self._site = site self.basepath = cherrypy.config.get('base.mount', "") self.user = None + _frame_ancestors = cherrypy.config.get('csp.frame-ancestors', "'none'") self.default_headers = { 'Cache-Control': 'no-cache, no-store, must-revalidate, private', 'Pragma': 'no-cache', - 'Content-Security-Policy': 'frame-ancestors \'none\'', - 'X-Frame-Options': 'deny', + 'Content-Security-Policy': f'frame-ancestors {_frame_ancestors}', } self.auth_protect = False diff --git a/man/ipsilon.conf.5.in b/man/ipsilon.conf.5.in index 9d08be6..7a86504 100644 --- a/man/ipsilon.conf.5.in +++ b/man/ipsilon.conf.5.in @@ -50,6 +50,9 @@ The base mount mount for UI pages. This should match the name of the IdP. .B base.dir The Ipsilon UI base directory, e.g. \fI/usr/share/ipsilon\fR. .TP +.B csp.frame-ancestors +The value of frame-ancestors in the Content Security Policy. This defaults to 'none', which allows does not allow (i)frames. +.TP .B admin.config.db Database URL for storing Ipsilon administrative settings. .TP diff --git a/tests/test1.py b/tests/test1.py index 4b3a9cb..72cd3dc 100755 --- a/tests/test1.py +++ b/tests/test1.py @@ -206,3 +206,9 @@ if __name__ == '__main__': page.expected_value('//div[@id="row_provider_http://keyless-sp"]/' '@title', 'WARNING: SP does not have signing keys!') + + with TC.case('Check CSP'): + page = sess.access("GET", 'https://127.0.0.10:45080/idp1/') + csp = page.headers.get("content-security-policy") + if csp != "frame-ancestors 'none'": + raise ValueError(f"Content Security Policy is set to {csp}")