PHP Server SSL WebSocket receives binary garbage - php

I'm having deep trouble with implementing a PHP server websocket. It works completely fine without SSL.
However, when I switch client to "wss://" and add SSL context to the server socket, I get random binary garbage after client connects. I searched everything on the web but cannot figure it out.
Client code does basically nothing:
var ws = new WebSocket('ws://127.0.0.1:8888');
ws.onopen = function() {
console.log('ws open');
}
Here is the minimal PHP code for SSL-less connection which works fine:
<?php
$socket = stream_socket_server(
'tcp://127.0.0.1:8888',
$errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN
);
$cn = stream_socket_accept($socket, 99999, $name);
$text = fread($cn, 1024);
echo $text;
?>
With it, and client using "ws://", I receive normal WebSocket upgrade request which I can process. Everything is fine.
Now, I change client call to "wss://" and add SSL to server:
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'local_cert', '../ssl/cert.pem');
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
stream_context_set_option($context, 'ssl', 'verify_peer', false);
$socket = stream_socket_server(
'tcp://127.0.0.1:8888',
$errno, $errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
$context
);
and this time I receive some binary encoded data which I'm unable to process:
▬♥☺☻☺☺♥♥{8T▬↕↑/Б{Ҭh#_W l0w2♣9x(j‹~Y{A∟MTT
::‼☺‼☻‼♥+/,0̨̩‼¶/5☺☺↨☺☺
↕►♦♦♦☺♣♣♣♠♠☺↕3+)**☺↔ JtD~[}▼$▲ɒNt◄vCyw-☻☺☺+♂
**♥♦♥♥♥☻♥☺♥☻☻ ☺§
I also tried all kinds of keys and certificates for SSL. I used Apache and Nginx certs, I created them manually with OpenSSL, I created them with PHP code that I found on web, nothing helps. I reproduced this on Windows and Ubuntu systems with PHP7.4 and Apache and Nginx web servers, used localhost and external IP addresses, does not differ.

Related

Correct way to reopen TCP connection with PHP after remote party closes?

I'm having issues reopening a TCP socket connection after its remotely closed. Specifically this is for Apple Push Notification service - when Apple encounters a device token from you that pairs with a device that deleted your application they close the secure TCP socket to their push gateway. I need to immediately reopen this connection (or start a new one) when this issue occurs so I can continue sending notification to other users who still do have the app installed. I have some code using which I try to sort this out but it's freezing my entire Apache server and I have to restart manually to get things running again and I have no idea why. Below is the code with which I attempt to reopen a socket with Apple:
if($error_response == "InvalidToken"){
$results .= "\ntried to restart";
fclose($fp);
usleep(20000);
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck_real.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', '****');
$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
}

Making a connection in PHP using TLS

I wrote a small SIP client for a special purpose.
Basically it connects to port 5060 using function fsockopen()
$fp = fsockopen("10.0.0.1", 5060, $errno, $errstr, 30);
and then basically reads and writes SIP commands using fread() and fwrite().
Now my SIP service operator wants us clients to use SIPS which is
basically SIP over TLS. I've spent hours looking for information
on how to connect to a port with TLS using PHP but without any
success. Apparently the fsockopen() supports TLS to an extent but
when I replaced the above with:
$fp = fsockopen("tls://10.0.0.1", 5061, $errno, $errstr, 10);
I get nothing. I can connect to the server from my shell with with OpenSSL client:
$ openssl s_client -connect 10.0.0.1:5061
And I can communicate with the SIP server through that connection without problems. OpenSSL support is compiled in the PHP.
My problem is that I can't set up the TLS connection with the server in PHP. I noticed in some forums that in older versions of PHP it was possible to build and use the SSL context with fsockopen() but apparently not anymore, since I get an error about too many parameters.
I also tried using stream_context_create() with stream_context_set_option() but I get no replies from the server:
$context = stream_context_create();
$result=stream_context_set_option($context, 'ssl', 'verify_peer', 'TRUE');
$result=stream_context_set_option($context, 'ssl', 'cafile', 'cert.cer');
$result=stream_context_set_option($context, 'ssl', 'verify_depth', '5');
$fp = stream_socket_client('tls://'.$host.':'.$port, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);
but I still can't get any replies or errors from the server. What is the recommended and working way of using TLS in PHP?
This is enough to open a TLS connection:
$context = stream_context_create();
$fp = stream_socket_client('tls://'.$host.':'.$port, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context)
if the certificates are in their place. If they are not, follow the instructions linked by Filippos.
I think some of your options are wrong for validating self signed certificates; the following should be enough to make it work:
$context = stream_context_create([
'ssl' => [
'verify_peer' => true,
'allow_self_signed' => true,
'local_cert' => 'cert.cer',
],
]);
I just recently encountered this error, debugging here and there for connection problem or proxy problem, finally found the culprit ... and the solution was to install php-pecl-crypto extension.
My machine is centos 7 using remi-repo for PHP
You will also need to install the appropriate certificates to get TLS working. Check this SO question for an example of how to use your key files with regular TLS connections.
Also, check the specs of SIPS itself in RFC 5630.

Port 2195 open but cannot access APNS server

Problem:
I cannot access APNS server. I get a 110 Connection Time Out error.
My situation:
I've contacted my host (hostmonster). They said my port 2195,2196 are already open.
My certificate and passphrase is no problem, for I've tested it from my local Mac.
I use a very simple PHP to test. Here's my code:
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'apn.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', 'aaa');
$fp = stream_socket_client("ssl://gateway.sandbox.push.apple.com:2195", $err,
$errstr,60,STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT,$ctx);
if ($fp)
echo ('aa');
else
echo ($err.$errstr);
I had the same exact problem, but with Bluehost. In my case, it turned out the Bluehost firewall was not properly open. The first line chat support from Bluehost said the ports 2195 & 2196 are open, and the problem was not with them. To demonstrate the problem was on their side, I asked them to run the following telnet command from the Bluehost server:
telnet gateway.push.apple.com 2195
Trying 17.149.36.246...
Trying 17.149.35.166...
Trying 17.149.35.170...
Trying 17.149.35.177...
... Connection timed out
The output should be:
telnet gateway.push.apple.com 2195
Trying 17.149.36.230...
Connected to gateway.push.apple.com.
Escape character is '^]'.
Then i submitted a support ticket to Bluehost with this information. They resolved the issue in 6 hours by properly opening the firewall.

