我的 websocket 的服务器源检查安全吗?

Posted

技术标签:

【中文标题】我的 websocket 的服务器源检查安全吗?【英文标题】:Is my websocket's server origin checking safe? 【发布时间】:2012-12-27 17:02:38 【问题描述】:

我正在使用https://github.com/lemmingzshadow/php-websocket/

我可以允许一些域,我已经允许localhost 和一个指向我本地服务器的域。但我想知道在他的计算机上有服务器的其他人是否可以使用他的本地主机服务器中的脚本连接到我的 websocket(通过我的域)。

以下是相关代码:

-> 服务器/server.php

$server->setAllowedOrigin('localhost');
$server->setAllowedOrigin('mydomain.com');

-> 服务器/lib/WebSocket/Connection.php

// check origin:
if($this->server->getCheckOrigin() === true)

    $origin = (isset($headers['Sec-WebSocket-Origin'])) ? $headers['Sec-WebSocket-Origin'] : false;
    $origin = (isset($headers['Origin'])) ? $headers['Origin'] : $origin;
    if($origin === false)
    
        $this->log('No origin provided.');
        $this->sendHttpResponse(401);
        stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
        $this->server->removeClientOnError($this);
        return false;
    

    if(empty($origin))
    
        $this->log('Empty origin provided.');
        $this->sendHttpResponse(401);
        stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
        $this->server->removeClientOnError($this);
        return false;
    

    if($this->server->checkOrigin($origin) === false)
    
        $this->log('Invalid origin provided.');
        $this->sendHttpResponse(401);
        stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
        $this->server->removeClientOnError($this);
        return false;
    

-> 服务器/lib/WebSocket/Server.php

public function checkOrigin($domain)

    $domain = str_replace('http://', '', $domain);
    $domain = str_replace('https://', '', $domain);
    $domain = str_replace('www.', '', $domain);
    $domain = str_replace('/', '', $domain);

    return isset($this->_allowedOrigins[$domain]);


public function setAllowedOrigin($domain)

    $domain = str_replace('http://', '', $domain);
    $domain = str_replace('www.', '', $domain);
    $domain = (strpos($domain, '/') !== false) ? substr($domain, 0, strpos($domain, '/')) : $domain;
    if(empty($domain))
    
        return false;
    
    $this->_allowedOrigins[$domain] = true;     
    return true;

编辑:

也许我不够清楚。我希望每个人都可以连接到 websocket,但前提是他们在我的域(或我的本地主机)中,类似于 AJAX 中的同源策略。

我担心的是,如果我允许 localhost,也许其他计算机中的所有其他 localhost 也会被允许。

【问题讨论】:

也许其他计算机中的所有其他 localhost 也将被允许。不,这不是问题。您的服务器正在将 IP 转换为名称,localhost 仅用于来自 127.0.0.1 的连接,这是环回地址。 谢谢,这就是我想知道的。但是来源是由标头提供的,如果我不告诉它这样做,我的服务器不应该分析我的标头来尝试转换 IP 和域,不是吗? 来自IP包的头部,用于实现TCP/IP传输。如果您在允许列表中使用主机名,则必须使用 DNS 将这些名称转换为 IP。 @Barmar 但是从 localhost 执行脚本与从 127.0.0.1 执行脚本相同,因为它是相同的;但我的 websocket 不这么认为,并阻止了来自 127.0.0.1 的连接,因为我只允许来自 localhost。然后,我的服务器没有转换 IP 或主机名。 我以为你担心它允许太多,而不是阻止真正的本地主机。您确定连接真的来自本地主机,而不是来自本地机器的配置 IP?如果连接是 localhost,它只会是from localhost。 【参考方案1】:

如果你想确定,你应该添加一个IP检查,分析$_SERVER["REMOTE_ADDR"]。正在检查的来源是客户端提供的文本值,很容易伪造。

