Merge pull request #9839 from kjellwinblad/kjell/fix/Authorization_header_log_leak_webhook/EMQX-8791

fix: Authorization header leak in log entries for webhook
This commit is contained in:
Kjell Winblad 2023-02-02 10:38:42 +01:00 committed by GitHub
commit 2cf193e2fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 82 additions and 8 deletions

View File

@ -609,7 +609,11 @@ do_redact(K, V, Checker) ->
-define(REDACT_VAL, "******"). -define(REDACT_VAL, "******").
redact_v(V) when is_binary(V) -> <<?REDACT_VAL>>; redact_v(V) when is_binary(V) -> <<?REDACT_VAL>>;
redact_v(_V) -> ?REDACT_VAL. %% The HOCON schema system may generate sensitive values with this format
redact_v([{str, Bin}]) when is_binary(Bin) ->
[{str, <<?REDACT_VAL>>}];
redact_v(_V) ->
?REDACT_VAL.
is_redacted(K, V) -> is_redacted(K, V) ->
do_is_redacted(K, V, fun is_sensitive_key/1). do_is_redacted(K, V, fun is_sensitive_key/1).

View File

@ -209,7 +209,7 @@ on_start(
?SLOG(info, #{ ?SLOG(info, #{
msg => "starting_http_connector", msg => "starting_http_connector",
connector => InstId, connector => InstId,
config => emqx_misc:redact(Config) config => redact(Config)
}), }),
{Transport, TransportOpts} = {Transport, TransportOpts} =
case Scheme of case Scheme of
@ -289,7 +289,11 @@ on_query(
?TRACE( ?TRACE(
"QUERY", "QUERY",
"http_connector_received", "http_connector_received",
#{request => Request, connector => InstId, state => State} #{
request => redact(Request),
connector => InstId,
state => redact(State)
}
), ),
NRequest = formalize_request(Method, BasePath, Request), NRequest = formalize_request(Method, BasePath, Request),
Worker = resolve_pool_worker(State, KeyOrNum), Worker = resolve_pool_worker(State, KeyOrNum),
@ -312,7 +316,7 @@ on_query(
{error, Reason} = Result -> {error, Reason} = Result ->
?SLOG(error, #{ ?SLOG(error, #{
msg => "http_connector_do_request_failed", msg => "http_connector_do_request_failed",
request => NRequest, request => redact(NRequest),
reason => Reason, reason => Reason,
connector => InstId connector => InstId
}), }),
@ -324,7 +328,7 @@ on_query(
{ok, StatusCode, Headers} -> {ok, StatusCode, Headers} ->
?SLOG(error, #{ ?SLOG(error, #{
msg => "http connector do request, received error response", msg => "http connector do request, received error response",
request => NRequest, request => redact(NRequest),
connector => InstId, connector => InstId,
status_code => StatusCode status_code => StatusCode
}), }),
@ -332,7 +336,7 @@ on_query(
{ok, StatusCode, Headers, Body} -> {ok, StatusCode, Headers, Body} ->
?SLOG(error, #{ ?SLOG(error, #{
msg => "http connector do request, received error response", msg => "http connector do request, received error response",
request => NRequest, request => redact(NRequest),
connector => InstId, connector => InstId,
status_code => StatusCode status_code => StatusCode
}), }),
@ -369,7 +373,11 @@ on_query_async(
?TRACE( ?TRACE(
"QUERY_ASYNC", "QUERY_ASYNC",
"http_connector_received", "http_connector_received",
#{request => Request, connector => InstId, state => State} #{
request => redact(Request),
connector => InstId,
state => redact(State)
}
), ),
NRequest = formalize_request(Method, BasePath, Request), NRequest = formalize_request(Method, BasePath, Request),
ok = ehttpc:request_async( ok = ehttpc:request_async(
@ -409,7 +417,7 @@ do_get_status(PoolName, Timeout) ->
{error, Reason} = Error -> {error, Reason} = Error ->
?SLOG(error, #{ ?SLOG(error, #{
msg => "http_connector_get_status_failed", msg => "http_connector_get_status_failed",
reason => Reason, reason => redact(Reason),
worker => Worker worker => Worker
}), }),
Error Error
@ -562,3 +570,63 @@ reply_delegator(ReplyFunAndArgs, Result) ->
_ -> _ ->
emqx_resource:apply_reply_fun(ReplyFunAndArgs, Result) emqx_resource:apply_reply_fun(ReplyFunAndArgs, Result)
end. end.
%% The HOCON schema system may generate sensitive keys with this format
is_sensitive_key([{str, StringKey}]) ->
is_sensitive_key(StringKey);
is_sensitive_key(Atom) when is_atom(Atom) ->
is_sensitive_key(erlang:atom_to_binary(Atom));
is_sensitive_key(Bin) when is_binary(Bin), (size(Bin) =:= 19 orelse size(Bin) =:= 13) ->
try
%% This is wrapped in a try-catch since we don't know that Bin is a
%% valid string so string:lowercase/1 might throw an exception.
%%
%% We want to convert this to lowercase since the http header fields
%% are case insensitive, which means that a user of the Webhook bridge
%% can write this field name in many different ways.
LowercaseBin = iolist_to_binary(string:lowercase(Bin)),
case LowercaseBin of
<<"authorization">> -> true;
<<"proxy-authorization">> -> true;
_ -> false
end
catch
_:_ -> false
end;
is_sensitive_key(_) ->
false.
%% Function that will do a deep traversal of Data and remove sensitive
%% information (i.e., passwords)
redact(Data) ->
emqx_misc:redact(Data, fun is_sensitive_key/1).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
redact_test_() ->
TestData1 = [
{<<"content-type">>, <<"application/json">>},
{<<"Authorization">>, <<"Basic YWxhZGRpbjpvcGVuc2VzYW1l">>}
],
TestData2 = #{
headers =>
[
{[{str, <<"content-type">>}], [{str, <<"application/json">>}]},
{[{str, <<"Authorization">>}], [{str, <<"Basic YWxhZGRpbjpvcGVuc2VzYW1l">>}]}
]
},
[
?_assert(is_sensitive_key(<<"Authorization">>)),
?_assert(is_sensitive_key(<<"AuthoriZation">>)),
?_assert(is_sensitive_key('AuthoriZation')),
?_assert(is_sensitive_key(<<"PrOxy-authoRizaTion">>)),
?_assert(is_sensitive_key('PrOxy-authoRizaTion')),
?_assertNot(is_sensitive_key(<<"Something">>)),
?_assertNot(is_sensitive_key(89)),
?_assertNotEqual(TestData1, redact(TestData1)),
?_assertNotEqual(TestData2, redact(TestData2))
].
-endif.

View File

@ -0,0 +1 @@
Make sure that the content of an Authorization header that users have specified for a webhook bridge is not printed to log files.

View File

@ -0,0 +1 @@
确保用户为webhook-bridge指定的Authorization-HTTP-header的内容不会被打印到日志文件。