php有4.6版本吗,【php】Swoole v4.6 版本新特性之 SNI 支持

首页

专栏

php

文章详情

1

110159.html

Swoole v4.6 版本新特性之 SNI 支持

110159.html沈唁

发布于 今天 07:36

Swoole 在 v4.6.0 版本中对 SNI 进行了支持,这篇文章就对这个新特性进行一些演示和说明。

先来了解一下什么是 SNI 协议?

Server Name Identification 简称 SNI,是一个扩展的 TLS 计算机联网协议,用来解决一个服务器拥有多个域名的情况。

在该协议下,在握手过程开始时通过客户端告诉它正在连接的服务器的主机名称。这允许服务器在相同的 IP 地址和 TCP 端口号上呈现多个证书,并且因此允许在相同的 IP 地址上提供多个安全 HTTPS 网站(或其他任何基于 TLS 的服务),而不需要所有这些站点使用相同的证书。

那么如果一台服务器有多个虚拟主机,而且每个主机的域名不一样,使用了不一样的证书,该和哪个主机进行通信?

和 HTTP 协议用来解决服务器多域名的方案类似,HTTP 在请求头中使用 Host 字段来指定要访问的域名。

而 TLS 的做法也是添加 Host,在客户端发出 SSL 请求中的 Client Hello 阶段进行添加,这样就可以让服务器能够切换到正确的域并返回相应的证书。

在 Swoole 的 GitHub 中也有一个 Issue (#4031),想让 Swoole 的 HTTP Server 支持通过 Hostname 来配置 SSL 信息。

实际上是 Swoole 在 #3908 中已经进行了支持,不过英文网站的文档还没来及更新,所以没有找到相关说明。

下面就来演示 Swoole 如何设置 SNI:

首先先下载证书,这里直接使用 Swoole 测试的证书

wget -r -np -nd -P ./ssl_certs https://cdn.jsdelivr.net/gh/swoole/[email protected]/tests/include/ssl_certs/

下载完成后会存放在当前目录下的ssl_certs目录中

再来创建一个 HTTP Server

use Swoole\Http\Request;

use Swoole\Http\Response;

use Swoole\Http\Server;

$http = new Server('0.0.0.0', 9501);

$http->on('request', function (Request $request, Response $response) {

$response->end('Hello Swoole');

});

$http->start();

这里就需要用到一个新增的 ssl_sni_certs 选项

ssl_sni_certs的参数是一个二维数组,key 为 Hostname,value 是对应的证书配置

$http->set([

'ssl_sni_certs' => [

'cs.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_cs_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_cs_key.pem'

],

'uk.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_uk_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_uk_key.pem'

],

'us.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_us_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_us_key.pem',

],

]

]);

配置一下 Server 的 sock_type 来支持 SSL,以及对应的证书配置,就有了如下代码

use Swoole\Http\Request;

use Swoole\Http\Response;

use Swoole\Http\Server;

define('SSL_FILE_DIR', __DIR__ . '/ssl_certs');

$http = new Server('127.0.0.1', 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);

$http->set([

'log_file' => '/dev/null',

'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',

'ssl_key_file' => SSL_FILE_DIR . '/server.key',

'ssl_protocols' => SWOOLE_SSL_TLSv1_2 | SWOOLE_SSL_TLSv1_3 | SWOOLE_SSL_TLSv1_1 | SWOOLE_SSL_SSLv2,

'ssl_sni_certs' => [

'cs.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_cs_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_cs_key.pem'

],

'uk.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_uk_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_uk_key.pem'

],

'us.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_us_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_us_key.pem',

],

]

]);

$http->on('request', function (Request $request, Response $response) {

$response->end('Hello Swoole');

});

$http->start();

搞个客户端来测试一下

$flags = STREAM_CLIENT_CONNECT;

$port = 9501;

$ctxArr = [

'cafile' => SSL_FILE_DIR . '/sni_server_ca.pem',

'capture_peer_cert' => true,

'verify_peer' => false,

];

$ctxArr['peer_name'] = 'cs.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

$ctxArr['peer_name'] = 'uk.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

$ctxArr['peer_name'] = 'us.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

在测试的同时,使用 tcpdump 进行抓包

tcpdump -i lo0 port 9501 -w sni.pcap

请求成功后,客户端会输出对应的三个 Hostname

$ php swoole.php

string(10) "cs.php.net"

string(10) "uk.php.net"

string(10) "us.php.net"

然后使用 Wireshark 来分析一下抓到的包,通过ssl.handshake来过滤出想要的报文

cfccec6463f29e333218dbdd7df24c9b.png

分析报文发现 server_name 的扩展字段只存在于 Client Hello 这个过程中

接着使用ssl.handshake.extensions_server_name进行过滤,提取出包含 SNI 协议的 Client Hello 报文

