Freshly installed, CouchDB offers little to no security. There’s even a name for that temporary state: its called the “Admin Party”, as every user is considered to be an admin.
While it might seem dangerous, it’s actually a nice thing as it lets you fine tune the level of security you want to obtain.
Say you’re in learning mode and you just want to figure out what CouchDB is and how it works. You’re creating databases and documents and fetching them through design documents. At this time there’s no need to secure anything, and anyway, CouchDB is bound to your local address so you’re the only one who can mess around with it.
But start using it in production, with an application that manipulates sensible information, and you’ll have to go deeper into securing it.
And that’s what i’m going to describe, in 3 steps.
CouchDB stores its users in the _users database. A user document looks like this:
{
"_id": "org.couchdb.user:couchdb",
"_rev": "1-8995e8ff247dae75048ab2dc800136d7",
"name": "couchuser",
"password": null,
"roles": [
],
"type": "user"
}
The _id is the user’s name prefixed with “org.couchdb.user”. _rev is the document’s revision. name is the name of the user, the one that the user will remember, along with his password.
Roles is a feature that we’ll develop when speaking about _security documents. It allows to create some sort of groups for the users.
Finally, type can only be user.
While roles and type should be defined by the application during the account creation phase, name and password are chosen by the user. They are his credentials.
More information on the authentication database in the official wiki
So, how does one authenticate himself?
Basic authentication
This authentication mode is using the name:password@url style. For a couch user couchuser whose password is couchpass, the request url would be:
http://couchuser:couchpass@localhost:5984/db/document
Querying CouchDB with the user’s credentials will create a “user context” object that will be passed around to control functions, such as the validation functions we are going to talk about.
Cookie authentication
The second way of authenticating a user is by issuing a POST request on _session. The request must embed the credentials:
curl -vX POST http://localhost:5984/_session -H 'application/x-www-form-urlencoded' \
-d 'name=couchuser&password=couchpass'
The response’s header will have a Set-Cookie field that can now be re-used to authenticate the user for 10 minutes, depending on your configure files.
HTTP/1.1 200 OK
< Set-Cookie: \
AuthSession=Y291Y2hkYjo1MDQ2NUI3MjrL7w8A3NhUpW6XkVvVVJ25epeGsw; \
Version=1; Path=/; HttpOnly
You’re going to need to be able to extract the AuthSession from the header before passing it to further requests. Here’s an example of a request embedding AuthSession:
curl [your request here] --cookie \
AuthSession=Y291Y2hkYjo1MDQ2NUI3MjrL7w8A3NhUpW6XkVvVVJ25epeGsw
More on authentication is available here.
Now that the user is authenticated, we can control which databases he has access to.
Database-wise, there are two types of users, admins and readers.
Readers can create, read and write document except for design documents. Database admins can also modify design documents and _security documents. They can do some other stuff but it’s not the purpose of this article.
The _security document will simply specify what type of database user a CouchDB user is:
// This example comes from the CouchDB guide
{
"admins" : {
"names" : ["joe", "phil"],
"roles" : ["boss"]
},
"readers" : {
"names" : ["dave"],
"roles" : ["producer", "consumer"]
}
}
Notice the “roles” fields. Instead of adding every user in the names array of the _security document, you could instead specify which roles are admins or readers, and assign the adequate role to the user.
As soon as a _security document is added to a database, any user not listed in is not granted any access. [need to double check on that]
Authorization documents in the official wiki are explained here.
We can now prevent users from wandering around in our application. But if a user’s granted access to a database, how can we secure write access to documents that he doesn’t own?
Design documents can have a validation function that is called everytime a document is altered. Here’s an example of a function that checks if the user who tries to modify a document is its owner.
"validate_doc_update": "function (newDoc, oldDoc, userCtx) {
if (newDoc.author != userCtx.name) {
throw({
forbidden: 'Only' + userCtx.name + ' may edit this document'
});
}
}"
Throw defines the error that is returned to the application. Other helper functions are available too:
UserCtx has other properties:
And a last argument is passed to the validation function, the database’s security document.
To go further, we could imagine adding a proxy, between CouchDB and the application, that would filter out dangerous requests. Querying the database through https should also be considered.
In a future article, I will explain how to secure CouchDB when used by an OlivesJS/EmilyJS application.