You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2022/07/22 16:04:31 UTC

[couchdb] 01/01: Revert "Replace SHA-1 with SHA-256 for cookie authentication (#4094)"

This is an automated email from the ASF dual-hosted git repository.

rnewson pushed a commit to branch revert-4094-for-now
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 3527d304719c8597ecd7f74bc8459eb14e2d23f3
Author: Robert Newson <rn...@apache.org>
AuthorDate: Fri Jul 22 17:04:17 2022 +0100

    Revert "Replace SHA-1 with SHA-256 for cookie authentication (#4094)"
    
    This reverts commit daff65d8c8772c637eb60365d6e672543163a740.
---
 src/couch/src/couch_httpd_auth.erl  | 18 +++----
 test/elixir/test/config/skip.elixir |  4 ++
 test/elixir/test/proxyauth_test.exs | 99 ++++++++++++++++++++++++-------------
 3 files changed, 78 insertions(+), 43 deletions(-)

diff --git a/src/couch/src/couch_httpd_auth.erl b/src/couch/src/couch_httpd_auth.erl
index d4261ba9b..c74ca9bd8 100644
--- a/src/couch/src/couch_httpd_auth.erl
+++ b/src/couch/src/couch_httpd_auth.erl
@@ -153,14 +153,14 @@ null_authentication_handler(Req) ->
 % Headers  name can be defined in local.ini. By default they are :
 %
 %   * X-Auth-CouchDB-UserName : contain the username, (x_auth_username in
-%   cttpd_auth section)
+%   couch_httpd_auth section)
 %   * X-Auth-CouchDB-Roles : contain the user roles, list of roles separated by a
-%   comma (x_auth_roles in chttpd_auth section)
+%   comma (x_auth_roles in couch_httpd_auth section)
 %   * X-Auth-CouchDB-Token : token to authenticate the authorization (x_auth_token
-%   in chttpd_auth section). This token is an hmac-sha256 created from secret key
+%   in couch_httpd_auth section). This token is an hmac-sha1 created from secret key
 %   and username. The secret key should be the same in the client and couchdb node.
-%   Secret key is the secret key in chttpd_auth section of ini. This token is optional
-%   if value of proxy_use_secret key in chttpd_auth section of ini isn't true.
+%   Secret key is the secret key in couch_httpd_auth section of ini. This token is optional
+%   if value of proxy_use_secret key in couch_httpd_auth section of ini isn't true.
 %
 proxy_authentication_handler(Req) ->
     case proxy_auth_user(Req) of
@@ -202,7 +202,7 @@ proxy_auth_user(Req) ->
                             Req#httpd{user_ctx = #user_ctx{name = ?l2b(UserName), roles = Roles}};
                         Secret ->
                             ExpectedToken = couch_util:to_hex(
-                                couch_util:hmac(sha256, Secret, UserName)
+                                couch_util:hmac(sha, Secret, UserName)
                             ),
                             case header_value(Req, XHeaderToken) of
                                 Token when Token == ExpectedToken ->
