Show More
@@ -0,0 +1,4 b'' | |||
|
1 | # URI for the CSP Report. Included here to prevent a cyclic dependency. | |
|
2 | # csp_report_uri is needed both by the BaseHandler (for setting the report-uri) | |
|
3 | # and by the CSPReportHandler (which depends on the BaseHandler). | |
|
4 | csp_report_uri = r"/api/security/csp-report" |
@@ -0,0 +1,23 b'' | |||
|
1 | """Tornado handlers for security logging.""" | |
|
2 | ||
|
3 | # Copyright (c) IPython Development Team. | |
|
4 | # Distributed under the terms of the Modified BSD License. | |
|
5 | ||
|
6 | from tornado import gen, web | |
|
7 | ||
|
8 | from ...base.handlers import IPythonHandler, json_errors | |
|
9 | from . import csp_report_uri | |
|
10 | ||
|
11 | class CSPReportHandler(IPythonHandler): | |
|
12 | '''Accepts a content security policy violation report''' | |
|
13 | @web.authenticated | |
|
14 | @json_errors | |
|
15 | def post(self): | |
|
16 | '''Log a content security policy violation report''' | |
|
17 | csp_report = self.get_json_body() | |
|
18 | self.log.warn("Content security violation: %s", | |
|
19 | self.request.body.decode('utf8', 'replace')) | |
|
20 | ||
|
21 | default_handlers = [ | |
|
22 | (csp_report_uri, CSPReportHandler) | |
|
23 | ] |
@@ -32,6 +32,8 b' from IPython.utils.path import filefind' | |||
|
32 | 32 | from IPython.utils.py3compat import string_types |
|
33 | 33 | from IPython.html.utils import is_hidden, url_path_join, url_escape |
|
34 | 34 | |
|
35 | from IPython.html.services.security import csp_report_uri | |
|
36 | ||
|
35 | 37 | #----------------------------------------------------------------------------- |
|
36 | 38 | # Top-level handlers |
|
37 | 39 | #----------------------------------------------------------------------------- |
@@ -45,17 +47,22 b' class AuthenticatedHandler(web.RequestHandler):' | |||
|
45 | 47 | def set_default_headers(self): |
|
46 | 48 | headers = self.settings.get('headers', {}) |
|
47 | 49 | |
|
48 |
if " |
|
|
49 | headers["X-Frame-Options"] = "SAMEORIGIN" | |
|
50 | if "Content-Security-Policy" not in headers: | |
|
51 | headers["Content-Security-Policy"] = ( | |
|
52 | "frame-ancestors 'self'; " | |
|
53 | # Make sure the report-uri is relative to the base_url | |
|
54 | "report-uri " + url_path_join(self.base_url, csp_report_uri) + ";" | |
|
55 | ) | |
|
50 | 56 | |
|
57 | # Allow for overriding headers | |
|
51 | 58 | for header_name,value in headers.items() : |
|
52 | 59 | try: |
|
53 | 60 | self.set_header(header_name, value) |
|
54 | except Exception: | |
|
61 | except Exception as e: | |
|
55 | 62 | # tornado raise Exception (not a subclass) |
|
56 | 63 | # if method is unsupported (websocket and Access-Control-Allow-Origin |
|
57 | 64 | # for example, so just ignore) |
|
58 |
|
|
|
65 | self.log.debug(e) | |
|
59 | 66 | |
|
60 | 67 | def clear_login_cookie(self): |
|
61 | 68 | self.clear_cookie(self.cookie_name) |
@@ -225,7 +225,7 b' class NotebookWebApplication(web.Application):' | |||
|
225 | 225 | handlers.extend(load_handlers('services.sessions.handlers')) |
|
226 | 226 | handlers.extend(load_handlers('services.nbconvert.handlers')) |
|
227 | 227 | handlers.extend(load_handlers('services.kernelspecs.handlers')) |
|
228 | ||
|
228 | handlers.extend(load_handlers('services.security.handlers')) | |
|
229 | 229 | handlers.append( |
|
230 | 230 | (r"/nbextensions/(.*)", FileFindHandler, { |
|
231 | 231 | 'path': settings['nbextensions_path'], |
@@ -65,7 +65,10 b' class KernelAPITest(NotebookTestBase):' | |||
|
65 | 65 | self.assertEqual(r.status_code, 201) |
|
66 | 66 | self.assertIsInstance(kern1, dict) |
|
67 | 67 | |
|
68 |
self.assertEqual(r.headers[' |
|
|
68 | self.assertEqual(r.headers['Content-Security-Policy'], ( | |
|
69 | "frame-ancestors 'self'; " | |
|
70 | "report-uri /api/security/csp-report;" | |
|
71 | )) | |
|
69 | 72 | |
|
70 | 73 | def test_main_kernel_handler(self): |
|
71 | 74 | # POST request |
@@ -75,7 +78,10 b' class KernelAPITest(NotebookTestBase):' | |||
|
75 | 78 | self.assertEqual(r.status_code, 201) |
|
76 | 79 | self.assertIsInstance(kern1, dict) |
|
77 | 80 | |
|
78 |
self.assertEqual(r.headers[' |
|
|
81 | self.assertEqual(r.headers['Content-Security-Policy'], ( | |
|
82 | "frame-ancestors 'self'; " | |
|
83 | "report-uri /api/security/csp-report;" | |
|
84 | )) | |
|
79 | 85 | |
|
80 | 86 | # GET request |
|
81 | 87 | r = self.kern_api.list() |
@@ -180,16 +180,42 b' Backwards incompatible changes' | |||
|
180 | 180 | |
|
181 | 181 | .. DO NOT EDIT THIS LINE BEFORE RELEASE. INCOMPAT INSERTION POINT. |
|
182 | 182 | |
|
183 | IFrame embedding | |
|
184 | ```````````````` | |
|
183 | Content Security Policy | |
|
184 | ``````````````````````` | |
|
185 | 185 | |
|
186 | The IPython Notebook and its APIs by default will only be allowed to be | |
|
187 | embedded in an iframe on the same origin. | |
|
186 | The Content Security Policy is a web standard for adding a layer of security to | |
|
187 | detect and mitigate certain classes of attacks, including Cross Site Scripting | |
|
188 | (XSS) and data injection attacks. This was introduced into the notebook to | |
|
189 | ensure that the IPython Notebook and its APIs (by default) can only be embedded | |
|
190 | in an iframe on the same origin. | |
|
188 | 191 | |
|
189 | To override this, set ``headers[X-Frame-Options]`` to one of | |
|
192 | Override ``headers['Content-Security-Policy']`` within your notebook | |
|
193 | configuration to extend for alternate domains and security settings.:: | |
|
190 | 194 |
|
|
191 | * DENY | |
|
192 | * SAMEORIGIN | |
|
193 | * ALLOW-FROM uri | |
|
195 | c.NotebookApp.tornado_settings = { | |
|
196 | 'headers': { | |
|
197 | 'Content-Security-Policy': "frame-ancestors 'self'" | |
|
198 | } | |
|
199 | } | |
|
194 | 200 |
|
|
195 | See `Mozilla's guide to X-Frame-Options <https://developer.mozilla.org/en-US/docs/Web/HTTP/X-Frame-Options>`_ for more examples. | |
|
201 | Example policies:: | |
|
202 | ||
|
203 | Content-Security-Policy: default-src 'self' https://*.jupyter.org | |
|
204 | ||
|
205 | Matches embeddings on any subdomain of jupyter.org, so long as they are served | |
|
206 | over SSL. | |
|
207 | ||
|
208 | There is a `report-uri <https://developer.mozilla.org/en-US/docs/Web/Security/CSP/CSP_policy_directives#report-uri>`_ endpoint available for logging CSP violations, located at | |
|
209 | ``/api/security/csp-report``. To use it, set ``report-uri`` as part of the CSP:: | |
|
210 | ||
|
211 | c.NotebookApp.tornado_settings = { | |
|
212 | 'headers': { | |
|
213 | 'Content-Security-Policy': "frame-ancestors 'self'; report-uri /api/security/csp-report" | |
|
214 | } | |
|
215 | } | |
|
216 | ||
|
217 | It simply provides the CSP report as a warning in IPython's logs. The default | |
|
218 | CSP sets this report-uri relative to the ``base_url`` (not shown above). | |
|
219 | ||
|
220 | For a more thorough and accurate guide on Content Security Policies, check out | |
|
221 | `MDN's Using Content Security Policy <https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Using_Content_Security_Policy>`_ for more examples. |
General Comments 0
You need to be logged in to leave comments.
Login now