You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by de...@apache.org on 2013/10/01 15:46:52 UTC

[42/50] [abbrv] git commit: updated refs/heads/replicator-redesign to 066d69b

Import Security chapter from Guide to CouchDB. Describe _users db.

TODO: Authorization and Database Security parts.

COUCHDB-1822


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/d513c576
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/d513c576
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/d513c576

Branch: refs/heads/replicator-redesign
Commit: d513c57619251d4b7b0023a7f2b86b69b4a11300
Parents: 5522488
Author: Alexander Shorin <kx...@apache.org>
Authored: Sat Sep 28 15:45:14 2013 +0400
Committer: Alexander Shorin <kx...@apache.org>
Committed: Sat Sep 28 15:45:14 2013 +0400

----------------------------------------------------------------------
 share/doc/build/Makefile.am      |   3 +
 share/doc/src/intro/index.rst    |   1 +
 share/doc/src/intro/security.rst | 525 ++++++++++++++++++++++++++++++++++
 3 files changed, 529 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/d513c576/share/doc/build/Makefile.am
----------------------------------------------------------------------
diff --git a/share/doc/build/Makefile.am b/share/doc/build/Makefile.am
index 870976a..ef98bde 100644
--- a/share/doc/build/Makefile.am
+++ b/share/doc/build/Makefile.am
@@ -115,6 +115,7 @@ html_files = \
     html/_sources/intro/futon.txt \
     html/_sources/intro/index.txt \
     html/_sources/intro/overview.txt \
+    html/_sources/intro/security.txt \
     html/_sources/intro/tour.txt \
     html/_sources/intro/why.txt \
     html/_sources/maintenance/compaction.txt \
@@ -234,6 +235,7 @@ html_files = \
     html/intro/futon.html \
     html/intro/index.html \
     html/intro/overview.html \
+    html/intro/security.html \
     html/intro/tour.html \
     html/intro/why.html \
     html/maintenance/compaction.html \
@@ -375,6 +377,7 @@ src_files = \
     ../src/intro/futon.rst \
     ../src/intro/index.rst \
     ../src/intro/overview.rst \
+    ../src/intro/security.rst \
     ../src/intro/tour.rst \
     ../src/intro/why.rst \
     ../src/maintenance/compaction.rst \

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d513c576/share/doc/src/intro/index.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/intro/index.rst b/share/doc/src/intro/index.rst
index a934fae..1c1088b 100644
--- a/share/doc/src/intro/index.rst
+++ b/share/doc/src/intro/index.rst
@@ -51,5 +51,6 @@ teach how to use CouchDB.
    consistency
    tour
    api
