mirror of
https://github.com/jupyter/notebook.git
synced 2024-12-21 04:10:17 +08:00
155 lines
5.7 KiB
ReStructuredText
155 lines
5.7 KiB
ReStructuredText
.. _notebook_security:
|
|
|
|
Security in Jupyter notebooks
|
|
=============================
|
|
|
|
As Jupyter notebooks become more popular for sharing and collaboration,
|
|
the potential for malicious people to attempt to exploit the notebook
|
|
for their nefarious purposes increases. IPython 2.0 introduces a
|
|
security model to prevent execution of untrusted code without explicit
|
|
user input.
|
|
|
|
The problem
|
|
-----------
|
|
|
|
The whole point of Jupyter is arbitrary code execution. We have no
|
|
desire to limit what can be done with a notebook, which would negatively
|
|
impact its utility.
|
|
|
|
Unlike other programs, an Jupyter notebook document includes output.
|
|
Unlike other documents, that output exists in a context that can execute
|
|
code (via Javascript).
|
|
|
|
The security problem we need to solve is that no code should execute
|
|
just because a user has **opened** a notebook that **they did not
|
|
write**. Like any other program, once a user decides to execute code in
|
|
a notebook, it is considered trusted, and should be allowed to do
|
|
anything.
|
|
|
|
Our security model
|
|
------------------
|
|
|
|
- Untrusted HTML is always sanitized
|
|
- Untrusted Javascript is never executed
|
|
- HTML and Javascript in Markdown cells are never trusted
|
|
- **Outputs** generated by the user are trusted
|
|
- Any other HTML or Javascript (in Markdown cells, output generated by
|
|
others) is never trusted
|
|
- The central question of trust is "Did the current user do this?"
|
|
|
|
The details of trust
|
|
--------------------
|
|
|
|
Jupyter notebooks store a signature in metadata, which is used to answer
|
|
the question "Did the current user do this?"
|
|
|
|
This signature is a digest of the notebooks contents plus a secret key,
|
|
known only to the user. The secret key is a user-only readable file in
|
|
the Jupyter profile's security directory. By default, this is::
|
|
|
|
~/.jupyter/profile_default/security/notebook_secret
|
|
|
|
.. note::
|
|
|
|
The notebook secret being stored in the profile means that
|
|
loading a notebook in another profile results in it being untrusted,
|
|
unless you copy or symlink the notebook secret to share it across profiles.
|
|
|
|
When a notebook is opened by a user, the server computes a signature
|
|
with the user's key, and compares it with the signature stored in the
|
|
notebook's metadata. If the signature matches, HTML and Javascript
|
|
output in the notebook will be trusted at load, otherwise it will be
|
|
untrusted.
|
|
|
|
Any output generated during an interactive session is trusted.
|
|
|
|
Updating trust
|
|
**************
|
|
|
|
A notebook's trust is updated when the notebook is saved. If there are
|
|
any untrusted outputs still in the notebook, the notebook will not be
|
|
trusted, and no signature will be stored. If all untrusted outputs have
|
|
been removed (either via ``Clear Output`` or re-execution), then the
|
|
notebook will become trusted.
|
|
|
|
While trust is updated per output, this is only for the duration of a
|
|
single session. A notebook file on disk is either trusted or not in its
|
|
entirety.
|
|
|
|
Explicit trust
|
|
**************
|
|
|
|
Sometimes re-executing a notebook to generate trusted output is not an
|
|
option, either because dependencies are unavailable, or it would take a
|
|
long time. Users can explicitly trust a notebook in two ways:
|
|
|
|
- At the command-line, with::
|
|
|
|
jupyter trust /path/to/notebook.ipynb
|
|
|
|
- After loading the untrusted notebook, with ``File / Trust Notebook``
|
|
|
|
These two methods simply load the notebook, compute a new signature with
|
|
the user's key, and then store the newly signed notebook.
|
|
|
|
Reporting security issues
|
|
-------------------------
|
|
|
|
If you find a security vulnerability in Jupyter, either a failure of the
|
|
code to properly implement the model described here, or a failure of the
|
|
model itself, please report it to security@ipython.org.
|
|
|
|
If you prefer to encrypt your security reports,
|
|
you can use :download:`this PGP public key <ipython_security.asc>`.
|
|
|
|
Affected use cases
|
|
------------------
|
|
|
|
Some use cases that work in Jupyter 1.0 will become less convenient in
|
|
2.0 as a result of the security changes. We do our best to minimize
|
|
these annoyance, but security is always at odds with convenience.
|
|
|
|
Javascript and CSS in Markdown cells
|
|
************************************
|
|
|
|
While never officially supported, it had become common practice to put
|
|
hidden Javascript or CSS styling in Markdown cells, so that they would
|
|
not be visible on the page. Since Markdown cells are now sanitized (by
|
|
`Google Caja <https://developers.google.com/caja>`__), all Javascript
|
|
(including click event handlers, etc.) and CSS will be stripped.
|
|
|
|
We plan to provide a mechanism for notebook themes, but in the meantime
|
|
styling the notebook can only be done via either ``custom.css`` or CSS
|
|
in HTML output. The latter only have an effect if the notebook is
|
|
trusted, because otherwise the output will be sanitized just like
|
|
Markdown.
|
|
|
|
Collaboration
|
|
*************
|
|
|
|
When collaborating on a notebook, people probably want to see the
|
|
outputs produced by their colleagues' most recent executions. Since each
|
|
collaborator's key will differ, this will result in each share starting
|
|
in an untrusted state. There are three basic approaches to this:
|
|
|
|
- re-run notebooks when you get them (not always viable)
|
|
- explicitly trust notebooks via ``jupyter trust`` or the notebook menu
|
|
(annoying, but easy)
|
|
- share a notebook secret, and use an Jupyter profile dedicated to the
|
|
collaboration while working on the project.
|
|
|
|
Multiple profiles or machines
|
|
*****************************
|
|
|
|
Since the notebook secret is stored in a profile directory by default,
|
|
opening a notebook with a different profile or on a different machine
|
|
will result in a different key, and thus be untrusted. The only current
|
|
way to address this is by sharing the notebook secret. This can be
|
|
facilitated by setting the configurable:
|
|
|
|
.. sourcecode:: python
|
|
|
|
c.NotebookApp.secret_file = "/path/to/notebook_secret"
|
|
|
|
in each profile, and only sharing the secret once per machine.
|