diff --git a/changelogs/unreleased/ghs_16_user_enumeration.md b/changelogs/unreleased/ghs_16_user_enumeration.md new file mode 100644 index 0000000000000000000000000000000000000000..f22ebf08a2346e63d4fca1371add63e5ae49b785 --- /dev/null +++ b/changelogs/unreleased/ghs_16_user_enumeration.md @@ -0,0 +1,4 @@ +## bugfix/core + +* Now the same error is returned when invalid password or login is entered + during authorization to prevent user enumeration (ghs-16). diff --git a/src/box/applier.cc b/src/box/applier.cc index 053e4d4ded055d8424e4d02be591367674c97b14..de5ffd695171466d035944c072d39981b3d56fec 100644 --- a/src/box/applier.cc +++ b/src/box/applier.cc @@ -123,7 +123,7 @@ applier_log_error(struct applier *applier, struct error *e) case ER_SYSTEM: case ER_SSL: case ER_UNKNOWN_REPLICA: - case ER_PASSWORD_MISMATCH: + case ER_CREDS_MISMATCH: case ER_XLOG_GAP: case ER_TOO_EARLY_SUBSCRIBE: case ER_SYNC_QUORUM_TIMEOUT: @@ -2203,7 +2203,7 @@ applier_f(va_list ap) } else if (e->errcode() == ER_CFG || e->errcode() == ER_ACCESS_DENIED || e->errcode() == ER_NO_SUCH_USER || - e->errcode() == ER_PASSWORD_MISMATCH) { + e->errcode() == ER_CREDS_MISMATCH) { /* Invalid configuration */ applier_log_error(applier, e); applier_disconnect(applier, APPLIER_LOADING); diff --git a/src/box/authentication.cc b/src/box/authentication.cc index 2c8109f1f50b9ec43c07d56aa4effc677058bb1c..c74fd91fb080ac76fa3d67c7fe308da955e3c903 100644 --- a/src/box/authentication.cc +++ b/src/box/authentication.cc @@ -41,7 +41,14 @@ void authenticate(const char *user_name, uint32_t len, const char *salt, const char *tuple) { - struct user *user = user_find_by_name_xc(user_name, len); + struct user *user = user_find_by_name(user_name, len); + if (user == NULL) { + if (diag_get()->last->code == ER_NO_SUCH_USER) { + diag_clear(diag_get()); + diag_set(ClientError, ER_CREDS_MISMATCH); + } + diag_raise(); + } struct session *session = current_session(); uint32_t part_count; uint32_t scramble_len; @@ -93,7 +100,7 @@ authenticate(const char *user_name, uint32_t len, const char *salt, auth_res.is_authenticated = false; if (session_run_on_auth_triggers(&auth_res) != 0) diag_raise(); - tnt_raise(ClientError, ER_PASSWORD_MISMATCH, user->def->name); + tnt_raise(ClientError, ER_CREDS_MISMATCH); } ok: /* check and run auth triggers on success */ diff --git a/src/box/errcode.h b/src/box/errcode.h index 4807745830d4aab32960db19c443408558dd4f57..c88892f485c5c029b49290cebae1f463434c3282 100644 --- a/src/box/errcode.h +++ b/src/box/errcode.h @@ -99,7 +99,7 @@ struct errcode_record { /* 44 */_(ER_DROP_USER, "Failed to drop user or role '%s': %s") \ /* 45 */_(ER_NO_SUCH_USER, "User '%s' is not found") \ /* 46 */_(ER_USER_EXISTS, "User '%s' already exists") \ - /* 47 */_(ER_PASSWORD_MISMATCH, "Incorrect password supplied for user '%s'") \ + /* 47 */_(ER_CREDS_MISMATCH, "User not found or supplied credentials are invalid") \ /* 48 */_(ER_UNKNOWN_REQUEST_TYPE, "Unknown request type %u") \ /* 49 */_(ER_UNKNOWN_SCHEMA_OBJECT, "Unknown object type '%s'") \ /* 50 */_(ER_CREATE_FUNCTION, "Failed to create function '%s': %s") \ diff --git a/test/box-luatest/ghs_16_user_enumeration_test.lua b/test/box-luatest/ghs_16_user_enumeration_test.lua new file mode 100644 index 0000000000000000000000000000000000000000..b822ea58854173cd14500f10b069c6f13085cdc7 --- /dev/null +++ b/test/box-luatest/ghs_16_user_enumeration_test.lua @@ -0,0 +1,33 @@ +local net = require('net.box') +local server = require('test.luatest_helpers.server') +local urilib = require('uri') +local t = require('luatest') +local g = t.group() + +g.before_all = function() + g.server = server:new({alias = 'master'}) + g.server:start() + g.server:exec(function() + box.schema.user.create('test', {password = '1111'}) + end) +end + +g.after_all = function() + g.server:stop() +end + +-- If we raise different errors in case of entering an invalid password and +-- entering the login of a non-existent user during authorization, it will +-- open the door for an unauthorized person to enumerate users. +-- So raised errors must be the same in the cases described above. +g.test_user_enum_on_auth = function() + local uri = urilib.parse(g.server.net_box_uri) + local err_msg = 'User not found or supplied credentials are invalid' + local cmd = 'return box.session.info()' + local c = net.connect('test:1112@' .. uri.unix) + t.assert_error_msg_contains(err_msg, c.eval , c, cmd) + c:close() + c = net.connect('nobody:1112@' .. uri.unix) + t.assert_error_msg_contains(err_msg, c.eval , c, cmd) + c:close() +end diff --git a/test/box/error.result b/test/box/error.result index ba7bc0b5b8b23cd12bdfa091713060b248a24525..d162cfd26440a509d34462212f4a16241fc95cd8 100644 --- a/test/box/error.result +++ b/test/box/error.result @@ -267,7 +267,7 @@ t; | 44: box.error.DROP_USER | 45: box.error.NO_SUCH_USER | 46: box.error.USER_EXISTS - | 47: box.error.PASSWORD_MISMATCH + | 47: box.error.CREDS_MISMATCH | 48: box.error.UNKNOWN_REQUEST_TYPE | 49: box.error.UNKNOWN_SCHEMA_OBJECT | 50: box.error.CREATE_FUNCTION diff --git a/test/box/net.box_incorrect_iterator_gh-841.result b/test/box/net.box_incorrect_iterator_gh-841.result index 498d8b691a42f3995db2c2474f5d17e0454be98e..43e42cac65deb629091a04bca16a87b91878b074 100644 --- a/test/box/net.box_incorrect_iterator_gh-841.result +++ b/test/box/net.box_incorrect_iterator_gh-841.result @@ -492,7 +492,7 @@ cn:is_connected() ... cn.error --- -- User 'netbox' is not found +- User not found or supplied credentials are invalid ... cn.state --- diff --git a/test/box/net.box_uri_first_arg_gh-398.result b/test/box/net.box_uri_first_arg_gh-398.result index 2caf8f7987501480f9d861e2cea88ca19a010ea6..bbf651dbf94f9c5c3d5866979872d65c1b43146a 100644 --- a/test/box/net.box_uri_first_arg_gh-398.result +++ b/test/box/net.box_uri_first_arg_gh-398.result @@ -31,7 +31,7 @@ cn ~= nil, cn.state, cn.error --- - true - error -- Incorrect password supplied for user 'netbox' +- User not found or supplied credentials are invalid ... cn:close() ---