+   security
    futon
    curl

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d513c576/share/doc/src/intro/security.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/intro/security.rst b/share/doc/src/intro/security.rst
new file mode 100644
index 0000000..1c25159
--- /dev/null
+++ b/share/doc/src/intro/security.rst
@@ -0,0 +1,525 @@
+.. Licensed under the Apache License, Version 2.0 (the "License"); you may not
+.. use this file except in compliance with the License. You may obtain a copy of
+.. the License at
+..
+..   http://www.apache.org/licenses/LICENSE-2.0
+..
+.. Unless required by applicable law or agreed to in writing, software
+.. distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+.. WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+.. License for the specific language governing permissions and limitations under
+.. the License.
+
+
+.. _intro/security:
+
+********
+Security
+********
+
+In this chapter, we'll look at the basic security mechanisms in CouchDB: the
+`Admin Party`, `Basic Authentication`, `Cookie Authentication`; how CouchDB
+handles users and protects their credentials.
+
+==============
+Authentication
+==============
+
+.. _intro/security/admin_party:
+
+The Admin Party
+===============
+
+When you start out fresh, CouchDB allows any request to be made by anyone.
+Create a database? No problem, here you go. Delete some documents? Same deal.
+CouchDB calls this the `Admin Party`. Everybody has privileges to do anything.
+Neat.
+
+While it is incredibly easy to get started with CouchDB that way,
+it should be obvious that putting a default installation into the wild is
+adventurous. Any rogue client could come along and delete a database.
+
+A note of relief: by default, CouchDB will listen only on your loopback
+network interface (``127.0.0.1`` or ``localhost``) and thus only you will be
+able to make requests to CouchDB, nobody else. But when you start to open up
+your CouchDB to the public (that is, by telling it to bind to your machine's
+public IP address), you will want to think about restricting access so that
+the next bad guy doesn't ruin your admin party.
+
+In our previous discussions, we dropped some keywords about how things
+without the `Admin Party` work. First, there's *admin* itself, which implies
+some sort of super user. Then there are *privileges*. Let's explore these terms
+a little more.
+
+CouchDB has the idea of an *admin user* (e.g. an administrator, a super user,
+or root) that is allowed to do anything to a CouchDB installation. By default,
+everybody is an admin. If you don't like that, you can create specific admin
+users with a username and password as their credentials.
+
+CouchDB also defines a set of requests that only admin users are allowed to
+do. If you have defined one or more specific admin users, CouchDB will ask for
+identification for certain requests:
+
+- Creating a database (:put:`PUT /database </{db}>`)
+- Deleting a database (:put:`DELETE /database </{db}>`)
+- Setup a database security (:put:`PUT /database/_security
+  </{db}/_security>`)
+- Creating a design document (:put:`PUT /database/_design/app
+  </{db}/_design/{ddocname}>`)
+- Updating a design document (:put:`PUT /database/_design/app?rev=1-4E2
+  </{db}/_design/{ddocname}>`)
+- Deleting a design document (:delete:`DELETE /database/_design/app?rev=2-6A7
+  </{db}/_design/{ddocname}>`)
+- Execute a temporary view (:post:`POST /database/_temp_view
+  </{db}/_temp_view>`)
+- Triggering compaction (:post:`POST /database/_compact </{db}/_compact>`)
+- Reading the task status list (:get:`GET /_active_tasks </_active_tasks>`)
+- Restarting the server (:post:`:POST /_restart </_restart>`)
+- Reading the active configuration (:get:`GET /_config </_config>`)
+- Updating the active configuration (:put:`PUT /_config/section/key
+  </_config/{section}/{key}>`)
+
+
+Creating New Admin User
+-----------------------
+
+Let's do another walk through the API using `curl` to see how CouchDB behaves
+when you add admin users.
+
+::
+
+  > HOST="http://127.0.0.1:5984"
+  > curl -X PUT $HOST/database
+  {"ok":true}
+
+When starting out fresh, we can add a database. Nothing unexpected. Now let's
+create an admin user. We'll call her ``anna``, and her password is ``secret``.
+Note the double quotes in the following code; they are needed to denote a string
+value for the :ref:`configuration API <api/config>`::
+
+  > curl -X PUT $HOST/_config/admins/anna -d '"secret"'
+  ""
+
+As per the :ref:`_config <api/config>` API's behavior, we're getting
+the previous value for the config item we just wrote. Since our admin user
+didn't exist, we get an empty string.
+
+
+Hashing Passwords
+-----------------
+
+Seeing the plain-text password is scary, isn't it? No worries, CouchDB doesn't
+show up the plain-text password anywhere. It gets hashed right away. The hash
+is that big, ugly, long string that starts out with ``-hashed-``.
+How does that work?
+
+#. Creates a new 128-bit UUID. This is our *salt*.
+#. Creates a sha1 hash of the concatenation of the bytes of the plain-text
+   password and the salt ``(sha1(password + salt))``.
+#. Prefixes the result with ``-hashed-`` and appends ``,salt``.
+
+To compare a plain-text password during authentication with the stored hash,
+the same procedure is run and the resulting hash is compared to the stored
+hash. The probability of two identical hashes for different passwords is too
+insignificant to mention (c.f. `Bruce Schneier`_). Should the stored hash fall
+into the hands of an attacker, it is, by current standards, way too inconvenient
+(i.e., it'd take a lot of money and time) to find the plain-text password from
+the hash.
+
+.. _Bruce Schneier: http://en.wikipedia.org/wiki/Bruce_Schneier
+
+But what's with the ``-hashed-`` prefix? When CouchDB starts up, it reads a set
+of `.ini` files with config settings. It loads these settings into an internal
+data store (not a database). The config API lets you read the current
+configuration as well as change it and create new entries. CouchDB is writing
+any changes back to the `.ini` files.
+
+The `.ini` files can also be edited by hand when CouchDB is not running.
+Instead of creating the admin user as we showed previously, you could have
+stopped CouchDB, opened your `local.ini`, added ``anna = secret`` to the
+:ref:`[admins] section <config/admins>`, and restarted CouchDB. Upon reading
+the new line from local.ini, CouchDB would run the hashing algorithm and write
+back the hash to `local.ini`, replacing the plain-text password. To make sure
+CouchDB only hashes plain-text passwords and not an existing hash a second time,
+it prefixes the hash with ``-hashed-``, to distinguish between plain-text
+passwords and hashed passwords. This means your plain-text password can't start
+with the characters ``-hashed-``, but that's pretty unlikely to begin with.
+
+.. note::
+
+   Since :ref:`1.3.0 release <release/1.3.0>` CouchDB uses ``-pbkdf2-`` prefix
+   by default to sign about using `PBKDF2`_ hashing algorithm instead of `SHA1`.
+
+   .. _PBKDF2: http://en.wikipedia.org/wiki/PBKDF2
+
+
+.. _intro/security/basicauth:
+
+Basic Authentication
+====================
+
+Now that we have defined an admin, CouchDB will not allow us to create new
+databases unless we give the correct admin user credentials. Let's verify::
+
+  > curl -X PUT $HOST/somedatabase
+  {"error":"unauthorized","reason":"You are not a server admin."}
+
+That looks about right. Now we try again with the correct credentials::
+
+  > HOST="http://anna:secret@127.0.0.1:5984"
+  > curl -X PUT $HOST/somedatabase
+  {"ok":true}
+
+If you have ever accessed a website or FTP server that was password-protected,
+the ``username:password@`` URL variant should look familiar.
+
+If you are security conscious, the missing ``s`` in ``http://`` will make you
+nervous. We're sending our password to CouchDB in plain text. This is a bad
+thing, right? Yes, but consider our scenario: CouchDB listens on ``127.0.0.1``
+on a development box that we're the sole user of. Who could possibly sniff our
+password?
+
+If you are in a production environment, however, you need to reconsider. Will
+your CouchDB instance communicate over a public network? Even a LAN shared
+with other collocation customers is public. There are multiple ways to secure
+communication between you or your application and CouchDB that exceed the
+scope of this documentation. CouchDB as of version :ref:`1.1.0 <release/1.1.0>`
+comes with :ref:`SSL built in <config/ssl>`.
+
+.. seealso::
+
+   :ref:`Basic Authentication API Reference <api/auth/basic>`
+
+
+.. _intro/security/cookie:
+
+Cookie Authentication
+=====================
+
+Basic authentication that uses plain-text passwords is nice and convenient,
+but not very secure if no extra measures are taken. It is also a very poor
+user experience. If you use basic authentication to identify admins,
+your application's users need to deal with an ugly, unstylable browser modal
+dialog that says non-professional at work more than anything else.
+
+To remedy some of these concerns, CouchDB supports cookie authentication.
+With cookie authentication your application doesn't have to include the ugly
+login dialog that the users' browsers come with. You can use a regular HTML
+form to submit logins to CouchDB. Upon receipt, CouchDB will generate a
+one-time token that the client can use in its next request to CouchDB. When
+CouchDB sees the token in a subsequent request, it will authenticate the user
+based on the token without the need to see the password again. By default,
+a token is valid for 10 minutes.
+
+To obtain the first token and thus authenticate a user for the first time,
+the username and password must be sent to the :ref:`_session <api/auth/session>`
+API. The API is smart enough to decode HTML form submissions, so you don't have
+to resort to any smarts in your application.
+
+If you are not using HTML forms to log in, you need to send an HTTP request
+that looks as if an HTML form generated it. Luckily, this is super simple::
+
+  > HOST="http://127.0.0.1:5984"
+  > curl -vX POST $HOST/_session \
+         -H 'Content-Type:application/x-www-form-urlencoded' \
+         -d 'name=anna&password=secret'
+
+CouchDB replies, and we'll give you some more detail::
+
+  < HTTP/1.1 200 OK
+  < Set-Cookie: AuthSession=YW5uYTo0QUIzOTdFQjrC4ipN-D-53hw1sJepVzcVxnriEw;
+  < Version=1; Path=/; HttpOnly
+  > ...
+  <
+  {"ok":true}
+
+A :statuscode:`200` response code tells us all is well, a :header:`Set-Cookie`
+header includes the token we can use for the next request, and the standard JSON
+response tells us again that the request was successful.
+
+Now we can use this token to make another request as the same user without
+sending the username and password again::
+
+  > curl -vX PUT $HOST/mydatabase \
+         --cookie AuthSession=YW5uYTo0QUIzOTdFQjrC4ipN-D-53hw1sJepVzcVxnriEw \
+         -H "X-CouchDB-WWW-Authenticate: Cookie" \
+         -H "Content-Type:application/x-www-form-urlencoded"
+  {"ok":true}
+
+You can keep using this token for 10 minutes by default. After 10 minutes you
+need to authenticate your user again. The token lifetime can be configured
+with the timeout (in seconds) setting in the :ref:`couch_httpd_auth
+<config/couch_httpd_auth>` configuration section.
+
+.. seealso::
+
+   :ref:`Cookie Authentication API Reference <api/auth/cookie>`
+
+
+=======================
+Authentication Database
+=======================
+
+You may already note, that CouchDB administrators are defined within config file
+and you now wondering does regular users are also stored there. No, they don't.
+CouchDB has special `authentication database` -- ``_users`` by default -- that
+stores all registered users as JSON documents.
+
+CouchDB uses special database (called ``_users`` by default) to store
+information about registered users. This is a `system database` -- this means
+that while it shares common :ref:`database API <api/database>`, there are some
+special security-related constraints applied and used agreements on documents
+structure. So how `authentication database` is different from others?
+
+- Only administrators may browse list of all documents
+  (:get:`GET /_users/_all_docs </{db}/_all_docs>`)
+- Only administrators may listen :ref:`changes feed
+  <changes>` (:get:`GET /_users/_changes </{db}/_changes>`)
+- Only administrators may execute design functions like :ref:`views <viewfun>`,
+  :ref:`shows <showfun>` and :ref:`others <ddocs>`
+- Only administrators may :method:`GET`, :method:`PUT` or :method:`DELETE`
+  any document (to be honest, that they always can do)
+- There is special design document ``_auth`` that cannot be modified
+- Every document (of course, except `design documents`) represents registered
+  CouchDB users and belong to him
+- Users may only access (:get:`GET /_users/org.couchdb.user:Jan
+  </{db}/{docid}>`) or modify (:put:`PUT /_users/org.couchdb.user:Jan
+  </{db}/{docid}>`) documents that they owns
+
+These draconian rules are reasonable: CouchDB cares about user's personal
+information and doesn't discloses it for everyone. Often, users documents are
+contains not only system information like `login`, `password hash` and `roles`,
+but also sensitive personal information like: real name, email, phone, special
+internal identifications and more - this is not right information that you
+want to share with the World.
+
+
+Users Documents
+===============
+
+Each CouchDB user is stored in document format. These documents are contains
+several *mandatory* fields, that CouchDB handles for correct authentication
+process:
+
+- **_id** (*string*): Document ID. Contains user's login with special prefix
+  :ref:`org.couchdb.user`
+- **derived_key** (*string*): `PBKDF2`_ key
+- **name** (*string*): User's name aka login. **Immutable** e.g. you cannot
+  rename existed user - you have to create new one
+- **roles** (*array* of *string*): List of user roles. CouchDB doesn't provides
+  any builtin roles, so you're free to define your own depending on your needs.
+  However, you cannot set system roles like ``_admin`` there. Also, only
+  administrators may assign roles to users - by default all users have no roles
+- **password_sha** (*string*): Hashed password with salt. Used for ``simple``
+  `password_scheme`
+- **password_scheme** (*string*): Password hashing scheme. May be ``simple`` or
+  ``pbkdf2``
+- **salt** (*string*): Hash salt. Used for ``simple`` `password_scheme`
+- **type** (*string*): Document type. Constantly have value ``user``
+
+Additionally, you may specify any custom fields that are relates to the target
+user. This is good place to store user's private information because only he and
+CouchDB administrators may browse it.
+
+.. _org.couchdb.user:
+
+Why ``org.couchdb.user:`` prefix?
+---------------------------------
+
+The reason to have special prefix before user's login name is to have
+namespaces which users are belongs to. This prefix is designed to prevent
+replication conflicts when you'll try to merge two `_user` databases or more.
+
+For current CouchDB releases, all users are belongs to the same
+``org.couchdb.user`` namespace and this cannot be changed, but we'd made
+such design decision for future releases.
+
+
+Creating New User
+=================
+
+Creating new user is a very trivial operation. You need just to send single
+:method:`PUT` request with user's data to CouchDB. Let's create user with login
+`jan` and password `apple`::
+
+  curl -X PUT http://localhost:5984/_users/org.couchdb.user:jan \
+       -H "Accept: application/json" \
+       -H "Content-Type: application/json" \
+       -d '{"name": "jan", "password": "apple", "roles": [], "type": "user"}'
+
+This `curl` command will produce next HTTP request:
+
+.. code-block:: http
+
+  PUT /_users/org.couchdb.user:jan HTTP/1.1
+  Accept: application/json
+  Content-Length: 62
+  Content-Type: application/json
+  Host: localhost:5984
+  User-Agent: curl/7.31.0
+
+And CouchDB responds with:
+
+.. code-block:: http
+
+  HTTP/1.1 201 Created
+  Cache-Control: must-revalidate
+  Content-Length: 83
+  Content-Type: application/json
+  Date: Fri, 27 Sep 2013 07:33:28 GMT
+  ETag: "1-e0ebfb84005b920488fc7a8cc5470cc0"
+  Location: http://localhost:5984/_users/org.couchdb.user:jan
+  Server: CouchDB (Erlang OTP)
+
+  {"ok":true,"id":"org.couchdb.user:jan","rev":"1-e0ebfb84005b920488fc7a8cc5470cc0"}
+
+Document successfully created what also means that user `jan` have created too!
+Let's check is this true::
+
+  curl -X POST http://localhost:5984/_session -d 'name=jan&password=apple'
+
+CouchDB should respond with:
+
+.. code-block:: javascript
+
+  {"ok":true,"name":"jan","roles":[]}
+
+Which means that username was recognized and password's hash matches with stored
+one. If we specify wrong login and/or password, CouchDB will notify us with
+the next error message:
+
+.. code-block:: javascript
+
+  {"error":"unauthorized","reason":"Name or password is incorrect."}
+
+
+Password Changing
+=================
+
+This is quite common situation: user had forgot his password, it was leaked
+somehow (via copy-paste, screenshot, or by typing in wrong chat window) or
+something else. Let's change password for our user `jan`.
+
+First of all, let's define what is the password changing from the point of
+CouchDB and the authentication database. Since "users" are "documents", this
+operation is nothing, but updating the document with special field ``password``
+which contains the *plain text password*. Scared? No need to: the authentication
+database has special internal hook on  document update which looks for this
+field and replaces it with the *secured hash*, depending on chosen
+``password_scheme``.
+
+Summarizing above, we need to get document content, add ``password`` field
+with new plain text password value and store JSON result to the authentication
+database.
+
+::
+
+  curl -X GET http://localhost:5984/_users/org.couchdb.user:jan
+
+.. code-block:: javascript
+
+  {
+    "_id": "org.couchdb.user:jan",
+    "_rev": "1-e0ebfb84005b920488fc7a8cc5470cc0",
+    "derived_key": "e579375db0e0c6a6fc79cd9e36a36859f71575c3",
+    "iterations": 10,
+    "name": "jan",
+    "password_scheme": "pbkdf2",
+    "roles": [],
+    "salt": "1112283cf988a34f124200a050d308a1",
+    "type": "user"
+  }
+
+Here is our user's document. We may strip hashes from stored document to reduce
+amount of posted data::
+
+  curl -X PUT http://localhost:5984/_users/org.couchdb.user:jan \
+       -H "Accept: application/json" \
+       -H "Content-Type: application/json" \
+       -H "If-Match: 1-e0ebfb84005b920488fc7a8cc5470cc0" \
+       -d '{"name":"jan", "roles":[], "type":"user", "password":"orange"}'
+
+.. code-block:: javascript
+
+  {"ok":true,"id":"org.couchdb.user:jan","rev":"2-ed293d3a0ae09f0c624f10538ef33c6f"}
+
+Updated! Now let's check that password was really changed::
+
+  curl -X POST http://localhost:5984/_session -d 'name=jan&password=apple'
+
+CouchDB should respond with:
+
+.. code-block:: javascript
+
+  {"error":"unauthorized","reason":"Name or password is incorrect."}
+
+Looks like the password ``apple`` is wrong, what about ``orange``?
+
+::
+
+  curl -X POST http://localhost:5984/_session -d 'name=jan&password=orange'
+
+CouchDB should respond with:
+
+.. code-block:: javascript
+
+  {"ok":true,"name":"jan","roles":[]}
+
+Hooray! You may wonder why so complex: need to retrieve user's document, add
+special field to him, post it back - where is one big button that changes the
+password without worry about document's content? Actually, :ref:`Futon
+<intro/futon>` has such at the right bottom corner if you have logged in -
+all implementation details are hidden from your sight.
+
+.. note::
+
+  There is no password confirmation for API request: you should implement it
+  on your application layer like Futon does.
+
+
+Users Public Information
+========================
+
+.. versionadded:: 1.4
+
+Sometimes users *wants* to share some information with the World. For instance,
+their contact email to let other users get in touch with him. To solve this
+problem, but still keep sensitive and private information secured there is
+special :ref:`configuration <config>` option :ref:`public_fields
+<config/couch_httpd_auth/public_fields>`. In this options you may define comma
+separated list of users document fields that will be publicity available.
+
+Normally, if you request any user's document and you're not administrator or
+this document owner, CouchDB will respond with :statuscode:`404`::
+
+  curl http://localhost:5984/_users/org.couchdb.user:robert
+
+.. code-block:: javascript
+
+  {"error":"not_found","reason":"missing"}
+
+This response is constant for both cases when user exists or not exists - by
+security reasons.
+
+Now let's share field ``name``. First, setup the ``public_fields`` configuration
+option. Remember, that this action requires administrator's privileges and
+the next command will ask for password for user `admin`, assuming that he is
+the server administrator::
+
+  curl -X PUT http://localhost:5984/_config/couch_http_auth/public_fields \
+       -H "Content-Type: application/json" \
+       -d '"name"' \
+       -u admin
+
+What have changed? Let's check Robert's document once again::
+
+  curl http://localhost:5984/_users/org.couchdb.user:robert
+
+.. code-block:: javascript
+
+  {"_id":"org.couchdb.user:robert","_rev":"6-869e2d3cbd8b081f9419f190438ecbe7","name":"robert"}
+
+Good news! Now we may read field ``name`` from *every user's document without
+need to be an administrator*. That's important note: don't publish sensitive
+information, especially without user's acknowledge - they may not like such
+actions from your side.