Apple Push with proxy and stream_context

I have to send push notification to iOS devices. My connection has to be enabled through a proxy. I tried everything but without success. I have an error 110 Connection Timed Out. It's working with cURL if I just try to connect to Apple push's address. I don't know where the problem is. Proxy config ? PHP stream_context wrong implementation ?
Here's my code :
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'certificate.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', 'my_passphrase');
stream_context_set_option($ctx, 'ssl', 'verify_peer', false);
stream_context_set_option($ctx, 'http', 'proxy', 'tcp://my-proxy.net:8080');
stream_context_set_option($ctx, 'http', 'request_fulluri', true);
$fp = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $err,$errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
var_dump($fp);
var_dump($err);
var_dump($errstr);
exit;
Do you have an idea ?
EDIT:
Can it be directly linked to Squid ? I just figured out the proxy is running with Squid.
I also try with fopen() function instead of stream_socket_client() but it seems it doesn't allow ssl protocol.
Here's my var_dump outputs : bool(false) int(110) string(20) "Connection timed out"
I also have this warning : Warning: stream_socket_client(): unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Connection timed out) in /share/www/website/test.php on line 22
It could simply be your proxy does not allow port 2195 to be opened.
iPhone Push Notification Unable to Connect to the SSL Server
I guess you either:
Need to talk to the people who run the proxy to see if port 2195 is open or not.
or
Setup a test server listening on port 2195 and then try to do a test connection to it through the proxy. That should allow you to test if it is the proxy that is blocking the connection requests.
or
Test whether Curl can open the connection using a proxy.
Which is done by setting the options:
// sets the proxy to go through
curl_setopt($ch, CURLOPT_PROXY, $proxy);
// sets to use a tunnel proxy which most http proxies are
curl_setopt($ch, CURLOPT_HTTPTUNNELPROXY, $proxy);
Full testing code here.
create the SSL context
open a tcp socket to the proxy
send request to the proxy to connect to APNs
once connexion is accepted enable SSL
Have a look at my reply in this post: Send Push Notification to APNS through proxy

PHP WebSocket SSL

I am trying to upgrade my PHP WebSocket library to SSL, using the wss://-protocol on the client-side. However, I find myself at a loss trying to figure out a) how to set up the server socket, and b) how to decode the incoming messages. Here is my current attempt at the former:
$context = stream_context_create();
// local_cert must be in PEM format
stream_context_set_option($context, 'ssl', 'local_cert', $pemFilePath);
// Pass Phrase (password) of private key
stream_context_set_option($context, 'ssl', 'passphrase', $pemPassPhrase);
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
stream_context_set_option($context, 'ssl', 'verify_peer', false);
$serverSocket = stream_socket_server('ssl://'.$host.':'.$port, $errNo, $errStr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);
However, when using SSL as the server protocol, the client does not seem to be sending any messages, and when using tcp, I get weird, encrypted data, although it was my guess that it would be magically decrypted due to my providing the options to the stream context.
What am I doing wrong?
I ran into the same problem with our implementation, and found a good workaround. Put your websocket server on any firewalled port, and tunnel connections from a secure port port. You can use stunnel with a configuration like this:
[websockets]
accept = 8443
connect = 8080
This way, stunnel does all the security, and your websocket server does all the websockets. Win!

Categories