From 901f1e9492f70ff0c32e45dd5788f11d4aeb7da3 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 15 Feb 2018 14:02:46 +0000 Subject: [PATCH 1/2] Use CSP header to treat served files as belonging to a separate origin --- notebook/base/handlers.py | 4 ++++ notebook/files/handlers.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/notebook/base/handlers.py b/notebook/base/handlers.py index ddd82426d2..d1f5afc4a1 100755 --- a/notebook/base/handlers.py +++ b/notebook/base/handlers.py @@ -629,6 +629,10 @@ def set_headers(self): # disable browser caching, rely on 304 replies for savings if "v" not in self.request.arguments: self.add_header("Cache-Control", "no-cache") + + # In case we're serving HTML/SVG, confine any Javascript to a unique + # origin so it can't interact with the notebook server. + self.set_header('Content-Security-Policy', 'sandbox allow-scripts') def compute_etag(self): return None diff --git a/notebook/files/handlers.py b/notebook/files/handlers.py index b942149844..2cf63ff959 100644 --- a/notebook/files/handlers.py +++ b/notebook/files/handlers.py @@ -64,6 +64,10 @@ def get(self, path, include_body=True): else: self.set_header('Content-Type', 'text/plain; charset=UTF-8') + # In case we're serving HTML/SVG, confine any Javascript to a unique + # origin so it can't interact with the notebook server. + self.set_header('Content-Security-Policy', 'sandbox allow-scripts') + if include_body: if model['format'] == 'base64': b64_bytes = model['content'].encode('ascii') From 694ed72fb4a80ccae6b352811f5dd2277e6889b8 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 8 Mar 2018 16:59:12 +0000 Subject: [PATCH 2/2] Use content_security_policy property to add restriction when serving user files --- notebook/base/handlers.py | 11 +++++++---- notebook/files/handlers.py | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/notebook/base/handlers.py b/notebook/base/handlers.py index d1f5afc4a1..430ff0f0ed 100755 --- a/notebook/base/handlers.py +++ b/notebook/base/handlers.py @@ -601,6 +601,13 @@ def prepare(self): class AuthenticatedFileHandler(IPythonHandler, web.StaticFileHandler): """static files should only be accessible when logged in""" + @property + def content_security_policy(self): + # In case we're serving HTML/SVG, confine any Javascript to a unique + # origin so it can't interact with the notebook server. + return super(AuthenticatedFileHandler, self).content_security_policy + \ + "; sandbox allow-scripts" + @web.authenticated def get(self, path): if os.path.splitext(path)[1] == '.ipynb' or self.get_argument("download", False): @@ -629,10 +636,6 @@ def set_headers(self): # disable browser caching, rely on 304 replies for savings if "v" not in self.request.arguments: self.add_header("Cache-Control", "no-cache") - - # In case we're serving HTML/SVG, confine any Javascript to a unique - # origin so it can't interact with the notebook server. - self.set_header('Content-Security-Policy', 'sandbox allow-scripts') def compute_etag(self): return None diff --git a/notebook/files/handlers.py b/notebook/files/handlers.py index 2cf63ff959..7973fd6914 100644 --- a/notebook/files/handlers.py +++ b/notebook/files/handlers.py @@ -26,6 +26,13 @@ class FilesHandler(IPythonHandler): a subclass of StaticFileHandler. """ + @property + def content_security_policy(self): + # In case we're serving HTML/SVG, confine any Javascript to a unique + # origin so it can't interact with the notebook server. + return super(FilesHandler, self).content_security_policy + \ + "; sandbox allow-scripts" + @web.authenticated def head(self, path): self.get(path, include_body=False) @@ -64,10 +71,6 @@ def get(self, path, include_body=True): else: self.set_header('Content-Type', 'text/plain; charset=UTF-8') - # In case we're serving HTML/SVG, confine any Javascript to a unique - # origin so it can't interact with the notebook server. - self.set_header('Content-Security-Policy', 'sandbox allow-scripts') - if include_body: if model['format'] == 'base64': b64_bytes = model['content'].encode('ascii')