40398a542017a4e71ac903b0f238b41d.png

就可以看到 SNI 扩展字段:

Extension: server_name (len=15)

Type: server_name (0)

Length: 15

Server Name Indication extension

Server Name list length: 13

Server Name Type: host_name (0)

Server Name length: 10

Server Name: cs.php.net

这里指定了该 TLS 握手的目标域名为 cs.php.net。

通过 SNI,拥有多域名的服务器就可以正常建立 TLS 连接了。

下面是完整的测试代码:

use Swoole\Http\Request;

use Swoole\Http\Response;

use Swoole\Http\Server;

use Swoole\Process\Manager;

use Swoole\Process\Pool;

define('SSL_FILE_DIR', __DIR__ . '/ssl_certs');

$pm = new Manager();

$pm->add(function (Pool $pool, int $workerId) {

$http = new Server('127.0.0.1', 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);

$http->set([

'log_file' => '/dev/null',

'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',

'ssl_key_file' => SSL_FILE_DIR . '/server.key',

'ssl_protocols' => SWOOLE_SSL_TLSv1_2 | SWOOLE_SSL_TLSv1_3 | SWOOLE_SSL_TLSv1_1 | SWOOLE_SSL_SSLv2,

'ssl_sni_certs' => [

'cs.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_cs_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_cs_key.pem'

],

'uk.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_uk_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_uk_key.pem'

],

'us.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_us_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_us_key.pem',

],

]

]);

$http->on('request', function (Request $request, Response $response) {

$response->end('Hello Swoole');

});

$http->start();

});

$pm->add(function (Pool $pool, int $workerId) {

$flags = STREAM_CLIENT_CONNECT;

$port = 9501;

$ctxArr = [

'cafile' => SSL_FILE_DIR . '/sni_server_ca.pem',

'capture_peer_cert' => true,

'verify_peer' => false,

];

$ctxArr['peer_name'] = 'cs.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

$ctxArr['peer_name'] = 'uk.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

$ctxArr['peer_name'] = 'us.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

$pool->shutdown();

});

$pm->start();

phpswoole

阅读 41发布于 今天 07:36

赞1收藏

分享

本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议

110159.html

Swoole

PHP的协程框架

关注专栏

110159.html

沈唁

Swoole & Hyperf & Docsify 开发组成员

欢迎关注我的个人公众号:沈唁志

1.2k声望

70粉丝

关注作者

0 条评论

得票时间

110159.html

提交评论

110159.html

沈唁

Swoole & Hyperf & Docsify 开发组成员

欢迎关注我的个人公众号:沈唁志

1.2k声望

70粉丝

关注作者

宣传栏

目录

Swoole 在 v4.6.0 版本中对 SNI 进行了支持,这篇文章就对这个新特性进行一些演示和说明。

先来了解一下什么是 SNI 协议?

Server Name Identification 简称 SNI,是一个扩展的 TLS 计算机联网协议,用来解决一个服务器拥有多个域名的情况。

在该协议下,在握手过程开始时通过客户端告诉它正在连接的服务器的主机名称。这允许服务器在相同的 IP 地址和 TCP 端口号上呈现多个证书,并且因此允许在相同的 IP 地址上提供多个安全 HTTPS 网站(或其他任何基于 TLS 的服务),而不需要所有这些站点使用相同的证书。

那么如果一台服务器有多个虚拟主机,而且每个主机的域名不一样,使用了不一样的证书,该和哪个主机进行通信?

和 HTTP 协议用来解决服务器多域名的方案类似,HTTP 在请求头中使用 Host 字段来指定要访问的域名。

而 TLS 的做法也是添加 Host,在客户端发出 SSL 请求中的 Client Hello 阶段进行添加,这样就可以让服务器能够切换到正确的域并返回相应的证书。

在 Swoole 的 GitHub 中也有一个 Issue (#4031),想让 Swoole 的 HTTP Server 支持通过 Hostname 来配置 SSL 信息。

实际上是 Swoole 在 #3908 中已经进行了支持,不过英文网站的文档还没来及更新,所以没有找到相关说明。

下面就来演示 Swoole 如何设置 SNI:

首先先下载证书,这里直接使用 Swoole 测试的证书

wget -r -np -nd -P ./ssl_certs https://cdn.jsdelivr.net/gh/swoole/[email protected]/tests/include/ssl_certs/

下载完成后会存放在当前目录下的ssl_certs目录中

再来创建一个 HTTP Server

use Swoole\Http\Request;

use Swoole\Http\Response;

use Swoole\Http\Server;

$http = new Server('0.0.0.0', 9501);

$http->on('request', function (Request $request, Response $response) {

$response->end('Hello Swoole');

});

$http->start();

这里就需要用到一个新增的 ssl_sni_certs 选项

ssl_sni_certs的参数是一个二维数组,key 为 Hostname,value 是对应的证书配置

$http->set([

'ssl_sni_certs' => [

'cs.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_cs_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_cs_key.pem'

],

'uk.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_uk_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_uk_key.pem'

],

'us.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_us_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_us_key.pem',

],

]

]);

