(ws) Implement simple header based websocket authorization technique to reject

This commit is contained in:
Benjamin Sergeant 2020-12-17 22:42:14 -08:00
parent 93ad709dfd
commit 461a645704
5 changed files with 93 additions and 8 deletions

View File

@ -2,6 +2,12 @@
All changes to this project will be documented in this file. All changes to this project will be documented in this file.
## [11.0.5] - 2020-12-17
(ws) Implement simple header based websocket authorization technique to reject
client which do not supply a certain header ("Authorization") with a special
value (see doc).
## [11.0.4] - 2020-11-16 ## [11.0.4] - 2020-11-16
(ixwebsocket) Handle EINTR return code in ix::poll and IXSelectInterrupt (ixwebsocket) Handle EINTR return code in ix::poll and IXSelectInterrupt

View File

@ -195,6 +195,63 @@ Server: Python/3.7 websockets/8.0.2
Upgrade: websocket Upgrade: websocket
``` ```
It is possible to pass custom HTTP header when doing the connection handshake,
the remote server might process them to implement a simple authorization
scheme.
```
src$ ws connect -H Authorization:supersecret ws://localhost:8008
Type Ctrl-D to exit prompt...
[2020-12-17 22:35:08.732] [info] Authorization: supersecret
Connecting to url: ws://localhost:8008
> [2020-12-17 22:35:08.736] [info] ws_connect: connected
[2020-12-17 22:35:08.736] [info] Uri: /
[2020-12-17 22:35:08.736] [info] Headers:
[2020-12-17 22:35:08.736] [info] Connection: Upgrade
[2020-12-17 22:35:08.736] [info] Sec-WebSocket-Accept: 2yaTFcdwn8KL6IzSMj2u6Le7KTg=
[2020-12-17 22:35:08.736] [info] Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15
[2020-12-17 22:35:08.736] [info] Server: ixwebsocket/11.0.4 macos ssl/SecureTransport zlib 1.2.11
[2020-12-17 22:35:08.736] [info] Upgrade: websocket
[2020-12-17 22:35:08.736] [info] Received 25 bytes
ws_connect: received message: Authorization suceeded!
[2020-12-17 22:35:08.736] [info] Received pong ixwebsocket::heartbeat::30s::0
hello
> [2020-12-17 22:35:25.157] [info] Received 7 bytes
ws_connect: received message: hello
```
If the wrong header is passed in, the server would close the connection with a custom close code (>4000, and <4999).
```
[2020-12-17 22:39:37.044] [info] Upgrade: websocket
ws_connect: connection closed: code 4001 reason Permission denied
```
## echo server
The ws echo server will respond what the client just sent him. If we use the
simple --http_authorization_header we can enforce that client need to pass a
special value in the Authorization header to connect.
```
$ ws echo_server --http_authorization_header supersecret
[2020-12-17 22:35:06.192] [info] Listening on 127.0.0.1:8008
[2020-12-17 22:35:08.735] [info] New connection
[2020-12-17 22:35:08.735] [info] remote ip: 127.0.0.1
[2020-12-17 22:35:08.735] [info] id: 0
[2020-12-17 22:35:08.735] [info] Uri: /
[2020-12-17 22:35:08.735] [info] Headers:
[2020-12-17 22:35:08.735] [info] Authorization: supersecret
[2020-12-17 22:35:08.735] [info] Connection: Upgrade
[2020-12-17 22:35:08.735] [info] Host: localhost:8008
[2020-12-17 22:35:08.735] [info] Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15
[2020-12-17 22:35:08.735] [info] Sec-WebSocket-Key: eFF2Gf25dC7eC15Ab1135G==
[2020-12-17 22:35:08.735] [info] Sec-WebSocket-Version: 13
[2020-12-17 22:35:08.735] [info] Upgrade: websocket
[2020-12-17 22:35:08.735] [info] User-Agent: ixwebsocket/11.0.4 macos ssl/SecureTransport zlib 1.2.11
[2020-12-17 22:35:25.157] [info] Received 7 bytes
```
## Websocket proxy ## Websocket proxy
``` ```

View File

