Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPで TLSのプロトコルを実装してみる

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for higaki higaki
March 20, 2026

PHPで TLSのプロトコルを実装してみる

PHPerKaigi2026 2026/03/21 11:35〜Track B の登壇資料です。

Avatar for higaki

higaki

March 20, 2026
Tweet

More Decks by higaki

Other Decks in Programming

Transcript

  1. TLSとは SSL/TLS はセッション層に位置するセキュアプロトコルで、通信の 暗号化、データ完全性の確保、サーバ(場合によりクライアント)の 認証を行うことができる。 • 通信の暗号化 • データ完全性の確保 •

    サーバ(場合によりクライアント)の認証 引用:IPA TLS 暗号設定 ガイドライン [https://www.ipa.go.jp/security/crypto/guideline/gmcbt80000005ufv-att/ipa-cryptrec-gl-3001-3.1.1.pdf]
  2. TLS1.2とTLS1.3の大きな違い(補足スライド) 参考:RFC8446 Major Differences from TLS 1.2 [https://datatracker.ietf.org/doc/html/rfc8446#section-1.2] • ServerHello

    以降の通信が暗号化 • 1-RTTでハンドシェイクが完結 • 0-RTTモードが追加 • 鍵交換は DHE、ECDHE、PSK のみが規定され、いずれかの利用が必須になった • HKDF-Expand, HKDF-Extractを使った鍵導出 に変更 • 共通鍵暗号は AES-GCM、AES-CCM、ChaCha20-Poly1305 のみが規定された • 署名は RSA-PSS、RSASSA-PKCS1-v1_5、ECDSA が必須になった 参考:IPA TLS 暗号設定 ガイドライン [https://www.ipa.go.jp/security/crypto/guideline/gmcbt80000005ufv-att/ipa-cryptrec-gl-3001-3.1.1.pdf]
  3. ClientHello(Client → Server) TLSの最初のメッセージ • 対応できる鍵交換・署名アルゴリズム・暗号スイート • サポートversion(TLS1.2, TLS1.3) ClientHello

    やっほー、話したいんだけどさ。 暗号はこのへん使えるよ( cipher suites)。 署名はこのへんならいける( signature_algorithms)。 鍵交換のパーツも先にいくつか持ってきた( key_share)。 Client Server
  4. EncryptedExtensions(Client ← Server) ServerHelloの追加情報 • ServerHelloで送信したExtension以外 ◦ key_share ◦ pre_shared_key

    ◦ supported_versions EncryptedExtensions Client (お互い共通鍵を計算できるし、ここから暗号化して送るか) これ補足事項ね。 Server
  5. ApplicationData(Client ←→ Server) • アプリケーション層のやり取り ◦ HTTPとかそのあたり ▪ GET /

    HTTP/1.1 Client Server ApplicationData (ApplicationDataは別の共通鍵で暗号化する) 本題(ApplicationData)は GET / HTTP/1.1
  6. 注意事項 • TLS1.3のサーバ側を実装。 • パフォーマンスは考慮できていない。 ◦ いろいろ漏れはあるけど、動くものを実装。 • 特定の鍵交換・暗号方式しか対応していない。 ◦

    鍵交換:ECDHE(X25519) ◦ 暗号化:TLS_AES_256_GCM_SHA384 ◦ 署名:ECDSA • OpenSSL使用。 • 0-RTT未対応。 • 1プロセス1リクエスト。 ◦ 複数リクエストはタイミングによって壊れる • 表示されているプログラムは抜粋してます
  7. • RFC8446 ◦ https://datatracker.ietf.org/doc/html/rfc8446 • TLS 暗号設定 ガイドライン ◦ https://www.ipa.go.jp/security/crypto/guideline/gmcbt80000005ufv-att/ipa-cryptrec-gl-3001-3.1.1.pdf

    • エムスリーテックブック8 ◦ https://techbookfest.org/product/b94hFWewG7fVRLgqEmSjT1?productVariantID=dWQKDmPPqwY 3dfr28X7j4L • SSL/TLS実践入門 ◦ https://gihyo.jp/book/2024/978-4-297-14178-3 • pizzacatさんのtails(HaskellのTLS実装) ◦ https://github.com/pizzacat83/tails • nsfisisさんのphpcon-kagawa-2025 ◦ https://github.com/nsfisis/nil.ninja/tree/main/vhosts/t/phpcon-kagawa-2025 • ichikawaさんの迂闊にTLS/SSLをPHPで実装してみたら最高だった件 ◦ https://blog.ichikaway.com/entry/20240801/ore-no-tls 参考
  8. • TCPソケットを作成し、リクエストを受け取る • リクエストが Handshake or ApplicationDataを判定 • (Handshakeの場合) ◦

    ClientHelloの内容を読み取る ◦ ServerHello 作成 → そのままレスポンスとして返す ◦ EncryptedExtensions 作成 → 暗号化してレスポンスとして返す ◦ … ◦ Finished 作成 → 暗号化してレスポンスとして返す • (ApplicationDataの場合) ◦ 復号化する ◦ リクエストの内容に応じたレスポンスを返す 流れ
  9. • TCPソケットを作成し、リクエストを受け取る • リクエストが Handshake or ApplicationDataを判定 • (Handshakeの場合) ◦

    ClientHelloの内容を読み取る ◦ ServerHello 作成 → そのままレスポンスとして返す ◦ EncryptedExtensions 作成 → 暗号化してレスポンスとして返す ◦ … ◦ Finished 作成 → 暗号化してレスポンスとして返す • (ApplicationDataの場合) ◦ 復号化する ◦ リクエストの内容に応じたレスポンスを返す 最初の一歩
  10. • TCPソケットを作成し、リクエストを受け取る • リクエストが Handshake or ApplicationDataを判定 • (Handshakeの場合) ◦

    ClientHelloの内容を読み取る ◦ ServerHello 作成 → そのままレスポンスとして返す ◦ EncryptedExtensions 作成 → 暗号化してレスポンスとして返す ◦ … ◦ Finished 作成 → 暗号化してレスポンスとして返す • (ApplicationDataの場合) ◦ 復号化する ◦ リクエストの内容に応じたレスポンスを返す 流れ
  11. $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_bind($socket, '0.0.0.0', 443); socket_listen($socket, 5);

    $sock = socket_accept($socket); $chunk = ''; :/ $chunk にリクエストが格納される $bytes = socket_recv($sock, $chunk, 8192, 0);
  12. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … このbinaryから意味を見出していく リクエストのParse
  13. ClientHelloのフォーマット(RFC8446で設定) 固定値 (0x16) (Handshake) 固定値(0x0301 or 0x0303) TLSRecordの長さ 固定値 (0x01)

    (ClientHello) Handshakeの長さ 固定値(0x0303) ランダム値 対応できる暗号スイート一覧 固定値(0x00) 拡張 レガシーセッションID
  14. ClientHelloのフォーマット(RFC8446で設定) 固定値 (0x16) (Handshake) 固定値(0x0301 or 0x0303) TLSRecordの長さ 固定値 (0x01)

    (ClientHello) Handshakeの長さ 固定値(0x0303) ランダム値 対応できる暗号スイート一覧 固定値(0x00) 拡張 レガシーセッションID 対応できる署名アルゴリズム 鍵交換のパーツ
  15. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  16. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] この通信は Handshake(0x16 = 22)
  17. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] 固定値(0x0301 or 0x0303)
  18. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] TLSPlaintext長さは 1714(0x06b2)
  19. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] Handshakeの中身
  20. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  21. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] ClientHello(1)
  22. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] Handshakeの長さ 1710(0x0006ae)
  23. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] ClientHelloの中身
  24. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  25. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  26. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  27. 16 03 01 06 b2 01 00 06 ae 03

    03 a6 c0 23 df 11 … 省略するが流れは同じ リクエストのParse 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  28. ... $chunk = ''; :/ $chunk にリクエストが格納される $bytes = socket_recv($sock,

    $chunk, 8192, 0); :/ リクエストをParse $tlsRecord = TlsRecord::from($chunkHex); ここから補助スライド
  29. ... $chunk = ''; :/ $chunk にリクエストが格納される $bytes = socket_recv($sock,

    $chunk, 8192, 0); :/ リクエストをParse $tlsRecord = TlsRecord::from($chunkHex); TLS通信における データの送受信単位
  30. final readonly class TlsRecord { public function :_construct( public TlsPlaintext|TlsCiphertext|HexString

    $element, ) {} public static function from(HexString $hex): self { $contentType = ContentType::fromHex($hex:>sub(0, 1)); $_legacyRecordVersion = ProtocolVersion::fromHex($hex:>sub(1, 2)); $_length = HexLength::from($hex:>sub(3, 2)); $element = match ($contentType) { ContentType::Handshake :> TlsPlaintext::new($contentType, Handshake::from($hex:>sub(5))), ContentType::ApplicationData :> TlsCiphertext::new($hex:>sub(5)), default :> $hex:>sub(5), }; return new self( element: $element, ); } }
  31. final readonly class TlsRecord { public function :_construct( public TlsPlaintext|TlsCiphertext|HexString

    $element, ) {} public static function from(HexString $hex): self { $contentType = ContentType::fromHex($hex:>sub(0, 1)); $_legacyRecordVersion = ProtocolVersion::fromHex($hex:>sub(1, 2)); $_length = HexLength::from($hex:>sub(3, 2)); $element = match ($contentType) { ContentType::Handshake :> TlsPlaintext::new($contentType, Handshake::from($hex:>sub(5))), ContentType::ApplicationData :> TlsCiphertext::new($hex:>sub(5)), default :> $hex:>sub(5), }; return new self( element: $element, ); } } 16 03 01 06 b2 01 00 06 ae 03 03 a6 c0 23 df 11 …
  32. final readonly class TlsRecord { public function :_construct( public TlsPlaintext|TlsCiphertext|HexString

    $element, ) {} public static function from(HexString $hex): self { $contentType = ContentType::fromHex($hex:>sub(0, 1)); $_legacyRecordVersion = ProtocolVersion::fromHex($hex:>sub(1, 2)); $_length = HexLength::from($hex:>sub(3, 2)); $element = match ($contentType) { ContentType::Handshake :> TlsPlaintext::new($contentType, Handshake::from($hex:>sub(5))), ContentType::ApplicationData :> TlsCiphertext::new($hex:>sub(5)), default :> $hex:>sub(5), }; return new self( element: $element, ); } } 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] 16 03 01 06 b2 01 00 06 ae 03 03 a6 c0 23 df 11 …
  33. final readonly class TlsRecord { public function :_construct( public TlsPlaintext|TlsCiphertext|HexString

    $element, ) {} public static function from(HexString $hex): self { $contentType = ContentType::fromHex($hex:>sub(0, 1)); $_legacyRecordVersion = ProtocolVersion::fromHex($hex:>sub(1, 2)); $_length = HexLength::from($hex:>sub(3, 2)); $element = match ($contentType) { ContentType::Handshake :> TlsPlaintext::new($contentType, Handshake::from($hex:>sub(5))), ContentType::ApplicationData :> TlsCiphertext::new($hex:>sub(5)), default :> $hex:>sub(5), }; return new self( element: $element, ); } } 16 03 01 06 b2 01 00 06 ae 03 03 a6 c0 23 df 11 … 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] この通信は Handshake(0x16 = 22)
  34. final readonly class TlsRecord { public function :_construct( public TlsPlaintext|TlsCiphertext|HexString

    $element, ) {} public static function from(HexString $hex): self { $contentType = ContentType::fromHex($hex:>sub(0, 1)); $_legacyRecordVersion = ProtocolVersion::fromHex($hex:>sub(1, 2)); $_length = HexLength::from($hex:>sub(3, 2)); $element = match ($contentType) { ContentType::Handshake :> TlsPlaintext::new($contentType, Handshake::from($hex:>sub(5))), ContentType::ApplicationData :> TlsCiphertext::new($hex:>sub(5)), default :> $hex:>sub(5), }; return new self( element: $element, ); } } 16 03 01 06 b2 01 00 06 ae 03 03 a6 c0 23 df 11 … 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] 固定値(0x0301 or 0x0303)
  35. final readonly class TlsRecord { public function :_construct( public TlsPlaintext|TlsCiphertext|HexString

    $element, ) {} public static function from(HexString $hex): self { $contentType = ContentType::fromHex($hex:>sub(0, 1)); $_legacyRecordVersion = ProtocolVersion::fromHex($hex:>sub(1, 2)); $_length = HexLength::from($hex:>sub(3, 2)); $element = match ($contentType) { ContentType::Handshake :> TlsPlaintext::new($contentType, Handshake::from($hex:>sub(5))), ContentType::ApplicationData :> TlsCiphertext::new($hex:>sub(5)), default :> $hex:>sub(5), }; return new self( element: $element, ); } } 16 03 01 06 b2 01 00 06 ae 03 03 a6 c0 23 df 11 … 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] TLSPlaintext長さは 1714(0x06b2)
  36. final readonly class TlsRecord { public function :_construct( public TlsPlaintext|TlsCiphertext|HexString

    $element, ) {} public static function from(HexString $hex): self { $contentType = ContentType::fromHex($hex:>sub(0, 1)); $_legacyRecordVersion = ProtocolVersion::fromHex($hex:>sub(1, 2)); $_length = HexLength::from($hex:>sub(3, 2)); $element = match ($contentType) { ContentType::Handshake :> TlsPlaintext::new($contentType, Handshake::from($hex:>sub(5))), ContentType::ApplicationData :> TlsCiphertext::new($hex:>sub(5)), default :> $hex:>sub(5), }; return new self( element: $element, ); } } 16 03 01 06 b2 01 00 06 ae 03 03 a6 c0 23 df 11 … 0x16(22)は Handshake 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  37. final readonly class TlsRecord { public function :_construct( public TlsPlaintext|TlsCiphertext|HexString

    $element, ) {} public static function from(HexString $hex): self { $contentType = ContentType::fromHex($hex:>sub(0, 1)); $_legacyRecordVersion = ProtocolVersion::fromHex($hex:>sub(1, 2)); $_length = HexLength::from($hex:>sub(3, 2)); $element = match ($contentType) { ContentType::Handshake :> TlsPlaintext::new($contentType, Handshake::from($hex:>sub(5))), ContentType::ApplicationData :> TlsCiphertext::new($hex:>sub(5)), default :> $hex:>sub(5), }; return new self( element: $element, ); } } 16 03 01 06 b2 01 00 06 ae 03 03 a6 c0 23 df 11 … 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  38. final readonly class TlsRecord { public function :_construct( public TlsPlaintext|TlsCiphertext|HexString

    $element, ) {} public static function from(HexString $hex): self { $contentType = ContentType::fromHex($hex:>sub(0, 1)); $_legacyRecordVersion = ProtocolVersion::fromHex($hex:>sub(1, 2)); $_length = HexLength::from($hex:>sub(3, 2)); $element = match ($contentType) { ContentType::Handshake :> TlsPlaintext::new($contentType, Handshake::from($hex:>sub(5))), ContentType::ApplicationData :> TlsCiphertext::new($hex:>sub(5)), default :> $hex:>sub(5), }; return new self( element: $element, ); } } ちなみに、暗号化されると全てApplicationDataになる
  39. • TCPソケットを作成し、リクエストを受け取る • リクエストが Handshake or ApplicationDataを判定 • (Handshakeの場合) ◦

    ClientHelloの内容を読み取る ◦ ServerHello 作成 → そのままレスポンスとして返す ◦ EncryptedExtensions 作成 → 暗号化してレスポンスとして返す ◦ … ◦ Finished 作成 → 暗号化してレスポンスとして返す • (ApplicationDataの場合) ◦ 復号化する ◦ リクエストの内容に応じたレスポンスを返す 流れ
  40. • TCPソケットを作成し、リクエストを受け取る • リクエストが Handshake or ApplicationDataを判定 • (Handshakeの場合) ◦

    ClientHelloの内容を読み取る ◦ ServerHello 作成 → そのままレスポンスとして返す ◦ EncryptedExtensions 作成 → 暗号化してレスポンスとして返す ◦ … ◦ Finished 作成 → 暗号化してレスポンスとして返す • (ApplicationDataの場合) ◦ 復号化する ◦ リクエストの内容に応じたレスポンスを返す 流れ
  41. final readonly class Handshake { public function :_construct( public HandshakeType

    $msgType, public HexLength $length, public ClientHello|HandshakeResponder $body, ) {} … } public static function from(HexString $hex): self { $msgType = HandshakeType::fromHex($hex:>sub(0, 1)); $length = HexLength::from($hex:>sub(1, 3)); $body = match ($msgType) { HandshakeType::ClientHello :> ClientHello::from($hex:>sub(4)), default :> throw new \RuntimeException('not implemented'), }; return new self( msgType: $msgType, length: $length, body: $body, ); }
  42. final readonly class Handshake { public function :_construct( public HandshakeType

    $msgType, public HexLength $length, public ClientHello|HandshakeResponder $body, ) {} … } public static function from(HexString $hex): self { $msgType = HandshakeType::fromHex($hex:>sub(0, 1)); $length = HexLength::from($hex:>sub(1, 3)); $body = match ($msgType) { HandshakeType::ClientHello :> ClientHello::from($hex:>sub(4)), default :> throw new \RuntimeException('not implemented'), }; return new self( msgType: $msgType, length: $length, body: $body, ); } 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] 01 00 06 ae 03 03 a6 c0 23 df 11 …
  43. final readonly class Handshake { public function :_construct( public HandshakeType

    $msgType, public HexLength $length, public ClientHello|HandshakeResponder $body, ) {} … } public static function from(HexString $hex): self { $msgType = HandshakeType::fromHex($hex:>sub(0, 1)); $length = HexLength::from($hex:>sub(1, 3)); $body = match ($msgType) { HandshakeType::ClientHello :> ClientHello::from($hex:>sub(4)), default :> throw new \RuntimeException('not implemented'), }; return new self( msgType: $msgType, length: $length, body: $body, ); } 01 00 06 ae 03 03 a6 c0 23 df 11 … 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  44. final readonly class Handshake { public function :_construct( public HandshakeType

    $msgType, public HexLength $length, public ClientHello|HandshakeResponder $body, ) {} … } public static function from(HexString $hex): self { $msgType = HandshakeType::fromHex($hex:>sub(0, 1)); $length = HexLength::from($hex:>sub(1, 3)); $body = match ($msgType) { HandshakeType::ClientHello :> ClientHello::from($hex:>sub(4)), default :> throw new \RuntimeException('not implemented'), }; return new self( msgType: $msgType, length: $length, body: $body, ); } 01 00 06 ae 03 03 a6 c0 23 df 11 … 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  45. final readonly class Handshake { public function :_construct( public HandshakeType

    $msgType, public HexLength $length, public ClientHello|HandshakeResponder $body, ) {} … } public static function from(HexString $hex): self { $msgType = HandshakeType::fromHex($hex:>sub(0, 1)); $length = HexLength::from($hex:>sub(1, 3)); $body = match ($msgType) { HandshakeType::ClientHello :> ClientHello::from($hex:>sub(4)), default :> throw new \RuntimeException('not implemented'), }; return new self( msgType: $msgType, length: $length, body: $body, ); } 01 00 06 ae 03 03 a6 c0 23 df 11 … 0x01(1)は ClientHello 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  46. final readonly class Handshake { public function :_construct( public HandshakeType

    $msgType, public HexLength $length, public ClientHello|HandshakeResponder $body, ) {} … } public static function from(HexString $hex): self { $msgType = HandshakeType::fromHex($hex:>sub(0, 1)); $length = HexLength::from($hex:>sub(1, 3)); $body = match ($msgType) { HandshakeType::ClientHello :> ClientHello::from($hex:>sub(4)), default :> throw new \RuntimeException('not implemented'), }; return new self( msgType: $msgType, length: $length, body: $body, ); } 01 00 06 ae 03 03 a6 c0 23 df 11 … 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446]
  47. final readonly class Handshake { public function :_construct( public HandshakeType

    $msgType, public HexLength $length, public ClientHello|HandshakeResponder $body, ) {} … } public static function from(HexString $hex): self { $msgType = HandshakeType::fromHex($hex:>sub(0, 1)); $length = HexLength::from($hex:>sub(1, 3)); $body = match ($msgType) { HandshakeType::ClientHello :> ClientHello::from($hex:>sub(4)), default :> throw new \RuntimeException('not implemented'), }; return new self( msgType: $msgType, length: $length, body: $body, ); } 01 00 06 ae 03 03 a6 c0 23 df 11 … 引用:RFC8446 [https://datatracker.ietf.org/doc/html/rfc8446] 省略しますが、流れは同じです。 ここまで補助スライド
  48. EncryptedExtensions(Client ← Server) ServerHelloの追加情報 • ServerHelloで送信したExtension以外 ◦ key_share ◦ pre_shared_key

    ◦ supported_versions EncryptedExtensions Client (お互い共通鍵を計算できるし、ここから暗号化して送るか) これ補足事項ね。 Server
  49. EncryptedExtensions(Client ← Server) ServerHelloの追加情報 • ServerHelloで送信したExtension以外 ◦ key_share ◦ pre_shared_key

    ◦ supported_versions EncryptedExtensions Client (お互い共通鍵を計算 できるし、ここから暗号化して送るか) これ補足事項ね。 Server
  50. stringの扱い方 16 03 01 06 b2 01 00 06 "�"

    var_dump() リクエストで 受け取るデータ
  51. stringの扱い方 16 03 01 06 b2 01 00 06 "�"

    var_dump() リクエストで 受け取るデータ 😭
  52. stringの扱い方 0b00010110 0b00000011 0b00000001 0b00000110 0b10110010 0b00000001 0b00000000 0b00000110 "�"

    var_dump() 0b00110001 0b00110110 0b00110000 0b00110011 0b00110000 0b00110001 0b00110000 0b00110110 0b01100010 0b00110010 0b00110000 0b00110001 0b00110000 0b00110000 0b00110000 0b00110110 bin2hex()
  53. stringの扱い方 0b00010110 0b00000011 0b00000001 0b00000110 0b10110010 0b00000001 0b00000000 0b00000110 "�"

    var_dump() 0b00110001 0b00110110 0b00110000 0b00110011 0b00110000 0b00110001 0b00110000 0b00110110 0b01100010 0b00110010 0b00110000 0b00110001 0b00110000 0b00110000 0b00110000 0b00110110 bin2hex() "16030106b2010006" var_dump()
  54. stringの扱い方 0b00010110 0b00000011 0b00000001 0b00000110 0b10110010 0b00000001 0b00000000 0b00000110 "�"

    var_dump() 0b00110001 0b00110110 0b00110000 0b00110011 0b00110000 0b00110001 0b00110000 0b00110110 0b01100010 0b00110010 0b00110000 0b00110001 0b00110000 0b00110000 0b00110000 0b00110110 bin2hex() "16030106b2010006" var_dump() ☺
  55. stringの扱い方 0b00010110 0b00000011 0b00000001 0b00000110 0b10110010 0b00000001 0b00000000 0b00000110 "�"

    var_dump() 0b00110001 0b00110110 0b00110000 0b00110011 0b00110000 0b00110001 0b00110000 0b00110110 0b01100010 0b00110010 0b00110000 0b00110001 0b00110000 0b00110000 0b00110000 0b00110110 bin2hex() "16030106b2010006" var_dump() 全部string型 😳
  56. クラス分け 0b00110001 0b00110110 0b00110000 0b00110011 0b00110000 0b00110001 0b00110000 0b00110110 0b01100010

    0b00110010 0b00110000 0b00110001 0b00110000 0b00110000 0b00110000 0b00110110 "16030106b2010006" 0b00010110 0b00000011 0b00000001 0b00000110 0b10110010 0b00000001 0b00000000 0b00000110 "�" string HexString
  57. $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_bind($socket, $this:>host, $this:>port) socket_listen($socket, 5)

    $sock = socket_accept($socket); $chunk = ''; :/ $chunk にリクエストが格納される $bytes = socket_recv($sock, $chunk, 8192, 0); :/ $chunk を HexString に変換、$chunkHex を Parseしていく $chunkHex = HexString::from($chunk);
  58. ... socket_listen($socket, 5) $sock = socket_accept($socket); $chunk = ''; :/

    $chunk にリクエストが格納される $bytes = socket_recv($sock, $chunk, 8192, 0); :/ $chunk を HexString に変換、$chunkHex を Parseしていく $chunkHex = HexString::from($chunk);
  59. ... $chunk = ''; :/ $chunk にリクエストが格納される $bytes = socket_recv($sock,

    $chunk, 8192, 0); :/ $chunk を HexString に変換、$chunkHex を Parseしていく $chunkHex = HexString::from($chunk);
  60. ... :/ $chunk を HexString に変換、$chunkHex を Parseしていく $chunkHex =

    HexString::from($chunk); :/ リクエストをParse $tlsRecord = TlsRecord::from($chunkHex);