if (!in_array($_SERVER["REMOTE_ADDR"], array("127.0.0.1", "ip.of.dom.ain")) exit;

如果您不想对 IP 地址进行硬编码或频繁更改,您可以使用gethostbyname 像这样获取域的 IP 地址。请注意,当必须为域查询 DNS 时,这将增加每个请求的延迟,并且当您的 DNS 出现故障时,这种方式将导致超时。 (下面的代码当然可以优化)

$allowed_origins = array('localhost', 'mydomain.com');
$allowed_ips = array();
foreach ($allowed_origins as $domain) 
    $server->setAllowedOrigin($domain);
    $allowed_ips[] = gethostbyname($domain);

if (!in_array($_SERVER["REMOTE_ADDR"], $allowed_ips)) exit; 

执行此操作的正确方法可能是通过您的网络服务器保护您的资源,使其仅接受来自您希望允许的 IP 地址的请求(请参阅deny in Apache 或deny in nginx)。

【讨论】:

但我无法进行 IP 检查,因为我希望每个人都可以连接到 websocket,但前提是他们在我的域中......并且使用 $_SERVER["REMOTE_ADDR"](或 Apache 中的 deny ) 我可以检查客户端的 IP,而不是域的 IP,不是吗? 哦,我明白了,我只是假设您在某种程度上是代理。好吧,那么任何人都可以连接到您的 websocket,这就是我们的想法。这个其他人的机器上是否有服务器并不重要。他可以使用像curl 这样的任何程序连接到您的服务器。您特别想避免什么情况? (你为什么说有服务器的人?) 我说的是有服务器的人,因为我将 localhost 添加到允许的源列表中,然后我认为有服务器的人(他的计算机上的 localhost)有一个连接到我的 Websocket 的脚本;他可以这样做,因为脚本是在他的本地主机中执行的,这与我的本地主机匹配,即使它们不一样。所以,我想要的是类似于 AJAX 中的同源策略,只允许来自我的域和本地主机的连接。【参考方案2】:

虽然当我只想允许我的本地主机时,我担心允许来自所有本地主机的连接,但我发现在another question,kanaka 中说:

为了澄清,javascript 运行在一个不妥协和良好的行为 浏览器不能影响 Sec-WebSocket-Origin 的值 加载页面的原始站点的值(允许您的 服务器只允许某些起始点)。但是,如果你有 在 Node.js 中作为 WebSocket 客户端运行的 Javascript,它可以设置 任何它想要的原始值。 CORS 安全是为了安全 浏览器用户,而不是您的服务器。你需要其他机制来 保护您的服务器。

那么,我的原点检查代码根本不安全;并且是否都允许所有 localhost 都没关系,因为如果有人有足够的知识使用他自己的 localhost 连接到我的 websocket,那么他很可能可以修改 Origin 标头。

【讨论】:

没有“别人的本地主机”这样的东西 @MahmoudAl-Qudsi 好吧,我的意思是“别人的本地服务器”,使用 localhost(在他的机器上)访问。 这也是我的意思。 每个服务器对自己来说都是“本地主机”。【参考方案3】:

如果您正在检查 Origin 标头,则足以执行类似同源策略之类的内容。 Origin 标头可以在自定义应用程序(如节点 WebSocket 客户端)中在浏览器之外被欺骗,但这没关系,对于普通的 HTTP 请求也是如此。 SOP 只针对浏览器。

所以检查Origin 标头不是关于访问保护,而是关于CSRF 保护。如果用户当前已登录,则不允许不受信任的第三方站点连接到您的 WebSocket 端点(自动发送凭据)。

【讨论】:

以上是关于我的 websocket 的服务器源检查安全吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 websocket 检查消息是不是真的在 Netty 中传递?

我的 CSRF 保护方法安全吗?

WebSockets...它们不安全吗?

Jetty websocket 客户端类 WebSocketClient 线程安全吗?

Java EE 7:如何将 EJB 注入 WebSocket ServerEndpoint?

如何使用 node.js 使 Websocket 服务器安全