@ -7,9 +7,9 @@
#pragma once #pragma once
#include "IXConnectionState.h" #include "IXConnectionState.h"
#include "IXNetSystem.h"
#include "IXSelectInterrupt.h" #include "IXSelectInterrupt.h"
#include "IXSocketTLSOptions.h" #include "IXSocketTLSOptions.h"
#include "IXNetSystem.h"
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
#include <functional> #include <functional>

View File

@ -6,4 +6,4 @@
#pragma once #pragma once
#define IX_WEBSOCKET_VERSION "11.0.4" #define IX_WEBSOCKET_VERSION "11.0.5"

View File

@ -1367,7 +1367,8 @@ namespace ix
const ix::SocketTLSOptions& tlsOptions, const ix::SocketTLSOptions& tlsOptions,
bool ipv6, bool ipv6,
bool disablePerMessageDeflate, bool disablePerMessageDeflate,
bool disablePong) bool disablePong,
const std::string& httpHeaderAuthorization)
{ {
spdlog::info("Listening on {}:{}", hostname, port); spdlog::info("Listening on {}:{}", hostname, port);
@ -1393,7 +1394,7 @@ namespace ix
} }
server.setOnClientMessageCallback( server.setOnClientMessageCallback(
[greetings](std::shared_ptr<ConnectionState> connectionState, [greetings, httpHeaderAuthorization](std::shared_ptr<ConnectionState> connectionState,
WebSocket& webSocket, WebSocket& webSocket,
const WebSocketMessagePtr& msg) { const WebSocketMessagePtr& msg) {
auto remoteIp = connectionState->getRemoteIp(); auto remoteIp = connectionState->getRemoteIp();
@ -1409,6 +1410,19 @@ namespace ix
spdlog::info("{}: {}", it.first, it.second); spdlog::info("{}: {}", it.first, it.second);
} }
if (!httpHeaderAuthorization.empty())
{
auto authorization = msg->openInfo.headers["Authorization"];
if (authorization != httpHeaderAuthorization)
{
webSocket.close(4001, "Permission denied");
}
else
{
webSocket.sendText("Authorization suceeded!");
}
}
if (greetings) if (greetings)
{ {
webSocket.sendText("Welcome !"); webSocket.sendText("Welcome !");
@ -3039,6 +3053,7 @@ int main(int argc, char** argv)
std::string publisherRolesecret; std::string publisherRolesecret;
std::string sendMsg("hello world"); std::string sendMsg("hello world");
std::string filename; std::string filename;
std::string httpHeaderAuthorization;
ix::SocketTLSOptions tlsOptions; ix::SocketTLSOptions tlsOptions;
ix::CobraConfig cobraConfig; ix::CobraConfig cobraConfig;
ix::CobraBotConfig cobraBotConfig; ix::CobraBotConfig cobraBotConfig;
@ -3185,6 +3200,7 @@ int main(int argc, char** argv)
echoServerApp->fallthrough(); echoServerApp->fallthrough();
echoServerApp->add_option("--port", port, "Port"); echoServerApp->add_option("--port", port, "Port");
echoServerApp->add_option("--host", hostname, "Hostname"); echoServerApp->add_option("--host", hostname, "Hostname");
echoServerApp->add_option("--http_authorization_header", httpHeaderAuthorization, "Hostname");
echoServerApp->add_flag("-q", quiet, "Quiet / only display warnings and errors"); echoServerApp->add_flag("-q", quiet, "Quiet / only display warnings and errors");
echoServerApp->add_flag("-g", greetings, "Greet"); echoServerApp->add_flag("-g", greetings, "Greet");
echoServerApp->add_flag("-6", ipv6, "IpV6"); echoServerApp->add_flag("-6", ipv6, "IpV6");
@ -3507,8 +3523,14 @@ int main(int argc, char** argv)
} }
else if (app.got_subcommand("echo_server")) else if (app.got_subcommand("echo_server"))
{ {
ret = ix::ws_echo_server_main( ret = ix::ws_echo_server_main(port,
port, greetings, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong); greetings,
hostname,
tlsOptions,
ipv6,
disablePerMessageDeflate,
disablePong,
httpHeaderAuthorization);
} }
else if (app.got_subcommand("push_server")) else if (app.got_subcommand("push_server"))
{ {