Fix to restrict Basic auth for websocket routes

ⓘ Require websocket routes to opt in with allow_basic_ws before Basic auth is accepted in session mode. Mark linked ws-link routes and no-cookie backend-session routes as allowed, while leaving normal session-backed routes unmarked.
This commit is contained in:
Ilia Ross
2026-06-23 01:09:43 +02:00
parent 6091f08e37
commit c72d232e2f
4 changed files with 17 additions and 7 deletions

View File

@@ -1512,6 +1512,7 @@ alarm(0);
my $websocket_upgrade_request = lc($header{'connection'}) =~ /upgrade/ &&
lc($header{'upgrade'}) eq 'websocket';
my $websocket_configured_request;
my $websocket_basic_auth_ok;
if ($websocket_upgrade_request) {
# Check the configured websocket paths before auth, so Basic auth can
# remain disabled for normal session-mode requests.
@@ -1519,6 +1520,12 @@ if ($websocket_upgrade_request) {
my $ws_simple = &simplify_path($page, $wsbogus);
$websocket_configured_request = !$wsbogus &&
&find_websocket_config($ws_simple);
if ($websocket_configured_request) {
# Session-mode Basic auth stays disabled unless the websocket
# route explicitly allows a no-cookie Basic-auth hop.
$websocket_basic_auth_ok =
$websocket_configured_request->{'allow_basic_ws'};
}
}
# If a remote IP is given in a header (such as via a proxy), only use it
@@ -1840,11 +1847,11 @@ if (!$validated && !$deny_authentication) {
}
}
# Keep Basic auth disabled in session mode except for configured websocket
# proxy paths. Linked-server websocket hops need it, and token/user checks
# still run before the backend connection is opened.
# Keep Basic auth disabled in session mode except for websocket routes that
# explicitly allow no-cookie Basic-auth hops. Token/user checks still run
# before the backend connection is opened.
if (!$validated && !$deny_authentication &&
(!$config{'session'} || $websocket_configured_request) &&
(!$config{'session'} || $websocket_basic_auth_ok) &&
$header{authorization} =~ /^basic\s+(\S+)$/i) {
# authorization given..
($authuser, $authpass) = split(/:/, &b64decode($1), 2);

View File

@@ -312,7 +312,7 @@ my %miniserv;
$miniserv{"websockets_$wspath"} =
"host=$backend_host port=$port ssl=$ssl wspath=$remote_path ".
"hostheader=$hostheader origin=$origin auth=basic:$auth ".
"checkssl=$checkssl nokey=1 user=$main::remote_user ".
"checkssl=$checkssl nokey=1 allow_basic_ws=1 user=$main::remote_user ".
"token=$token time=$now";
&put_miniserv_config(\%miniserv);
&unlock_file(&get_miniserv_config_file());

View File

@@ -1311,7 +1311,7 @@ EOF
subtest 'parse_websockets_config' => sub {
no warnings 'once';
local %miniserv::config = (
'websockets_/chat' => 'host=back.example.com port=9000 proto=ws ssl=1 checkssl=1 backend_session=abc123',
'websockets_/chat' => 'host=back.example.com port=9000 proto=ws ssl=1 checkssl=1 backend_session=abc123 allow_basic_ws=1',
'unrelated_key' => 'ignored',
);
local @miniserv::websocket_paths = ();
@@ -1326,6 +1326,7 @@ subtest 'parse_websockets_config' => sub {
is($ws->{ssl}, '1', 'ssl kv parsed');
is($ws->{checkssl}, '1', 'SSL check kv parsed');
is($ws->{backend_session}, 'abc123', 'backend session kv parsed');
is($ws->{allow_basic_ws}, '1', 'Basic auth websocket flag parsed');
};
# find_websocket_config

View File

@@ -14331,8 +14331,10 @@ my $opt_backend = "";
# cookie. Store the one-time backend session key that the child server expects.
$opt_backend = " backend_session=$backend_session"
if (defined($backend_session) && $backend_session =~ /^\S+$/);
my $opt_basic = "";
$opt_basic = " allow_basic_ws=1" if ($opt_backend);
$miniserv{"websockets_$wspath"} = "host=127.0.0.1 port=$port wspath=/ ".
"user=$remote_user$opt_buser$opt_backend token=$token time=$now";
"user=$remote_user$opt_buser$opt_backend$opt_basic token=$token time=$now";
&put_miniserv_config(\%miniserv);
&unlock_file(&get_miniserv_config_file());
&reload_miniserv();