PHP Ratchet WebSocket SSL 连接?

45

我有一个活动聊天服务器文件。

use Ratchet\Server\IoServer;
use Ratchet\WebSocket\WsServer;
use MyAppChat\Chat;
require dirname(__DIR__) . '/vendor/autoload.php';
$server = IoServer::factory(
    new WsServer(
        new Chat()
    )
  , 26666
);
$server->run();

我使用Websocket连接到ws,并且它运行良好。

if ("WebSocket" in window) {
    var ws = new WebSocket("ws://ratchet.mydomain.org:8888");
    ws.onopen = function() {
        // Web Socket is connected. You can send data by send() method.
        ws.send("message to send");
    };
    ws.onmessage = function (evt) { 
        var received_msg = evt.data;
    };
    ws.onclose = function() { 
        // websocket is closed. 
    };
} else {
  // the browser doesn't support WebSocket.
}

我想要安全连接,因此我尝试使用SSL进行连接,但无法正常工作。

if ("WebSocket" in window) {
    var ws = new WebSocket("wss://ratchet.mydomain.org:8888");
    ws.onopen = function() {
        // Web Socket is connected. You can send data by send() method.
        ws.send("message to send");
    };
    ws.onmessage = function (evt) { 
        var received_msg = evt.data;
    };
    ws.onclose = function() { 
        // websocket is closed. 
    };
} else {
  // the browser doesn't support WebSocket.
}

我的问题是如何使用SSL连接WebSocket,有什么想法吗?
9个回答

63

如果您正在使用 Apache Web 服务器(2.4 或更高版本),请在 httpd.conf 文件中启用以下模块:

  1. mod_proxy.so
  2. mod_proxy_wstunnel.so

将此设置添加到您的 httpd.conf 文件中。

ProxyPass /wss2/ ws://ratchet.mydomain.org:8888/

当您想要建立一个 WSS 连接时,请在您的 JavaScript 调用中使用此 URL:

var ws = new WebSocket("wss://ratchet.mydomain.org/wss2/NNN");

在应用设置之前,重新启动Apache Web服务器,并确保您的Ratchet工作程序(Web套接字连接)已打开(telnet主机名端口)。


3
好的,这个可以。但是我有一个问题:为什么要在结尾加上“/NNN”?谢谢! - amcastror
2
关于我们下一个URL参数(例如:/wss2/param1/param2...),它可以被我们的系统用来识别请求目的。如果不需要,您可以将其删除,但请注意,您必须具有相同匹配身份的URL参数(在本例中为“/wss2/”),以便服务器能够匹配/捕获请求并将其转发到自定义域/端口。 - webcoder
1
这些指令在哪种虚拟主机中起作用? 需要监听哪个端口?能否给出一个包含HTTP和WS协议的虚拟主机示例? - Flo Schild
2
作为后续:我最终在本地使用https在Chrome和Firefox上使其工作,但在我的情况下,我必须使用端口8080...我对要使用哪个端口感到困惑,这导致了很多尝试和错误。大多数是错误的。这是对我有用的内容:var conn = new WebSocket('wss://localhost/wss2/');ProxyPass /wss2/ ws://localhost:8080/$server = IoServer::factory( new HttpServer( new WsServer( new Chat() ) ), 8080 ); - Joe Leonard
1
通过这样做,会影响性能或导致任何漏洞吗? - Deepak M
显示剩余5条评论

18
几天前,我在寻找这个问题的答案时,在 Github 的 Ratchet issues 中找到了这篇文章:https://github.com/ratchetphp/Ratchet/issues/489 最后一个回答者是heidji,他说:
“我只是为像我这样的新手添加了这条评论,他们需要快速了解如何实现 SSL: 通过 ReactPHP 文档,您只需要以这种方式构造 SecureServer:
$webSock = new React\Socket\Server('0.0.0.0:8443', $loop);
$webSock = new React\Socket\SecureServer($webSock, $loop, ['local_cert' => '/etc/ssl/key.pem', 'allow_self_signed' => true, 'verify_peer' => false]);
然后按照 cboden 的建议将其注入到 IoServer 中”
因此,现在似乎有一种方法可以在不需要 HTTPS 代理的情况下实现使用 Ratchet 的安全 WebSocket 服务器。
这里是 SecureServer 类文档:https://github.com/reactphp/socket#secureserver

4
这是最新的答案,现在应该被接受作为采纳的答案,因为已经添加了SSL支持! - BenMorel

12

问题在于 React(Ratchet 基于此构建)不支持直接的 SSL 连接。请参阅此问题

有一个简单的解决方法,使用配置如下的stunnel

[websockets]
accept = 8443
connect = 8888

Stunnel将在8443端口处理SSL流量,并将其转发到您的Websocket服务器。


现在nginx已经支持代理websocket了,我建议使用它。这样我们就可以在443端口上提供wss服务,从而避免被大多数企业防火墙阻挡。 - Arun Poudel
4
似乎自2017年初以来已支持SSL。请参见reactphp/socket#55@Jordi的答案 - BenMorel

