🛡️ Security
Everyone who can log in to the admin can change your content. This page describes what Content Studio protects out of the box and what your deployment has to take care of.
What Content Studio does
- Staff only. Like the classic Django admin, only active staff users can log in, and every request checks this again. Deactivating a user or revoking their staff status ends their access immediately. What an editor may see and do is decided by the permission hooks.
- Short sessions. Sessions renew themselves while an editor works and
end after a week without use, and 30 days after logging in at the latest
(
MAX_SESSION_LIFETIME). Changing or resetting a password logs the user out everywhere (authentication). - Logging out ends the session on the server. A copy of the session cookie stops working at logout, and each renewal invalidates the previous cookie. A cookie presented again after its renewal was copied, so Content Studio ends that session.
- Limited login and password reset attempts, per IP address
(
LOGIN_THROTTLE_RATE,PASSWORD_RESET_THROTTLE_RATE). The address is the one the connection comes from; anX-Forwarded-Forheader sent by the client is ignored unless you configure your proxies (see the checklist below). Five wrong reset codes lock an email address out, and new passwords must passAUTH_PASSWORD_VALIDATORS. - Sanitized rich text.
HTMLFieldcleans its value on save with an allowlist sanitizer (HTML_SANITIZER). - Checked uploads. The media library accepts a list of file types up to
a maximum size, and rejects markup such as HTML and SVG
(
MEDIA_LIBRARY_ALLOWED_EXTENSIONS). - Nothing public but the login pages. Before logging in, the admin only receives what it needs to show the login form.
Deployment checklist
- Serve the admin over HTTPS. The session cookie is marked
SecurewheneverDEBUGis off (REFRESH_COOKIE_SECURE). Behind a proxy that terminates TLS, also set Django'sSECURE_PROXY_SSL_HEADER. - Configure a shared cache, such as Redis, Memcached or the database
cache. The login and password reset limits are counted in the cache; with
the default per-process cache, every worker counts separately.
Ended sessions are recorded in the cache
(
TOKEN_CACHE); with a per-process cache, a session logged out in one worker continues in another. Presence is kept in the cache too. - Pass on the client's IP address. The limits count per IP address.
Without further configuration, Content Studio uses the address of the
connection and ignores
X-Forwarded-For, which any client can set. Behind a proxy, every request then comes from the proxy's address and all clients share one limit. SetNUM_PROXIESin the Django REST Framework settings to the number of proxies in front of your application, and make sure those proxies add the client's address toX-Forwarded-For. - Serve uploads from a separate domain when you can, such as a CDN or an object storage bucket. Even with the upload checks, a file served from your own domain runs with your site's privileges if a browser renders it.
- Configure a task backend that runs Django tasks in a worker. Password reset emails are then sent outside the request, so its duration does not reveal whether an address has an account (password reset).
- Keep
DEBUGoff in production, and keep Django, Content Studio and your other dependencies up to date. - Use Django's security middleware and settings:
SecurityMiddlewarewith HSTS,XFrameOptionsMiddleware, andSESSION_COOKIE_SECUREandCSRF_COOKIE_SECUREfor the rest of your site.manage.py check --deploylists what is missing. - Keep
SECRET_KEYstrong and secret, or set a separateTOKEN_SIGNING_KEY: it signs the sessions.
Reporting a vulnerability
Report security problems privately, through GitHub's private vulnerability reporting or by email; see SECURITY.md.