Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Sun, 19 Jun 2016 03:12:41 -0400
From: Scott Arciszewski <scott@...agonie.com>
To: passwords@...ts.openwall.com
Subject: Am I Overlooking any Practical Attacks?

Hi,

I'm building a free software project that, I hope, will one day be the
secure alternative to CMS platforms like WordPress, Drupal, Joomla, and so
many others. One of the more interesting problems I tried to solve was
secure code delivery for our plugin architecture. (If you've read about the
triangle of secure code delivery, you've probably already got an idea of
what that entails.)

More importantly, I'm trying to eliminate the practical exploits against
user authentication systems. I'd like to think I've covered most of them,
but if anyone has any avenues for exploitation that I hadn't already
addressed (except for "implant malware on the user's computer and log
keystrokes" types of attacks, obviously), I would love to hear about them.

Current security features that will be implemented as of v0.3.0 (releasing
soon):

* Weak passwords are rejected. Weak means a Zxcvbn score < 3 (this
parameter can be configured). The rejection takes place server-side, but we
also use zxcvbn.js to give users immediate feedback. Consequently, one of
the 10,000 most common passwords will be accepted. The password feedback
messages also strongly encourage the use of password managers.

* In case your password gets leaked, two-factor authentication (HOTP/TOTP
for things like Google Authenticator; emphatically NOT sending a SMS) is
built-in. I'm going to expand this functionality to support hardware
devices very soon.

* Database dumps: We use Argon2i for password hashing (provided by
libsodium). Hashes are then encrypted using Halite's symmetric encryption
feature. The idea here is if you're using RDS (or otherwise have the
database on separate bare metal than the webserver), finding a SQLi doesn't
even give an attacker the hashes to begin cracking. (Even in absence of
hardware separation, they're still Argon2i hashes!)

Quick clarification: Halite is a libsodium wrapper. The symmetric
encryption feature uses HKDF-BLAKE2b + Xsalsa20-then-keyedBLAKE2b with a
192-bit nonce, 256-bit key-splitting salt (to derive an encryption key and
authentication key) and a 4-byte versioned header. This sounds like a lot,
but developers' experience with this feature looks like this:

    $ciphertext = Symmetric::encrypt($someString, $encryptionKeyObject);
    try {
        $plaintext = Symemtric::decrypt($ciphertext, $encryptionKeyObject);
    } catch (InvalidMessage $ex) {
        // Chosen-ciphertext attack?
        // Or maybe the wrong encryption key key?
        // Doesn't matter really. Fail appropriately.
    }

* Long-term cookie-based authentication: We use a two-part token. The first
part is a unique random identifier used in the database lookups (which are
assumed to leak timing information like most search operations). The second
is stored wholesale in the cookie (but a hash is stored in the database).
The user's cookie is re-hashed and compared with the stored hash (in
constant time) to refresh a user's session automatically. If you reset your
password, all long-term-auth tokens are invalidated server-side.

* Account recovery: Most importantly, it's optional. Users can uncheck a
box to prevent this from being used as a backdoor. Alternatively, they can
leave it enabled and provide a PGP public key. Or they can throw caution to
the wind and trust STARTTLS to not get stripped. The actual password
recovery email contains a two-part random token (modeled after the
long-term authentication tokens) that expires in (by default) an hour.
There also exists a configuration option that invalidates all of the user's
other browsing sessions the second they change their password.

* Session attacks: If the current connection is over TLS, we send HSTS
headers and all cookies are set to secure=1 (httpOnly=1 is enforced either
way). We explicitly enforce the session ID to use the operating system's
CSPRNG. Session IDs are rotated upon privilege change.

* User accounts are totally separate from Author profiles -- blog posts and
comments are attributed to Authors, not users. Many users can share access
to a specific Author, and each user can have access to many authors.

* Usernames aren't even used in the course of interacting with other users
(e.g. to invite another user to your Author profile). Instead you have a
Public ID (a randomly generated Base64UrlSafe string) which is also used in
your uploaded files "directory" (the filesystem is virtual; I don't trust
Apache nor most user's configuration knowledge to not accidentally make an
uploaded file executable) and a "display name", which you can change at any
time. Your username is strictly used for authentication.

* Login and account recovery attempts have progressive throttling -- based
on IP subnet (configurable by the site administrator; defaults to /32 for
IPv4 and /48 for IPv6) or username. You can set the initial delay (default:
250ms) and the cap (default: 30s); it will double with each successive
failed attempt. This helps to prevent users from hammering the RAM/CPU with
a barrage of Argon2i calculations and amplifying a DDoS attempt. The delay
can be configured in two modes: fast-exit (aborts and tells them to wait a
while before trying again) or sleep (avoids a UX wart, but is worse for
security -- an attacker might be able to use this to fill up the worker
process pool and block legitimate traffic). Conversely, in the fast-exit
case, someone who guesses your username would be able to deny a specific
user from logging in.

* Although I can't say it's completely gone, to minimize the risk of
username enumeration via timing information, we "verify" a hash of a dummy
password. Unlike my Underhanded Crypto Contest entry last year, this isn't
a backdoor. (Feel free to verify if you're curious!)

* Just in case something weird happens during the login process, passwords
are obfuscated from stack traces by the use of a HiddenString class.

I like to think I've got everything covered. What am I missing?

Links/references in case anyone wants to read more:

    The project I'm building: https://github.com/paragonie/airship

    The triangle of secure code delivery:
https://defuse.ca/triangle-of-secure-code-delivery.htm

    How Airship will tackle secure code delivery:
https://paragonie.com/blog/2016/05/keyggdrasil-continuum-cryptography-powering-cms-airship

    Zxcvbn: https://github.com/dropbox/zxcvbn

    Libsodium: https://github.com/jedisct1/libsodium

    Halite: https://github.com/paragonie/halite

    Proactively secure long-term user authentication:
https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence#title.2.1

    My UnderCrypto contest entry write-up:
https://paragonie.com/blog/2016/01/on-design-and-implementation-stealth-backdoor-for-web-applications

Kind regards,

Scott Arciszewski
Chief Development Officer
Paragon Initiative Enterprises <https://paragonie.com>

Content of type "text/html" skipped

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.