openstack-manuals/doc/security-guide/ch025_web-dashboard.xml
Shilla Saebi 68cb85d89e cleanup security-guide ch025_web-dashboard
made change to Horizon dashboard description in beginning of xml file
removing extra white space

Change-Id: I8a776010c06e0af99b2317475823355a3c6a4f18
2014-01-28 14:14:00 -05:00

259 lines
14 KiB
XML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://docbook.org/ns/docbook"
xmlns:db="http://docbook.org/ns/docbook" version="5.0"
xml:id="ch025_web-dashboard">
<?dbhtml stop-chunking?>
<title>Dashboard</title>
<para>Horizon is the OpenStack dashboard that provides users a self-service
portal to provision their own resources within the limits set by
administrators. These include provisioning users, defining instance flavors,
uploading VM images, managing networks, setting up security groups, starting
instances, and accessing the instances via a console.</para>
<para>The dashboard is based on the Django web framework, therefore
secure deployment practices for Django apply directly to Horizon.
This guide provides a popular set of Django security
recommendations, further information can be found by reading the
<link
xlink:href="https://docs.djangoproject.com/en/1.5/#security"
>Django deployment and security documentation</link>.</para>
<para>The dashboard ships with reasonable default security settings,
and has good <link
xlink:href="http://docs.openstack.org/developer/horizon/topics/deployment.html"
>deployment and configuration documentation</link>.</para>
<section xml:id="ch025_web-dashboard-idp237648">
<title>Basic Web Server Configuration</title>
<para>The dashboard should be deployed as a Web Services Gateway
Interface (WSGI) application behind an HTTPS proxy such as
Apache or nginx. If Apache is not already in use, we recommend
nginx since it is lighter weight and easier to configure
correctly.</para>
<para>When using nginx, we recommend <link
xlink:href="http://docs.gunicorn.org/en/latest/deploy.html"
>gunicorn</link> as the wsgi host with an appropriate number
of synchronous workers. We strongly advise against deployments
using fastcgi, scgi, or uWSGI. We strongly advise against the
use of synthetic performance benchmarks when choosing a wsgi
server.</para>
<para>When using Apache, we recommend <link
xlink:href="https://docs.djangoproject.com/en/1.5/howto/deployment/wsgi/modwsgi/"
>mod_wsgi</link> to host dashboard.</para>
</section>
<section xml:id="ch025_web-dashboard-idp240704">
<title>HTTPS</title>
<para>The dashboard should be deployed behind a secure HTTPS
server using a valid, trusted certificate from a recognized
certificate authority (CA). Private organization-issued
certificates are only appropriate when the root of trust is
pre-installed in all user browsers.</para>
<para>HTTP requests to the dashboard domain should be configured
to redirect to the fully qualified HTTPS URL.</para>
</section>
<section xml:id="ch025_web-dashboard-idp242624">
<title>HTTP Strict Transport Security (HSTS)</title>
<para>It is highly recommended to use HTTP Strict Transport
Security (HSTS).</para>
<para>NOTE: If you are using an HTTPS proxy in front of your web
server, rather than using an HTTP server with HTTPS
functionality, follow the <link
xlink:href="https://docs.djangoproject.com/en/1.5/ref/settings/#secure-proxy-ssl-header"
>Django documentation on modifying the SECURE_PROXY_SSL_HEADER
variable</link>.</para>
<para>See the chapter on PKI/SSL Everywhere for more specific
recommendations and server configurations for HTTPS
configurations, including the configuration of HSTS.</para>
</section>
<section xml:id="ch025_web-dashboard-idp245456">
<title>Front end Caching</title>
<para>Since dashboard is rendering dynamic content passed directly
from OpenStack API requests, we do not recommend front end
caching layers such as varnish. In Django, static media is
directly served from Apache or nginx and already benefits from
web host caching.</para>
</section>
<section xml:id="ch025_web-dashboard-idp246880">
<title>Domain Names</title>
<para>Many organizations typically deploy web applications at
subdomains of an overarching organization domain. It is natural
for users to expect a domain of the form
<uri>openstack.example.org</uri>. In this context, there are
often many other applications deployed in the same second-level
namespace, often serving user-controlled content. This name
structure is convenient and simplifies name server
maintenance.</para>
<para>We strongly recommend deploying horizon to a
<emphasis>second-level domain</emphasis>, such as
<uri>https://example.com</uri>, and advise against deploying
horizon on a <emphasis>shared subdomain</emphasis> of any level,
for example <uri>https://openstack.example.org</uri> or
<uri>https://horizon.openstack.example.org</uri>. We also
advise against deploying to bare internal domains like
<uri>https://horizon/</uri>.</para>
<para>This recommendation is based on the limitations browser
same-origin-policy. The recommendations in this guide cannot
effectively protect users against known attacks if dashboard is
deployed on a domain which also hosts user-generated content,
such as scripts, images, or uploads of any kind, even if the
user-generated content is on a different subdomain. This
approach is used by most major web presences, such as
googleusercontent.com, fbcdn.com, github.io, and twimg.com, to
ensure that user generated content stays separate from cookies
and security tokens.</para>
<para>Additionally, if you decline to follow this recommendation
above about second-level domains, it is vital that you avoid the
cookie backed session store and employ HTTP Strict Transport
Security (HSTS). When deployed on a subdomain, dashboard's
security is only as strong as the weakest application deployed
on the same second-level domain.</para>
</section>
<section xml:id="ch025_web-dashboard-idp251760">
<title>Static Media</title>
<para>Dashboard's static media should be deployed to a subdomain
of the dashboard domain and served by the web server. The use of
an external content delivery network (CDN) is also acceptable.
This subdomain should not set cookies or serve user-provided
content. The media should also be served with HTTPS.</para>
<para>Django media settings are documented at <link
xlink:href="https://docs.djangoproject.com/en/1.5/ref/settings/#static-root"
>https://docs.djangoproject.com/en/1.5/ref/settings/#static-root</link>.</para>
<para>Dashboard's default configuration uses <link
xlink:href="http://django-compressor.readthedocs.org/"
>django_compressor</link> to compress and minify css and
JavaScript content before serving it. This process should be
statically done before deploying dashboard, rather than using
the default in-request dynamic compression and copying the
resulting files along with deployed code or to the CDN server.
Compression should be done in a non-production build
environment. If this is not practical, we recommend disabling
resource compression entirely. Online compression dependencies
(less, nodejs) should not be installed on production
machines.</para>
</section>
<section xml:id="ch025_web-dashboard-idp255696">
<title>Secret Key</title>
<para>Dashboard depends on a shared SECRET_KEY setting for some
security functions. It should be a randomly generated string at
least 64 characters long. It must be shared across all active
Horizon instances. Compromise of this key may allow a remote
attacker to execute arbitrary code. Rotating this key
invalidates existing user sessions and caching. Do not commit
this key to public repositories.</para>
</section>
<section xml:id="ch025_web-dashboard-idp257248">
<title>Session Backend</title>
<para>Horizon's default session backend
(<emphasis>django.contrib.sessions.backends.signed_cookies</emphasis>)
stores user data in <emphasis>signed</emphasis> but
<emphasis>unencrypted </emphasis>cookies stored in the
browser. This approach allows the most simple session backend
scaling since each Horizon instance is stateless, but it comes
at the cost of <emphasis>storing sensitive access tokens in the
client browser</emphasis> and transmitting them with every
request. This backend ensures that session data has not been
tampered with, but the data itself is not encrypted other than
the encryption provided by HTTPS.</para>
<para>If your architecture allows it, we recommend using
<emphasis>django.contrib.sessions.backends.cache</emphasis> as
your session backend with memcache as the cache. Memcache must
not be exposed publicly, and should communicate over a secured
private channel. If you choose to use the signed cookies
backend, refer to the Django documentation understand the
security trade-offs.</para>
<para>For further details, consult the <link
xlink:href="https://docs.djangoproject.com/en/1.5/topics/http/sessions/#configuring-the-session-engine"
>Django session backend documentation</link>.</para>
</section>
<section xml:id="ch025_web-dashboard-idp262288">
<title>Allowed Hosts</title>
<para>Configure the ALLOWED_HOSTS setting with the domain or
domains where Horizon is available. Failure to configure this
setting (especially if not following the recommendation above
regarding second level domains) opens Horizon to a number of
serious attacks. Wild card domains should be avoided.</para>
<para>For further details, see the <link
xlink:href="https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts"
>Django documentation on settings</link>.</para>
</section>
<section xml:id="ch025_web-dashboard-idp264272">
<title>Cookies</title>
<para>Session Cookies should be set to HTTPONLY:</para>
<screen> 
SESSION_COOKIE_HTTPONLY = True</screen>
<para>Never configure CSRF or session cookies to have a wild card
domain with a leading dot. Horizon's session and CSRF cookie
should be secured when deployed with HTTPS:</para>
<screen> 
Code CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True</screen>
</section>
<section xml:id="ch025_web-dashboard-idp266976">
<title>Password Auto Complete</title>
<para>We recommend that implementers do not change the default
password auto complete behavior. Users choose stronger passwords
in environments that allow them to use the secure browser
password manager. Organizations which forbid the browser
password manager should enforce this policy at the desktop
level.</para>
</section>
<section xml:id="ch025_web-dashboard-idp268448">
<title>Cross Site Request Forgery (CSRF)</title>
<para>Django has a dedicated middleware for <link
xlink:href="https://docs.djangoproject.com/en/1.5/ref/contrib/csrf/#how-it-works"
>cross-site request forgery</link> (CSRF).</para>
<para>Dashboard is designed to discourage developers from
introducing cross-site scripting vulnerabilities with custom
dashboards. However, it is important to audit custom dashboards,
especially ones that are javascript-heavy for inappropriate use
of the @csrf_exempt decorator. Dashboards which do not follow
these recommended security settings should be carefully
evaluated before restrictions are relaxed.</para>
</section>
<section xml:id="ch025_web-dashboard-idp270608">
<title>Cross Site Scripting (XSS)</title>
<para>Unlike many similar systems, OpenStack dashboard allows the
entire Unicode character set in most fields. This means
developers have less latitude to make escaping mistakes that
open attack vectors for cross-site scripting (XSS).</para>
<para>Dashboard provides tools for developers to avoid creating
XSS vulnerabilities, but they only work if developers use them
correctly. Audit any custom dashboards, paying particular
attention to use of the mark_safe function, use of is_safe with
custom template tags, the safe template tag, anywhere auto escape
is turned off, and any JavaScript which might evaluate
improperly escaped data.</para>
</section>
<section xml:id="ch025_web-dashboard-idp272832">
<title>Cross Origin Resource Sharing (CORS)</title>
<para>Configure your web server to send a restrictive CORS header
with each response, allowing only the Horizon domain and
protocol:</para>
<screen>
Access-Control-Allow-Origin: https://example.com/</screen>
<para>Never allow the wild card origin.</para>
</section>
<section xml:id="ch025_web-dashboard-idp275056">
<title>Horizon Image Upload</title>
<para>We recommend that implementers <link
xlink:href="http://docs.openstack.org/developer/horizon/topics/deployment.html#file-uploads"
>disable HORIZON_IMAGES_ALLOW_UPLOAD</link> unless they have
implemented a plan to prevent resource exhaustion and denial of
service.</para>
</section>
<section xml:id="ch025_web-dashboard-idp276864">
<title>Upgrading</title>
<para>Django security releases are generally well tested and
aggressively backwards compatible. In almost all cases, new
major releases of Django are also fully backwards compatible
with previous releases. Dashboard implementers are strongly
encouraged to run the latest stable release of Django with
up-to-date security releases.</para>
</section>
<section xml:id="ch025_web-dashboard-idp278672">
<title>Debug</title>
<para>Make sure DEBUG is set to False in production. In Django,
DEBUG displays stack traces and sensitive web server state
information on any exception.</para>
</section>
</chapter>