Security model

This section attempts to document the Zulip security model. It likely does not cover every issue; if there are details you're curious about, please feel free to ask questions in #production help on the Zulip community server (or if you think you've found a security bug, please report it to security@zulip.com so we can do a responsible security announcement).

Secure your Zulip server like your email server

In particular, anyone with root access to a Zulip application server or Zulip database server, or with access to the zulip user on a Zulip application server, has complete control over the Zulip installation and all of its data (so they can read messages, modify history, etc.). It would be difficult or impossible to avoid this, because the server needs access to the data to support features expected of a group chat system like the ability to search the entire message history, and thus someone with control over the server has access to that data as well.

Encryption and authentication

Passwords

Zulip stores user passwords using the standard Argon2 and PBKDF2 algorithms. Argon2 is used for all new and changed passwords as of Zulip Server 1.6.0, but legacy PBKDF2 passwords that were last changed before the 1.6.0 upgrade are still supported.

When the user is choosing a password, Zulip checks the password's strength using the popular zxcvbn library. Weak passwords are rejected, and strong passwords encouraged. The minimum password strength allowed is controlled by two settings in /etc/zulip/settings.py:

By default, PASSWORD_MIN_GUESSES is 10000. This provides significant protection against online attacks, while limiting the burden imposed on users choosing a password. See password strength for an extended discussion on how we chose this value.

Estimating the guessability of a password is a complex problem and impossible to efficiently do perfectly. For background or when considering an alternate value for this setting, the article "Passwords and the Evolution of Imperfect Authentication" is recommended. The 2016 zxcvbn paper adds useful information about the performance of zxcvbn, and a large 2012 study of Yahoo users is informative about the strength of the passwords users choose.

Messages and history

Users and bots

Being an organization administrator does not generally provide the ability to read other users' private messages or messages sent to private streams to which the administrator is not subscribed. There are two exceptions:

User-uploaded content and user-generated requests

We support two ways of hosting them: the basic LOCAL_UPLOADS_DIR file storage backend, where they are stored in a directory on the Zulip server's filesystem, and the S3 backend, where the files are stored in Amazon S3. It would not be difficult to add additional supported backends should there be a need; see zerver/lib/upload.py for the full interface.

For both backends, the URLs used to access uploaded files are long, random strings, providing one layer of security against unauthorized users accessing files uploaded in Zulip (an authorized user would need to share the URL with an unauthorized user in order for the file to be accessed by the unauthorized user. Of course, any such authorized user could have just downloaded and sent the file instead of the URL, so this is arguably pretty good protection.) However, to help protect against accidental sharing of URLs to restricted files (e.g. by forwarding a missed-message email or leaks involving the Referer header), we provide additional layers of protection in both backends as well.

In the Zulip S3 backend, the random URLs to access files that are presented to users don't actually host the content. Instead, the S3 backend verifies that the user has a valid Zulip session in the relevant organization (and that has access to a Zulip message linking to the file), and if so, then redirects the browser to a temporary S3 URL for the file that expires a short time later. In this way, possessing a URL to a secret file in Zulip does not provide unauthorized users with access to that file.

We have a similar protection for the LOCAL_UPLOADS_DIR backend. Every access to an uploaded file has access control verified (confirming that the browser is logged into a Zulip account that has received the uploaded file in question).

Final notes and security response

If you find some aspect of Zulip that seems inconsistent with this security model, please report it to security@zulip.com so that we can investigate and coordinate an appropriate security release if needed.

Zulip security announcements will be sent to zulip-announce@googlegroups.com, so you should subscribe if you are running Zulip in production.