CouchDB Security in a Lightweight Stack? - couchdb

I'm working on a hobbyist project in order to become more familiar with CouchDB. This is my first time working with CouchDB. For this project, my goal is to investigate whether or not it's possible to build a web application with nothing but HTML, CSS, JavaScript, CouchDB, and nginx (i.e., I'm not hosting any of my code in Couch, just data).
It could be that this is highly impractical, but I'd first like to explore all the options in this stack.
At the moment, my biggest questions are about security. Let's say that I have a few databases in CouchDB, each corresponding to a hosted site. For this example, we'll focus on a single database-- i.e., a single site. Some of the content from this site should be available to everyone, even anonymous users; and other stuff should only be available to users with a certain role. What are my options, and how secure are each of them?
I've come up with a few ideas so far, and this is the one I was planning to work on over the weekend:
Add users and roles to /{site_db}/_security.
According to the Couch documentation, doing so will require any request for data in {site_db} to be from an authenticated user.
Add a user called anon, which will only have one role, which is anon.
When the user first visits the site, my JS model will check the status of the current session (GET /_session).
If no session exists, the JS model will authenticate using the anon account.
Define views in my design document.
Any views that should only be available to non-anonymous users should check the roles on the userCtx object.
Validation of any newly-created documents should check userCtx to see if the user's role is on the whitelist.
This seems like it should work, although I can't help thinking it's overly complex, and that there must be a better way. Also, I'm not sure how to prevent the anon user from updating his own user document to add more roles.

If you don't like CouchDB security model, you can implement yours easily in your reverse proxy instead.
Here is an example with Apache but it seems to be very similar in nginx.

Related

Access Control in a Web Application

I'm currently reading a lot about access control possibilites/mechanisms that can be used to protect resources in an application or web application. There's ACL, RBAC, ABAC and many other concepts out there.
Assuming I have developed a simple webservice that returns knowledgebase articles on a route like '/api/article'. The 'controller' connects to the database and fetches all articles and returns them as XML or JSON.
Now I would like to have control over which article in the database is accessible for which user or group. So for instance if user 'peter' accesses the route '/api/article' with his credentials, the webservice shall return only articles that are visible for 'peter'.
I would want to use ACL to control what each user/group can read/write/delete. But what I don't quite understand:
Where does one enforce the access control? Do I just fetch all records in the controller if a user accesses the route '/api/articles' and check each single record against an access control list (that doesn't sound very good performance wise)? Or is there a way that the 'SELECT' statement to the database only return the records that can actually be seen by that specific user?
I really tried hard to find more information on that topic, and there is a lot about different access control mechanisms, but not about where and how the actual enforcement happens...and it even get's more complex if it comes to other actions like modification, deletion and so on...
This is really a matter of implementation and everyone does it its own way. It also depends on the nature of the data, particularly on the size of your authorization (do your have 5 roles and users are attached to them or does each user have a specific set of articles he can access, different for each user - for instance)
If you do not want to make the processing on the controller, you could store the authorization information in your database, in a table which links a user to a set of KB articles, or a role (which would then be reflected in the article). In that case your SELECT query would just pass the authenticated user ID. This requires that the maintenance of the relationship is done of the database, which may not be obvious.
Alternatively you can store the ACL on the controller and build a query from there - for specific articles or groups of articles.
Getting all the articles and checking them on the controller is not a good idea (as you mention), DBs have been designed also to avoid such issues.

Couchdb apply filter server side

I'm developing a mobile app using PouchDB (client-side) and CouchDB (server-side).
I need to secure docs in order to allow users to read/write his own documents only.
I did a filter for this, something like:
function(doc, req) {
return doc.owner == req.userCtx.name || doc.sharedWith == req.userCtx.name;
}
and it works well, but only if the request from client includes the filter:
/somedatabase/_alldocs?filter=filter/secure
I need CouchDB to use the filter in every request, with or without client explicitation, for obvious security reasons. Is this even possible? Otherwise which is the correct approch to handle these security issues?
There is a similar question here but the answer is not applicable in my case since I need to share docs between users and replicate them between all databases is not a valid option.
So I don't know if you have looked at this wiki but it lists few options available. Some of them are outdated tho.
Per user database
Probably the most popular solution. As you said, you need to share documents with other users. This could be done by :
Copy document to other users when sharing. You could have a deamon that listen to _changes feed and update the author file in other users database.
Build a web service to access shared documents (very similar to proxy solution)
Smart Proxy
Build a smart proxy in front of your database and do some business logic to fetch the documents. This gives you more control on your data flow but it will surely be slower.
Note
The validate_doc_read server function could interest you but it has never been part of CouchDB's releases(due to the listed limitations).
Uhm, probably it isn't. The app that we are developing need to share documents with different users. any doc could be shared with a different group of users

CouchDB simple document design: need feedback

I am in the process of designing document storage for CouchDB and would really appreciate some feedback. These documents are to represent "assets".
These databases will also be synced locally to the browser via pouchdb.
Requirements:
Each user can have many assets
Users can share assets with others by providing them with a URI such as (xyz.com/some_id). Once users click this URI, they are considered to have been "joined" and are now part of a group.
Group users can share assets of their own with other members of the group.
My design
Each user will have his/her own database to store assets - let's call it "user". Each user DB will be prefixed with the his/her unique ID.
Shared assets will be stored in a separate database - let's call it "group". shared assets are DUPLICATED here and have an additional field for userId (to indicate creator).
Group database is prefixed with a unique ID just like a user database is prefixed with one too.
The reason for storing group assets in a separate database is because when pouchdb runs locally, it only knows about the current user and his/her shared assets. It does not know about other users and will should not query these "other" users' databases.
Any input would be GREATLY appreciated.
Seems like a great design. Another alternative would be to just have one database per group ("role"), and then replicate from a user's group(s) into their local PouchDB.
That might get hairy, though, when it comes time to replicate back to the server, because you're going to have to filter the documents as they leave the user's local database, depending on which group-database they belong to. Still, you're going to have to do that on the server side anyway with your current design.
Either way is fine, honestly. The only downside of your current approach is that documents are duplicated on the server side (once per user-db and once per group-db). On the other hand, your client code becomes dead-simple, because you don't have to do any filtered replication. If you have enough space on your server not to worry about it, then I would definitely go with your approach. :)

How to ease CouchDB read/write restrictions on _users database

In my couchapp two databases are being used
1 Is for application data
2 Is "_users" database.
In my application In one form I'm trying to implement autocomplete where data source is a "view" created in "_users" database.
Now when I login with normal user id other than admin. While trying to access the view inside "_users" database I'm getting the error 403 which is :
{"error":"forbidden","reason":"Only admins can access design document actions for system databases."}
Is it possible for me to allow and limit the access for non admin users to that view only ? So I can get the list of users from _users database into my application.
I've never been able to do many tasks that require much custom with CouchDB by itself. I've always needed a script somewhere else that gives me the info that I need.
What works for me is this setup:
A gatekeeper Sinatra app that has admin access to my CouchDB
Using CouchDB's config to proxy to my Sinatra app. httpd_global_handlers _my_service {couch_httpd_proxy, handle_proxy_req, <<"http://127.0.0.1:9999">>}
The reason for the proxy is because any request that comes through to your gatekeeper will have the AuthSession token set. Inside your gatekeeper, you can GET localhost:5984/_session passing the AuthSession cookie along, it will tell you who is making the request, allowing you to look them up and see if they have access, or just give everyone access to whatever you like. Another reason for the proxy is to avoid any CORS nonsense since you're making the request to yourserver:5984/_my_service.
Update
A purely client-side/javascript solution means that it will be fundamentally insecure at some point, since well, everything is on the client-side. But perhaps your application, doesn't need to be that secure. That's up to you.
One workaround could be to make your application authenticate as a predefined admin, and then create more admin users that way. You could authenticate once when your application boots or on an as needed basis.
The "problem" is that CouchDB sees the _users database as fundamentally special, and doesn't give you the opportunity to change the credential requirements like other databases. Normally you would be able to use the _security document to give role based or user based access. But that's not possible with _users.
An alternative implementation might be to keep track of your own users and forgo the _users database altogether. In that case you could set your own cookies and have your own login and logout methods that don't depend on CouchDB's authentication scheme. You could query your own _view/users because it would be in your main database. Things wouldn't be locked down tight but they would work fine as long as no one was interested in hacking your system. :)

How to Secure CouchDB

CouchDB access as a rest service seems insecure. Anyone can hit the database and delete/add documents once it is exposed.
What strategies are there to secure the CouchDB?
A lot has changed since 2009, so I'm going to throw an answer in here. This answer is drawn from this page on the wiki.
CouchDB has a _users database that serves the purpose of defining users. Here's the gist straight from the wiki:
An anonymous user can only create a new document.
An authenticated user can only update their own document.
A server or database admin can access and update all documents.
Only server or database admins can create design documents and access views and _all_docs and _changes.
Then, for any given database you can define permissions by name or by role. The way authentication is implemented is through a _session Database. Sending a valid username and password to the _session DB returns an authentication cookie. This is one of several option for CouchDB Authentication. There're a few more options:
This option is a little old 1.0 was a few months back, we're on 1.2 as of today. But it's still very well outlined.
And this one from "The Definitive Guide"
Also, depending on which hosting service you might be using, you'll have the option to restrict access to couch over SSL.
Between Node, Couch, and a variety of other technologies that effectively scale horizontally (adding more servers) there's an interesting kind of pressure or incentive being put on developers to make applications that scale well in that manner. But that's a separate issue all together.
The only thing which really works currently security wise is something like this in your CouchDB configuration.
[couch_httpd_auth]
require_valid_user=true
[admins]
admin = sekrit
This puts basic HTTP auth on all of CouchDB. Even this is not well supportet in client libraries. For python e.g. you need a patched library.
The second approach is to put a proxy in front of CouchDB and let the proxy do the authentication and authorization work. Due to CouchDB's RESTful design this is quite easy.
All other approaches must be considered up to now highly experimental.
This may be a little different from your original question. If your couchdb is only a back-end store for a full server app, you can make a special account for the server app to use and require those credentials for access to couchdb.
On the other hand, a pure couch app that people hit directly through a javascript client needs a lot of care to be secure.
Using rewrites is not optional. You need a vhosts config that forces requests to your domain through your rewrites.
Rewrite routes */_all_docs and /*/_design/* to a 404 page. Otherwise users can list every document or get your whole app.
Rewrite generic object access, ie /dbname/:id to a show that can deny access if the user is not allowed to see the document. Unfortunately there is no equivalent workaround for doc-based access control of attachments.
We used haproxy to filter GET requests on _users. There is no legit reason for someone from outside to get a user record or list all your users. We want users to be able to register so we need write access. Currently couch cannot block read access to a db and simultaneously allow writes. It's a bug. Filtering with something like haproxy is our best workaround for now.
Use your own database to keep contact information that is in addition to what is provided by _users. This allows more control over access.
validate_doc_update should carefully reject any writes that should not be allowed.
In every case you need to imagine what someone who understood the system could do to subvert it and lock down those avenues of attack.
CouchDB does cookies, SSL, oauth, and multi-users just fine:
Here's some actual code in python:
from couchdb import Server
s = Server("https://user:password#example.com:6984")
Request the cookie: url encoded above and below, of course
You have to put the credentials twice to get started with the first cookie
Both in the Server() constructor as well as the _session POST body
code, message, obj = s.resource.post('_session',headers={'Content-Type' : 'application/x-www-form-urlencoded'}, body="name=user&password=password")
assert(code == 200)
Now you have received a cookie, extract it
cookie = message["Set-Cookie"].split(";", 1)[0].strip()
Now, exit python and restart
Next, Request a server object, but without the username and password this time
s = Server("https://example.com:6984")
s.resource.headers["Cookie"] = cookie
Yay, no password, try to access the database:
db = s["database"]
Optionally set the "persistent" cookie option on the server side to make the cookie last longer.
Have you read CouchDB documentation http://couchdb.apache.org/docs/overview.html? It has a "Security and Validation" section that addresses some of your concerns.

Resources