This is an automated email from the ASF dual-hosted git repository. rnewson pushed a commit to branch decouple_offline_hash_strength_from_online in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 139cf454cf0791a52cd2113748ab5b61c317108f Author: Robert Newson <[email protected]> AuthorDate: Thu Oct 19 15:18:48 2023 +0100 in-memory password hash cache --- rel/overlay/etc/default.ini | 9 +++++ src/couch/src/couch_passwords_cache.erl | 62 +++++++++++++++++++++++++++++++++ src/couch/src/couch_primary_sup.erl | 14 +++++++- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini index 1604f1585..9a9cc20f6 100644 --- a/rel/overlay/etc/default.ini +++ b/rel/overlay/etc/default.ini @@ -913,3 +913,12 @@ enable = {{with_nouveau}} ;background_view_indexing_threshold = 80 ;interactive_view_indexing_threshold = 90 ;interactive_database_writes_threshold = 90 + +; To speed authentication on database requests when on-disk iteration count is +; high, an in-memory cache of password hashes with a lower iteration threshold +; is maintained. +; If you exclusively use authentication methods other than basic authentication +; (e.g, session cookies or proxy authentication) you might wish to disable this +; to avoid the slight per-request cost of this hashing. +[couch_passwords_cache] +;enable = true \ No newline at end of file diff --git a/src/couch/src/couch_passwords_cache.erl b/src/couch/src/couch_passwords_cache.erl new file mode 100644 index 000000000..4ee91439c --- /dev/null +++ b/src/couch/src/couch_passwords_cache.erl @@ -0,0 +1,62 @@ +% 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. +% +% password hashes on disk can take a long time to verify. This is by design, to +% guard against offline attacks. This module adds an in-memory cache for password +% verification to speed up verification after a successful slow verification from +% the hash stored on disk, in order not to transfer the deliberate offline attack +% protection to database requests. +% +% In memory we record a PBKDF_SHA256 derivation using a low number of iterations +% and check against this if present. Entries in couch_passwords_cache expire automatically +% and the maximum number of cached entries is configurable. + +-module(couch_passwords_cache). + +-define(CACHE, couch_passwords_cache_lru). + +-export([start_link/0]). + +% public api +-export([authenticate/3, insert/3]). + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +% public functions +-spec authenticate(AuthModule :: atom(), UserName :: binary(), Password :: binary()) -> + not_found | boolean(). +authenticate(AuthModule, UserName, Password) -> + case config:get_boolean("couch_passwords_cache", "enable", true) of + true -> + authenticate_int(AuthModule, UserName, Password); + false -> + not_found + end. + +authenticate_int(AuthModule, UserName, Password) -> + case ets_lru:lookup_d(?CACHE, {AuthModule, UserName}) of + not_found -> + not_found; + {ok, {Salt, Expected}} -> + Actual = hash(Password, Salt), + couch_passwords:verify(Expected, Actual) + end. + +-spec insert(AuthModule :: atom(), UserName :: binary(), Password :: binary()) -> ok. +insert(AuthModule, UserName, Password) -> + Salt = couch_uuids:random(), + DerivedKey = hash(Password, Salt), + ets_lru:insert(?CACHE, {AuthModule, UserName}, {Salt, DerivedKey}). + +hash(Password, Salt) -> + fast_pbkdf2:pbkdf2(sha256, Password, Salt, _Iterations = 5, _KeyLen = 32). diff --git a/src/couch/src/couch_primary_sup.erl b/src/couch/src/couch_primary_sup.erl index 1eae87160..cfb460bf8 100644 --- a/src/couch/src/couch_primary_sup.erl +++ b/src/couch/src/couch_primary_sup.erl @@ -23,7 +23,19 @@ init([]) -> {couch_task_status, {couch_task_status, start_link, []}, permanent, brutal_kill, worker, [couch_task_status]}, {couch_password_hasher, {couch_password_hasher, start_link, []}, permanent, brutal_kill, - worker, [couch_password_hasher]} + worker, [couch_password_hasher]}, + {couch_passwords_cache_lru, + {ets_lru, start_link, [ + couch_passwords_cache_lru, + + [ + {max_objects, + config:get_integer("couch_passwords_cache", "max_objects", 10_000)}, + {max_lifetime, + config:get_integer("couch_passwords_cache", "max_lifetime", 360_000)} + ] + ]}, + permanent, 5000, worker, [ets_lru]} ] ++ couch_servers(), {ok, {{one_for_one, 10, 3600}, Children}}.
