Client-Side Enforcement of Server-Side Security vulnerability in the WebSocket connection handling of mtrudel bandit allows a remote client to keep running WebSock handler code after the server has stopped its connection, via data frames sent after ignoring the server's Close frame.
When a WebSock callback returns {:stop, ...}, Bandit.WebSocket.Connection calls terminate/2, sends Close and shuts down the write side, but keeps the connection process running in a :closing state. handle_frame/3 and handle_info/3 never check that state, so later frames still reach handle_in/2 and later messages reach handle_info/2, with the state from the stop tuple, until the client sends Close, TCP drops or the idle timeout fires. A client refused in init/1 after a failed token check could still run an admin-only action.
This issue affects bandit: from 0.5.1 before 1.14.0.
1. Stop without close. In Bandit.WebSocket.Connection, do_stop/4 calls terminate/2, sends the Close frame and runs shutdown(:write). It then returns {:continue, %{connection | state: :closing}}, so Bandit.WebSocket.Handler keeps the process alive.
2. No state check. handle_frame/3 and handle_info/3 never check connection.state. Every later data frame still reaches handle_in/2, and every later message still reaches handle_info/2, with the state from the stop tuple.
3. Client-controlled lifetime. The connection ends only when the client sends Close, TCP drops, or the idle timeout fires, and each client frame resets that timeout. All stops through do_stop are affected: {:stop, :normal, ...}, {:stop, {:shutdown, :disconnected}, ...} and {:stop, {:shutdown, :restart}, ...}, which is what Phoenix's :socket_drain returns. A banned user who was kicked kept posting for longer than the idle timeout.
init/1 returns {:stop, :normal, {4401, "unauthorized"}, state} for a wrong token, and whose handle_in/2 runs an admin-only delete_all_users action when user_id is admin.?user_id=admin&token=wrong. The server calls terminate/2 and sends a Close frame with code 4401.delete_all_users.handle_in/2 runs delete_all_users as admin.A remote client that ignores the Close frame can keep invoking a WebSock application's handlers after the application refused or ended its session, bypassing an authorization check in init/1 or a kick or ban. This affects WebSock applications that rely on {:stop, ...} to end a session.
Mark the WebSock state as stopped whenever a callback returns {:stop, ...}, and have handle_in/2 and handle_info/2 return {:ok, state} without acting while that mark is set.
{
"capec_ids": [
"CAPEC-202"
],
"cpe_ids": [
"cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"
],
"cwe_ids": [
"CWE-602"
]
}