如何在 Perl 进程之间共享 OpenSSL 会话?

How can I share OpenSSL sessions between Perl processes?

我正在使用 Perl 通过 TLS 连接到一些(非常)低功率的硬件设备。第一次握手大约需要 10-15 秒才能完成!重用会话(来自同一个 Perl 进程)要快得多,但是当下一个作业连接到同一个设备时,新进程必须以相同的延迟建立一个新会话。我想 在我的进程之间共享一个会话缓存 但我 运行 遇到了问题(和段错误!)。

我有一个测试脚本(连接到 openssl s_server -www)和一个 IO::Socket::SSL::Session_Cache 包装器,它使用 Sereal 将缓存对象写入磁盘。尽管在缓存中找到了现有会话,它不会重用它们,并且在尝试向缓存中添加其他条目时有时会出现段错误。

use 5.20.1; use warnings;

use LWP::UserAgent;
use IO::Socket::SSL;
# $IO::Socket::SSL::DEBUG = 2;

request_with_new_ua();
request_with_new_ua();
request_with_new_ua();

sub request_with_new_ua {
   say "make request";
   my $ua = LWP::UserAgent->new;
   $ua->ssl_opts(
      verify_hostname => 0,
      SSL_session_cache => Inline::SessionStore->new,
   );
   process_response($ua->get('https://localhost:4433'));
}

sub process_response {
   my $res = shift;
   say "> $_" for grep /Session|Master/, split /\n/, $res->as_string;
}

BEGIN {
   package Inline::SessionStore;

   use 5.20.1; use warnings;
   use Moo;
   use experimental qw(signatures);
   use Sereal::Encoder;
   use Sereal::Decoder;
   use Try::Tiny;
   use Path::Tiny;

   has session_cache => ( is => 'rw' );

   my $encoder = Sereal::Encoder->new;
   my $decoder = Sereal::Decoder->new;
   my $file = path('/tmp/ssl-session-cache');

   sub get_session ($self, $key) {
      say "get session $key";
      my $cache;
      try {
         $cache = $decoder->decode($file->slurp_raw);
         say "got cache from file, ".ref $cache;
      } catch {
         say $_ unless /No such file/;
         $cache = IO::Socket::SSL::Session_Cache->new(128);
         say "made new cache";
      };
      $self->session_cache($cache);

      my $ret = $cache->get_session($key);
      say "found session $ret" if $ret;
      return $ret;
   }

   sub add_session {
      my $self = shift;
      say"add session " . join ' - ', @_;
      my $session = $self->session_cache->add_session(@_);

      $file->spew_raw($encoder->encode($self->session_cache));
      return $session;
   }

   sub del_session {
      my $self = shift;
      say "del session " . join ' - ', @_;
      $self->session_cache->del_session(@_);

      $file->spew_raw($encoder->encode($self->session_cache));
   }

   1;
}

并输出:

 rm -f /tmp/ssl-session-cache && perl wes.swp/workbench.pl
提出要求
获取会话 localhost:4433
做了新的缓存
添加会话 localhost:4433 - 23864624
> SSL 会话:
> 会话 ID:
> 会话 ID-ctx:01000000
> 万能钥匙:DDF335492BFE2A7BA7674A656E72005865859D89249D597302F338D01C5776E2C94B61E6BCBED6114DFDA5AAEECD22EA
提出要求
获取会话 localhost:4433
从文件中获取缓存,IO::Socket::SSL::Session_Cache
找到会话 23864624
添加会话 localhost:4433 - 23864624 # 尝试重新添加会话??
> SSL 会话:
> 会话 ID:
> 会话 ID-ctx:01000000
> 万能钥匙:4FE17B7FE9B4DE0A711C659FC333F686AD41840740B9D10E67A972D5A27D1720F0470329DA63DE65C1B023A1E2F0CC89
提出要求
获取会话 localhost:4433
从文件中获取缓存,IO::Socket::SSL::Session_Cache
找到会话 23864624
添加会话 localhost:4433 - 23864624
> SSL 会话:
> 会话 ID:
> 会话 ID-ctx:01000000
> 主密钥:C76C52E5ECC13B0BB4FA887B381779B6F686A73DDFBEA06B33336537EC6AE39290370C07505BCD8B552CA874CD6E4089

我觉得我快要开始工作了,但我遗漏了一些东西。

我认为没有办法在两者之间使用 IO::Socket::SSL/Net::SSLeay 或 Crypt::SSLeay(它们是 LWP 的新旧 SSL 后端)过程。

您尝试在代码中使用的会话缓存引用了 OpenSSL 库内部的 SESSION 对象。在 Perl 级别序列化缓存不会序列化 OpenSSL 库内部的部分,而只会包括指向内部结构的指针。由于这些指针仅对进程的当前状态有效,因此在不同的进程或进程状态中再次反序列化将导致悬空指针在最好的情况下指向任何地方或在最坏的情况下指向某些其他数据,从而导致在分段错误或内部数据损坏中。

在 Net::SSLeay 中有 i2d_SSL_SESSION 和 d2i_SSL_SESSION 函数,理论上可用于正确序列化 SESSION 对象。但我怀疑在当前的实现中是否可用。