security.rst
146 lines
| 5.5 KiB
| text/x-rst
|
RstLexer
MinRK
|
r15981 | Security in IPython notebooks | ||
============================= | ||||
As IPython 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 IPython 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 IPython 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 | ||||
-------------------- | ||||
IPython 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 IPython profile's security directory. By default, this is:: | ||||
~/.ipython/profile_default/security/notebook_secret | ||||
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:: | ||||
ipython 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 IPython, 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, | ||||
MinRK
|
r15986 | you can use :download:`this PGP public key <ipython_security.asc>`. | ||
MinRK
|
r15981 | |||
Affected use cases | ||||
------------------ | ||||
Some use cases that work in IPython 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 ``ipython trust`` or the notebook menu | ||||
(annoying, but easy) | ||||
- share a notebook secret, and use an IPython 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. | ||||