@@ -308,9 +308,7 @@ cookie_authentication_handler(#httpd{mochi_req = MochiReq} = Req, AuthModule) ->
                         {ok, UserProps, _AuthCtx} ->
                             UserSalt = couch_util:get_value(<<"salt">>, UserProps, <<"">>),
                             FullSecret = <<Secret/binary, UserSalt/binary>>,
-                            ExpectedHash = couch_util:hmac(
-                                sha256, FullSecret, User ++ ":" ++ TimeStr
-                            ),
+                            ExpectedHash = couch_util:hmac(sha, FullSecret, User ++ ":" ++ TimeStr),
                             Hash = ?l2b(HashStr),
                             Timeout = chttpd_util:get_chttpd_auth_config_integer(
                                 "timeout", 600
@@ -369,7 +367,7 @@ cookie_auth_header(_Req, _Headers) ->
 
 cookie_auth_cookie(Req, User, Secret, TimeStamp) ->
     SessionData = User ++ ":" ++ erlang:integer_to_list(TimeStamp, 16),
-    Hash = couch_util:hmac(sha256, Secret, SessionData),
+    Hash = couch_util:hmac(sha, Secret, SessionData),
     mochiweb_cookies:cookie(
         "AuthSession",
         couch_util:encodeBase64Url(SessionData ++ ":" ++ ?b2l(Hash)),
diff --git a/test/elixir/test/config/skip.elixir b/test/elixir/test/config/skip.elixir
index 7fbcacef7..bb27f13cd 100644
--- a/test/elixir/test/config/skip.elixir
+++ b/test/elixir/test/config/skip.elixir
@@ -2,6 +2,10 @@
   "CookieAuthTest": [
     "cookie auth"
   ],
+  "ProxyAuthTest": [
+    "proxy auth with secret",
+    "proxy auth without secret"
+  ],
   "ReaderACLTest": [
     "unrestricted db can be read"
   ],
diff --git a/test/elixir/test/proxyauth_test.exs b/test/elixir/test/proxyauth_test.exs
index 6a4b58561..6bf21920b 100644
--- a/test/elixir/test/proxyauth_test.exs
+++ b/test/elixir/test/proxyauth_test.exs
@@ -4,7 +4,27 @@ defmodule ProxyAuthTest do
   @moduletag :authentication
 
   @tag :with_db
-  test "proxy auth with secret" do
+  test "proxy auth with secret", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      shows: %{
+        welcome: """
+           function(doc,req) {
+          return "Welcome " + req.userCtx["name"];
+        }
+        """,
+        role: """
+          function(doc, req) {
+          return req.userCtx['roles'][0];
+        }
+        """
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
 
     users_db_name = random_db_name()
     create_db(users_db_name)
@@ -18,19 +38,19 @@ defmodule ProxyAuthTest do
         :value => users_db_name
       },
       %{
-        :section => "chttpd_auth",
+        :section => "couch_httpd_auth",
         :key => "proxy_use_secret",
         :value => "true"
       },
       %{
-        :section => "chttpd_auth",
+        :section => "couch_httpd_auth",
         :key => "secret",
         :value => secret
       }
     ]
 
     run_on_modified_server(server_config, fn ->
-      test_fun(users_db_name, secret)
+      test_fun(db_name, users_db_name, secret)
     end)
     delete_db(users_db_name)
   end
@@ -42,12 +62,16 @@ defmodule ProxyAuthTest do
     |> Enum.join("")
   end
 
-  defp hex_hmac_sha256(secret, message) do
-    signature = :crypto.mac(:hmac, :sha256, secret, message)
+  defp hex_hmac_sha1(secret, message) do
+    signature = case :erlang.system_info(:otp_release) do
+      '20' -> :crypto.hmac(:sha, secret, message)
+      '21' -> :crypto.hmac(:sha, secret, message)
+      _ -> :crypto.mac(:hmac, :sha, secret, message)
+    end
     Base.encode16(signature, case: :lower)
   end
 
-  def test_fun(users_db_name, secret) do
+  def test_fun(db_name, users_db_name, secret) do
     user = prepare_user_doc(name: "couch@apache.org", password: "test")
     create_doc(users_db_name, user)
 
@@ -61,24 +85,38 @@ defmodule ProxyAuthTest do
 
     headers = [
       "X-Auth-CouchDB-UserName": "couch@apache.org",
-      "X-Auth-CouchDB-Roles": "test_role",
-      "X-Auth-CouchDB-Token": hex_hmac_sha256(secret, "couch@apache.org")
+      "X-Auth-CouchDB-Roles": "test",
+      "X-Auth-CouchDB-Token": hex_hmac_sha1(secret, "couch@apache.org")
     ]
+    resp = Couch.get("/#{db_name}/_design/test/_show/welcome", headers: headers)
+    assert resp.body == "Welcome couch@apache.org"
 
-    resp2 =
-      Couch.get("/_session",
-        headers: headers
-      )
-
-    assert resp2.body["userCtx"]["name"] == "couch@apache.org"
-    assert resp2.body["userCtx"]["roles"] == ["test_role"]
-    assert resp2.body["info"]["authenticated"] == "proxy"
-    assert resp2.body["ok"] == true
-
+    resp = Couch.get("/#{db_name}/_design/test/_show/role", headers: headers)
+    assert resp.body == "test"
   end
 
   @tag :with_db
-  test "proxy auth without secret" do
+  test "proxy auth without secret", context do
+    db_name = context[:db_name]
+
+    design_doc = %{
+      _id: "_design/test",
+      language: "javascript",
+      shows: %{
+        welcome: """
+           function(doc,req) {
+          return "Welcome " + req.userCtx["name"];
+        }
+        """,
+        role: """
+          function(doc, req) {
+          return req.userCtx['roles'][0];
+        }
+        """
+      }
+    }
+
+    {:ok, _} = create_doc(db_name, design_doc)
 
     users_db_name = random_db_name()
     create_db(users_db_name)
@@ -90,20 +128,20 @@ defmodule ProxyAuthTest do
         :value => users_db_name
       },
       %{
-        :section => "chttpd_auth",
+        :section => "couch_httpd_auth",
         :key => "proxy_use_secret",
         :value => "false"
       }
     ]
 
     run_on_modified_server(server_config, fn ->
-      test_fun_no_secret(users_db_name)
+      test_fun_no_secret(db_name, users_db_name)
     end)
 
     delete_db(users_db_name)
   end
 
-  def test_fun_no_secret(users_db_name) do
+  def test_fun_no_secret(db_name, users_db_name) do
     user = prepare_user_doc(name: "couch@apache.org", password: "test")
     create_doc(users_db_name, user)
 
@@ -117,18 +155,13 @@ defmodule ProxyAuthTest do
 
     headers = [
       "X-Auth-CouchDB-UserName": "couch@apache.org",
-      "X-Auth-CouchDB-Roles": "test_role_1,test_role_2"
+      "X-Auth-CouchDB-Roles": "test"
     ]
 
-    resp2 =
-      Couch.get("/_session",
-        headers: headers
-      )
-
-    assert resp2.body["userCtx"]["name"] == "couch@apache.org"
-    assert resp2.body["userCtx"]["roles"] == ["test_role_1", "test_role_2"]
-    assert resp2.body["info"]["authenticated"] == "proxy"
-    assert resp2.body["ok"] == true
+    resp = Couch.get("/#{db_name}/_design/test/_show/welcome", headers: headers)
+    assert resp.body == "Welcome couch@apache.org"
 
+    resp = Couch.get("/#{db_name}/_design/test/_show/role", headers: headers)
+    assert resp.body == "test"
   end
 end