3

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...

Ilya
  • 5,377
  • 2
  • 18
  • 33
  • 1
    being cloudant is irrelevant. Your question is *how to securely store users passwords* period. Answer is cryptography. PBKDF2 (Password-Based Key Derivation Function 2) https://en.wikipedia.org/wiki/PBKDF2 – PA. Feb 09 '16 at 18:17
  • I disagree. Cloudant is a service, not just a DB. It could (and in my opinion should, because it's a basic function) handle it (the BaaS I'm currently using does, for instance). And even for a DB, a "password" field type can exist. Even if it's not handled, it could provide some utilities. – Ilya Feb 09 '16 at 18:29
  • I wish you good luck – PA. Feb 09 '16 at 19:11
  • Thanks :-) I'm not alone in this, so there's hope. Why code something one billion times in the client when you can code it once in the server ? – Ilya Feb 09 '16 at 19:24

3 Answers3

2

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.

Nuno Cruces
  • 1,584
  • 15
  • 18
  • Thanks for your answer, but I've several questions. First "Hashing passwords "automatically" can be accomplished by an update function" => is that documented ? – Ilya Feb 11 '16 at 08:20
  • 1
    See the update above. Full code for a design doc would be too much to post here. – Nuno Cruces Feb 11 '16 at 12:08
  • OK, so you're encoding yourself server-side and only sha1 is available in update functions (correct ?), but how about decoding ? – Ilya Feb 11 '16 at 12:46
  • CouchDB _expects_ the password to be hashed, not plaintext. CouchDB takes either salted-SHA1 or PBKDF2; Cloudant _only_ supports salted-SHA1. Since Cloudant doesn't encode the password for you, you _must encode the password yourself_. When authenticating requests (with HTTP basic auth), given the username:password, CouchDB/Cloudant will check that it matches the hash. Passwords are always stored as hashes, and hashes are never decoded: _that's the point of having hashes_. – Nuno Cruces Feb 11 '16 at 19:15
  • So just setting the "password_sha" and "salt" fields like you do in the update function is enough ? That's for the "_user" DB, right ? And BTW where did you find these infos ? – Ilya Feb 11 '16 at 20:30
  • 1
    Yes, and yes. [Here](https://wiki.apache.org/couchdb/Security_Features_Overview#Authentication_database) and [here](http://docs.couchdb.org/en/latest/intro/security.html#authentication-database). – Nuno Cruces Feb 12 '16 at 00:36
  • So it seems that CouchDB has adequate security features for users, but somewhat crippled in Cloudant because 1) not activated by default 2) PBKDF2 not available yet 3) not documented. About 1) I've created users but I don't see the hash fields. About 3), the links you provide are CouchDB docs, so no Cloudant docs on the subject that you know of ? – Ilya Feb 12 '16 at 08:19
  • Thanks a lot for your input, it's very helpful. I'll have a phone meeting with IBMers later today, I'll try to fill the blanks with what they tell me. – Ilya Feb 12 '16 at 09:16
  • Having no "official" answer from IBM to that question, I'll accept you answer as the (current) unofficial truth. Thanks again. – Ilya Feb 18 '16 at 20:48
2

Clusers just came out! It may be useful to you. Clusers is a user account creator meant for Cloudant and CouchDB. It uses the older "password_scheme: simple" which Cloudant and older CouchDB.

JasonSmith
  • 72,674
  • 22
  • 123
  • 149
0

First, you really, really do need to read Thomas Pornin's canonical answer to How to Securely Hash Passwords.

Read it right now.

Now read that CouchDB link, and see one of the recommended ways to produce password_sha for 1.3 (and if you're not on at least 1.3, get there).

{ "_id": "org.couchdb.user:username", "_rev": "1-227bbe6ddc1db6826fb6f8a250ef6264", "password_scheme": "pbkdf2", "iterations": 10, "name": "username", "roles": [ ], "type": "user", "derived_key": "aa7dc3719f9c48f1ac72754b28b3f2b6974c2062", "salt": "77bac623e30d91809eecbc974aecf807" }

Make certain that password_scheme is pbkdf2!

See that "iterations": 10 in the same? You need to bump that up by a huge amount - I'd say try a number in the low hundreds of thousands and see how it runs; SHA-1 is very cheap these days.

As far as Cloudant goes, here's a Github repository with some code to have Cloudant use the CouchDB _users.

Anti-weakpasswords
  • 2,604
  • 20
  • 25
  • Thanks for all the infos, I'll look at the links. But your answer is IMO beside the point, so I've updated the question to address it. – Ilya Feb 10 '16 at 08:23
  • @Ilya, did you look at that last link, the Github repository with code for Cloudant to use CouchDB _users? – Anti-weakpasswords Feb 11 '16 at 04:38
  • I did, but Cloudant can already use CouchDB _users ...? Beside it says "Most of this would work on a normal CouchDB instance, but Cloudant requires the salt and sha1-hashed password to be included with user creation." – Ilya Feb 11 '16 at 08:03
  • @Anti-weakpasswords, sound advice, but none of what you've proposed works with Cloudant DBaaS. You certainly can't update CouchDB, and PBKDF2 is not supported. The Github repo just helps a Node.js client do salted SHA1 on the client side. – Nuno Cruces Feb 11 '16 at 12:13