I made a WebSocket server with PHP and added the feature to add SSL/TLS to the server. Now I enabled crypto with stream_socket_enable_crypto and everything works. I read data and it's decrypted. Only HTTP requests are a bit weird:
Client sends:
GET / HTTP/1.1
Host: example.com:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0
Accept: */*
...
Server receives:
HTTP/1.1
Host: example.com:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0
Accept: */*
...
My Questions are: What is the reason for the missing bytes? What can I do to get those bytes back?
I use PHP 8.2
Server:
Terminal:
openssl genrsa -out wss.key 2048 && openssl req -key wss.key -new -x509 -days 365 -out wss.crt
PHP:
error_reporting(E_ALL);
ini_set("display_errors", "1");
if(ob_get_level() == 0) ob_start();
$server = stream_socket_server("tcp://example.com:8080", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);
stream_context_set_option($server, ["ssl" => [
"local_cert" => __DIR__."/wss.crt",
"local_pk" => __DIR__."/wss.key",
"disable_compression" => true,
"verify_peer" => false,
"verify_peer_name" => false,
"allow_self_signed" => true
]]);
$done = false;
while(!$done) {
$sockets = ["server" => $server];
if(stream_select($sockets, $w, $e, 0, 20) === false)
show("ERROR");
if(in_array($server, $sockets)) {
$client = stream_socket_accept($server);
$sockets[] = $client;
stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_TLS_SERVER);
}
foreach($sockets as $client) {
if(!in_array($client, $sockets))
continue;
show(fread($client, 100));
fclose($client);
$done = true;
show("EXIT");
}
}
function show(...$data) {
var_dump($data);
@ob_flush();
flush();
}
Client:
JS:
url = "ws://example.com:8080";
socket = new WebSocket(url);
setTimeout(() => {
socket.close();
socket = new WebSocket(url);
}, 1000);
Change example.com:8080
with your host and port
for detailed code look at PHPWS
I now know the issue. The TCP paylaod starts after
00 00
and in HTTP there begins theGET /
and in TLS theTLS headers
:17 03 03 03 15
with the TLS DATA TYPE (Application Data23
), the TLS VERSION (1.203 03
) and the PAYLOAD LENGTH (78903 15
)In PHP after
stream_socket_enable_crypto
the first package is read as TLS and the first 5 bytes as the TLS headers and after failing, because its HTTP, it is missing those 5 bytes in the payload.Solution:
Before enabling crypto, read the data without clearing it from the buffer (PEEK)
Now the data can be used if it's just a HTTP request and no TLS header information are missing when it's a TLS handshake.