Search code examples
databaseauthenticationpasswordscouchdbcloudant

How to securely store user passwords in a Cloudant DB?


How are you supposed to store users passwords in a Cloudant DB ? By users, I mean users of an application that uses Cloudant as a backend.

I've searched the docs, but I found nothing on that topic. There's a _users database, in which you can create users and add a "password" field, but the password is a regular field that the DB admin (and possibly others) can read.

Is there a built-in way to hide it from view or encrypt it ?

EDIT

I've found a piece of the puzzle, the CouchDB security feature that encrypts user's passwords.

Since CouchDB 1.2.0, the password_sha and salt fields are automatically created when a password field is present in the user document. When the user document is written, CouchDB checks for the existence of the password field and if it exists, it will generate a salt, hash the value of the password field and hash the concatenation of the password hash and the salt. It then writes the resulting password into the password_sha field and the salt into the salt field. The password field is removed.

This has the following implications: Clients no longer have to calculate the password salt and hash manually. Yay.

Now what's missing is the link between that underlying DB feature and Cloudant (just setting the password field in the user document is not working).

EDIT 2

Found that other question which is similar to this one - it's a broader problem, but specifically for web apps. There's an accepted answer from @JasonSmith that addresses my question:

Can I use CouchDB security features

Answer's "yes you can"

Cloudant does not yet have the newer CouchDB feature where the server will automatically hash the password for you

But the CouchDB doc states that this features is included in the 1.20 version from 2013! How is that a "newer" feature?

From the doc, I gather that Cloudant uses CouchDB 1.61.

To recap:

  • the feature exists,
  • it's a CouchDB security feature existing in the CouchDB version that Cloudant uses,
  • Cloudant can be configured to use CouchDB security features

So... the missing link is really really small...


Solution

  • As you've discovered, Cloudant does not automatically hash passwords server side, as introduced in Couch 1.2. Also, it only supports the simple password scheme: salted SHA1 (which you may find insufficient). That's how passwords are supposed to be saved (not plain text).

    It also misses a bunch of other security features, such as special access rules to the _users database (described here).

    Hashing passwords "automatically" can be accomplished by an update function (special access rules could be implemented through show/list functions). I have done this myself:

    function (doc, req) {
    
        var body = JSON.parse(req.body || '{}') || {};
    
        if (doc == null) doc = {
            _id: req.id,
            type: 'user'
        };
    
        doc.name = body.name;
        doc.roles = body.roles;
        doc.salt = req.uuid;
        doc.password_scheme = 'simple';
        doc.password_sha = hex_sha1(body.password + doc.salt);
    
        return [doc, { json: doc }];
    }
    

    Get hex_sha1 from here. Set the above as an update function in a design doc on the _users database. You can also use this as a validation function.

    Then instead of PUTing a user into the database, you PUT the same JSON to the update function, and it generates the salted hash before committing it to the database.

    If salted SHA1 is not enough for your purposes you can't rely on _users on Cloudant, as is.


    Not knowing more about your design, I can't really give much advice.

    But I should warn you that, thanks to poor _users support, it's e.g. nearly impossible to effectively implement a 2-tier architecture on Cloudant. I'd be glad to be contradicted by someone who knows better, but after banging my head against this for months (and nagging support), this is the conclusion I've come to.

    Eventually, you'll need an application layer to do user management, either through _users or API keys. Once you have such a layer, that's where you can hash passwords, and/or skip the _users database and do user management some other way. Every sample posted by Cloudant eventually does this, as soon as things get complicated enough (and none of the samples scale to tens of thousands of users, btw).


    Finally, to @Anti-weakpasswords, who says you must go with PBKDF2 and huge iteration counts.

    This is sound advice regarding saving passwords in general, but:

    1. this doesn't work with Cloudant, at all;
    2. it doesn't really work very well with CouchDB either.

    First, as stated, if salted SHA1 is all that Cloudant supports, period.

    But even for CouchDB, it's bad advice. With basic HTTP auth, you're sending the password on every single request. Key stretching with huge iteration counts on every single request would put tremendous pressure on the server, so large iteration counts are not recommended (that's why the docs have a 10 in there). If you're going down that road, you need to make sure you always use _session and cookies, and avoid basic auth like the plague.

    More likely, if you take security seriously, you need to get a layer between the client and the database that handles user management some other way, and have decoupled database users/roles with strong enough passwords not to need strong hashing at all.