9
我在Ratchet的谷歌小组上找到了这个答案,作者是Chris Boden:

最好的解决方案是使用Nginx作为您的Web服务器。让Nginx监听端口80以接收传入连接,并处理您的SSL。 Nginx将将传入的连接转发到PHP-FPM以处理您的常规网站,如果它检测到连接是WebSocket连接,则将其代理到您选择的端口上运行的Ratchet应用程序。然后,您的javascript可以通过wss://mydomain.org连接。

这是一种替代方法,如果您的应用程序要使用nginx提供服务,而不是使用stunnel


谢谢你的回答,我已经完成了这项工作:D,现在我将使用Python中的Scapy来欺骗源IP地址,但这真的很困难:(你能帮我吗? - Viet Nguyen
@temuri 是的,我有一段时间前找到了一个配置使其工作的要点。可惜现在找不到了。 - Songo
哎呀...有stunnel配置示例吗? - temuri
@Songo 谢谢!不幸的是,我对nginx配置不是很熟悉。我不确定如何修改我的现有配置。这是我尝试的结果:https://gist.github.com/temuri416/9633097。我做错了什么? - temuri
2
一个改进的尝试:https://dev59.com/b2Eh5IYBdhLWcg3wXCdL - temuri

6
如果您正在使用Nginx,只需在SSL服务器块中编写以下内容:
location /services/myservice {
    # switch off logging
    access_log off;

    # redirect all HTTP traffic to localhost
    proxy_pass http://localhost:1234;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # WebSocket support (nginx 1.4)
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    # Path rewriting
    rewrite /services/myservice/(.*) /$1 break;
    proxy_redirect off;

    # timeout extension, possibly keep this short if using a ping strategy
    proxy_read_timeout 99999s;
}

这将升级任何wss://yoursite.com/services/myservice的调用到运行在端口1234上的套接字。只需确保记得不要将端口1234开放给全世界。

嗨@RaisinBranCrunch,你能告诉我最终的代码是什么吗?我的意思是对于这行代码var ws = new WebSocket("wss://ratchet.mydomain.org/wss2/NNN");,需要做哪些更改。谢谢。 - Mushfiqur Rahman
@MushfiqurRahman 在我的帖子的位置块之前,我在服务器块中有一行代码:server_name subdomain.mysite.com;。因此,这意味着在我帖子中的位置块是/services/myservice,在Javascript中的最终结果将是var ws = new WebSocket("wss://subdomain.mysite.com/services/myservice"); - RaisinBranCrunch

4

对我来说,Apache也可行,只需在域配置中添加:

ProxyPass /wss/ wss://127.0.0.1:8888/

重新加载apache,然后在客户端设置中设置wss以包括/wss/位置。
wss://127.0.0.1/wss/

我们需要将这个添加到Apache 2.4的哪个文件中? - Param sohi
httpd.conf 文件中!文件 httpd.conf 应该位于目录 /conf/httpd.conf 下。 - Sajjad Hossain Sagor

3
如果您正在使用Windows IIS,请确保已为HTTPS进行配置(我使用自签名证书),然后安装反向代理:
URL重写:https://www.iis.net/downloads/microsoft/url-rewrite和ARR 3.0:https://www.iis.net/downloads/microsoft/application-request-routing 您还需要在IIS中启用WebSockets支持:enter image description here 创建文件夹(例如myproxyfolder)以进行URL重写,并在该文件夹上创建包含以下内容的web.config文件:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="WebSocketProxy" stopProcessing="true">
                    <match url="(.*)" />
                    <action type="Rewrite" url="http://127.0.0.1:8080" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

将 "http://127.0.0.1:8080" 更改为您的websocket服务(我在WIN上使用PHP的Ratchet)。

在javascript中的客户端,使用安全的websockets wss://协议,例如:

    mysock = new WebSocket('wss://127.0.0.1/myproxyfolder');
...

2
这对于我的Ubuntu 18.04很有效。
在终端中运行以下命令以启用代理模块:
sudo a2enmod proxy proxy_balancer proxy_wstunnel proxy_http 然后,在我的Apache虚拟主机配置文件(/etc/apache2/sites-available/000-default-le-ssl.conf)中添加以下行:
ProxyRequests Off
ProxyPass "/ws/" "ws://domain.com:5555/"
最后,重新启动apache服务即可。这样WebSocket就可以在https中使用了。

0

我试图为子域名实现这个功能。例如:从apache将realtime.domain.org重定向到localhost:8080。

以下是它的工作原理。您可以创建一个虚拟主机并代理通过它。

<VirtualHost *:80>
    ServerName realtime.domain.org

    RewriteEngine On
    RewriteCond %{HTTP:Connection} Upgrade [NC]
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteRule /(.*) ws://localhost:8080/$1 [P,L]

    ProxyPreserveHost On
    ProxyRequests off
    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/
</VirtualHost>

因此,所有对realtime.domain.org的请求都可以重定向到端口8080,在那里您可以运行WebSocket处理程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接