配置一下 Server 的 sock_type 来支持 SSL,以及对应的证书配置,就有了如下代码

use Swoole\Http\Request;

use Swoole\Http\Response;

use Swoole\Http\Server;

define('SSL_FILE_DIR', __DIR__ . '/ssl_certs');

$http = new Server('127.0.0.1', 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);

$http->set([

'log_file' => '/dev/null',

'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',

'ssl_key_file' => SSL_FILE_DIR . '/server.key',

'ssl_protocols' => SWOOLE_SSL_TLSv1_2 | SWOOLE_SSL_TLSv1_3 | SWOOLE_SSL_TLSv1_1 | SWOOLE_SSL_SSLv2,

'ssl_sni_certs' => [

'cs.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_cs_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_cs_key.pem'

],

'uk.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_uk_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_uk_key.pem'

],

'us.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_us_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_us_key.pem',

],

]

]);

$http->on('request', function (Request $request, Response $response) {

$response->end('Hello Swoole');

});

$http->start();

搞个客户端来测试一下

$flags = STREAM_CLIENT_CONNECT;

$port = 9501;

$ctxArr = [

'cafile' => SSL_FILE_DIR . '/sni_server_ca.pem',

'capture_peer_cert' => true,

'verify_peer' => false,

];

$ctxArr['peer_name'] = 'cs.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

$ctxArr['peer_name'] = 'uk.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

$ctxArr['peer_name'] = 'us.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

在测试的同时,使用 tcpdump 进行抓包

tcpdump -i lo0 port 9501 -w sni.pcap

请求成功后,客户端会输出对应的三个 Hostname

$ php swoole.php

string(10) "cs.php.net"

string(10) "uk.php.net"

string(10) "us.php.net"

然后使用 Wireshark 来分析一下抓到的包,通过ssl.handshake来过滤出想要的报文

cfccec6463f29e333218dbdd7df24c9b.png

分析报文发现 server_name 的扩展字段只存在于 Client Hello 这个过程中

接着使用ssl.handshake.extensions_server_name进行过滤,提取出包含 SNI 协议的 Client Hello 报文

40398a542017a4e71ac903b0f238b41d.png

就可以看到 SNI 扩展字段:

Extension: server_name (len=15)

Type: server_name (0)

Length: 15

Server Name Indication extension

Server Name list length: 13

Server Name Type: host_name (0)

Server Name length: 10

Server Name: cs.php.net

这里指定了该 TLS 握手的目标域名为 cs.php.net。

通过 SNI,拥有多域名的服务器就可以正常建立 TLS 连接了。

下面是完整的测试代码:

use Swoole\Http\Request;

use Swoole\Http\Response;

use Swoole\Http\Server;

use Swoole\Process\Manager;

use Swoole\Process\Pool;

define('SSL_FILE_DIR', __DIR__ . '/ssl_certs');

$pm = new Manager();

$pm->add(function (Pool $pool, int $workerId) {

$http = new Server('127.0.0.1', 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);

$http->set([

'log_file' => '/dev/null',

'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',

'ssl_key_file' => SSL_FILE_DIR . '/server.key',

'ssl_protocols' => SWOOLE_SSL_TLSv1_2 | SWOOLE_SSL_TLSv1_3 | SWOOLE_SSL_TLSv1_1 | SWOOLE_SSL_SSLv2,

'ssl_sni_certs' => [

'cs.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_cs_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_cs_key.pem'

],

'uk.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_uk_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_uk_key.pem'

],

'us.php.net' => [

'ssl_cert_file' => SSL_FILE_DIR . '/sni_server_us_cert.pem',

'ssl_key_file' => SSL_FILE_DIR . '/sni_server_us_key.pem',

],

]

]);

$http->on('request', function (Request $request, Response $response) {

$response->end('Hello Swoole');

});

$http->start();

});

$pm->add(function (Pool $pool, int $workerId) {

$flags = STREAM_CLIENT_CONNECT;

$port = 9501;

$ctxArr = [

'cafile' => SSL_FILE_DIR . '/sni_server_ca.pem',

'capture_peer_cert' => true,

'verify_peer' => false,

];

$ctxArr['peer_name'] = 'cs.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

$ctxArr['peer_name'] = 'uk.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

$ctxArr['peer_name'] = 'us.php.net';

$ctx = stream_context_create(['ssl' => $ctxArr]);

$client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx);

$cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];

var_dump(openssl_x509_parse($cert)['subject']['CN']);

$pool->shutdown();

});

$pm->start();

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值