Просмотр исходного кода

feat: sha3_256 dashboard account's password (#6084)

zhongwencool 4 лет назад
Родитель
Сommit
eea789451b

+ 5 - 5
apps/emqx_dashboard/include/emqx_dashboard.hrl

@@ -13,8 +13,9 @@
 %% See the License for the specific language governing permissions and
 %% limitations under the License.
 %%--------------------------------------------------------------------
+-define(ADMIN, emqx_admin).
 
--record(emqx_admin, {
+-record(?ADMIN, {
     username         :: binary(),
     pwdhash          :: binary(),
     tags             :: list() | binary(),
@@ -22,17 +23,16 @@
     extra = []       :: term() %% not used so far, for future extension
     }).
 
--define(ADMIN, emqx_admin).
 
--record(emqx_admin_jwt, {
+-define(ADMIN_JWT, emqx_admin_jwt).
+
+-record(?ADMIN_JWT, {
     token      :: binary(),
     username   :: binary(),
     exptime    :: integer(),
     extra = [] :: term() %% not used so far, fur future extension
     }).
 
--define(ADMIN_JWT, emqx_admin_jwt).
-
 -define(EMPTY_KEY(Key), ((Key == undefined) orelse (Key == <<>>))).
 
 -define(DASHBOARD_SHARD, emqx_dashboard_shard).

+ 5 - 10
apps/emqx_dashboard/src/emqx_dashboard_admin.erl

@@ -167,7 +167,7 @@ check(_, undefined) ->
 check(Username, Password) ->
     case lookup_user(Username) of
         [#?ADMIN{pwdhash = <<Salt:4/binary, Hash/binary>>}] ->
-            case Hash =:= md5_hash(Salt, Password) of
+            case Hash =:= sha3_hash(Salt, Password) of
                 true  -> ok;
                 false -> {error, <<"BAD_USERNAME_OR_PASSWORD">>}
             end;
@@ -201,16 +201,11 @@ destroy_token_by_username(Username, Token) ->
 %%--------------------------------------------------------------------
 
 hash(Password) ->
-    SaltBin = salt(),
-    <<SaltBin/binary, (md5_hash(SaltBin, Password))/binary>>.
+    SaltBin = emqx_dashboard_token:salt(),
+    <<SaltBin/binary, (sha3_hash(SaltBin, Password))/binary>>.
 
-md5_hash(SaltBin, Password) ->
-    erlang:md5(<<SaltBin/binary, Password/binary>>).
-
-salt() ->
-    _ = emqx_misc:rand_seed(),
-    Salt = rand:uniform(16#ffffffff),
-    <<Salt:32>>.
+sha3_hash(SaltBin, Password) ->
+    crypto:hash('sha3_256', <<SaltBin/binary, Password/binary>>).
 
 add_default_user() ->
     add_default_user(binenv(default_username), binenv(default_password)).

+ 11 - 8
apps/emqx_dashboard/src/emqx_dashboard_token.erl

@@ -40,7 +40,7 @@
 %% gen server part
 -behaviour(gen_server).
 
--export([start_link/0]).
+-export([start_link/0, salt/0]).
 
 -export([ init/1
         , handle_call/3
@@ -75,6 +75,12 @@ destroy(Token) when is_binary(Token)->
 destroy_by_username(Username) ->
     do_destroy_by_username(Username).
 
+%% @doc create 4 bytes salt.
+-spec(salt() -> binary()).
+salt() ->
+    <<X:16/big-unsigned-integer>> = crypto:strong_rand_bytes(2),
+    iolist_to_binary(io_lib:format("~4.16.0b", [X])).
+
 mnesia(boot) ->
     ok = mria:create_table(?TAB, [
                 {type, set},
@@ -110,7 +116,9 @@ do_verify(Token)->
             case ExpTime > erlang:system_time(millisecond) of
                 true ->
                     NewJWT = JWT#?ADMIN_JWT{exptime = jwt_expiration_time()},
-                    {atomic, Res} = mria:transaction(?DASHBOARD_SHARD, fun mnesia:write/1, [NewJWT]),
+                    {atomic, Res} = mria:transaction(?DASHBOARD_SHARD,
+                                                     fun mnesia:write/1,
+                                                     [NewJWT]),
                     Res;
                 _ ->
                     {error, token_timeout}
@@ -145,7 +153,7 @@ lookup_by_username(Username) ->
     List.
 
 jwk(Username, Password, Salt) ->
-    Key = erlang:md5(<<Salt/binary, Username/binary, Password/binary>>),
+    Key = crypto:hash(md5, <<Salt/binary, Username/binary, Password/binary>>),
     #{
         <<"kty">> => <<"oct">>,
         <<"k">> => jose_base64url:encode(Key)
@@ -157,11 +165,6 @@ jwt_expiration_time() ->
 token_ttl() ->
     emqx_conf:get([emqx_dashboard, token_expired_time], ?EXPTIME).
 
-salt() ->
-    _ = emqx_misc:rand_seed(),
-    Salt = rand:uniform(16#ffffffff),
-    <<Salt:32>>.
-
 format(Token, Username, ExpTime) ->
     #?ADMIN_JWT{
         token    = Token,

+ 39 - 16
apps/emqx_dashboard/test/emqx_dashboard_SUITE.erl

@@ -37,7 +37,18 @@
 
 -define(BASE_PATH, "api").
 
--define(OVERVIEWS, ['alarms/activated', 'alarms/deactivated', banned, brokers, stats, metrics, listeners, clients, subscriptions, routes, plugins]).
+-define(OVERVIEWS, ['alarms/activated',
+                    'alarms/deactivated',
+                    banned,
+                    brokers,
+                    stats,
+                    metrics,
+                    listeners,
+                    clients,
+                    subscriptions,
+                    routes,
+                    plugins
+                   ]).
 
 all() ->
 %%    TODO: V5 API
@@ -45,7 +56,8 @@ all() ->
     [t_cli, t_lookup_by_username_jwt, t_clean_expired_jwt].
 
 init_per_suite(Config) ->
-    emqx_common_test_helpers:start_apps([emqx_management, emqx_dashboard],fun set_special_configs/1),
+    emqx_common_test_helpers:start_apps([emqx_management, emqx_dashboard],
+        fun set_special_configs/1),
     Config.
 
 end_per_suite(_Config) ->
@@ -53,7 +65,8 @@ end_per_suite(_Config) ->
     mria:stop().
 
 set_special_configs(emqx_management) ->
-    emqx_config:put([emqx_management], #{listeners => [#{protocol => http, port => 8081}],
+    Listeners = [#{protocol => http, port => 8081}],
+    emqx_config:put([emqx_management], #{listeners => Listeners,
         applications =>[#{id => "admin", secret => "public"}]}),
     ok;
 set_special_configs(_) ->
@@ -62,27 +75,33 @@ set_special_configs(_) ->
 t_overview(_) ->
     mnesia:clear_table(?ADMIN),
     emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, <<"tag">>),
-    [?assert(request_dashboard(get, api_path(erlang:atom_to_list(Overview)), auth_header_()))|| Overview <- ?OVERVIEWS].
+    [?assert(request_dashboard(get, api_path(erlang:atom_to_list(Overview)),
+        auth_header_()))|| Overview <- ?OVERVIEWS].
 
 t_admins_add_delete(_) ->
     mnesia:clear_table(?ADMIN),
-    ok = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, <<"tag">>),
-    ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, <<"tag1">>),
+    Tag = <<"tag">>,
+    ok = emqx_dashboard_admin:add_user(<<"username">>, <<"password">>, Tag),
+    ok = emqx_dashboard_admin:add_user(<<"username1">>, <<"password1">>, Tag),
     Admins = emqx_dashboard_admin:all_users(),
     ?assertEqual(2, length(Admins)),
     ok = emqx_dashboard_admin:remove_user(<<"username1">>),
     Users = emqx_dashboard_admin:all_users(),
     ?assertEqual(1, length(Users)),
-    ok = emqx_dashboard_admin:change_password(<<"username">>, <<"password">>, <<"pwd">>),
+    ok = emqx_dashboard_admin:change_password(<<"username">>,
+                                              <<"password">>,
+                                              <<"pwd">>),
     timer:sleep(10),
-    ?assert(request_dashboard(get, api_path("brokers"), auth_header_("username", "pwd"))),
+    Header = auth_header_("username", "pwd"),
+    ?assert(request_dashboard(get, api_path("brokers"), Header)),
 
     ok = emqx_dashboard_admin:remove_user(<<"username">>),
-    ?assertNotEqual(true, request_dashboard(get, api_path("brokers"), auth_header_("username", "pwd"))).
+    ?assertNotEqual(true, request_dashboard(get, api_path("brokers"), Header)).
 
 t_rest_api(_Config) ->
     mnesia:clear_table(?ADMIN),
-    emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, <<"administrator">>),
+    Tag = <<"administrator">>,
+    emqx_dashboard_admin:add_user(<<"admin">>, <<"public">>, Tag),
     {ok, Res0} = http_get("users"),
 
     ?assertEqual([#{<<"username">> => <<"admin">>,
@@ -93,11 +112,15 @@ t_rest_api(_Config) ->
                     end,
     [AssertSuccess(R)
      || R <- [ http_put("users/admin", #{<<"tags">> => <<"a_new_tag">>})
-             , http_post("users", #{<<"username">> => <<"usera">>, <<"password">> => <<"passwd">>})
-             , http_post("auth", #{<<"username">> => <<"usera">>, <<"password">> => <<"passwd">>})
+             , http_post("users", #{<<"username">> => <<"usera">>,
+                                    <<"password">> => <<"passwd">>})
+             , http_post("auth", #{<<"username">> => <<"usera">>,
+                                   <<"password">> => <<"passwd">>})
              , http_delete("users/usera")
-             , http_put("users/admin/change_pwd", #{<<"old_pwd">> => <<"public">>, <<"new_pwd">> => <<"newpwd">>})
-             , http_post("auth", #{<<"username">> => <<"admin">>, <<"password">> => <<"newpwd">>})
+             , http_put("users/admin/change_pwd", #{<<"old_pwd">> => <<"public">>,
+                                                    <<"new_pwd">> => <<"newpwd">>})
+             , http_post("auth", #{<<"username">> => <<"admin">>,
+                                   <<"password">> => <<"newpwd">>})
              ]],
     ok.
 
@@ -106,11 +129,11 @@ t_cli(_Config) ->
     emqx_dashboard_cli:admins(["add", "username", "password"]),
     [#?ADMIN{ username = <<"username">>, pwdhash = <<Salt:4/binary, Hash/binary>>}] =
         emqx_dashboard_admin:lookup_user(<<"username">>),
-    ?assertEqual(Hash, erlang:md5(<<Salt/binary, <<"password">>/binary>>)),
+    ?assertEqual(Hash, crypto:hash(sha3_256, <<Salt/binary, <<"password">>/binary>>)),
     emqx_dashboard_cli:admins(["passwd", "username", "newpassword"]),
     [#?ADMIN{username = <<"username">>, pwdhash = <<Salt1:4/binary, Hash1/binary>>}] =
         emqx_dashboard_admin:lookup_user(<<"username">>),
-    ?assertEqual(Hash1, erlang:md5(<<Salt1/binary, <<"newpassword">>/binary>>)),
+    ?assertEqual(Hash1, crypto:hash(sha3_256, <<Salt1/binary, <<"newpassword">>/binary>>)),
     emqx_dashboard_cli:admins(["del", "username"]),
     [] = emqx_dashboard_admin:lookup_user(<<"username">>),
     emqx_dashboard_cli:admins(["add", "admin1", "pass1"]),