Slide 1

Slide 1 text

長谷川智希 𝕏 @tomzoh 2026/04/11 PHPカンファレンス小田原2026 デシリアライゼーションを理解する

Slide 2

Slide 2 text

௕୩઒ஐر ͸͕ͤΘ ͱ΋͖ @tomzoh http://www.dgcircus.com デジタルサーカス株式会社 ॴଐ ٕज़ΧϯϑΝϨϯεओ࠻ دߘɾஶॻ 来たれ!PHPer!We are hiring! 𝕏

Slide 3

Slide 3 text

௕୩઒ஐر ͸͕ͤΘ ͱ΋͖ @tomzoh ٕज़ΧϯϑΝϨϯεӡӦࢀՃ ֤छϓϩάϥϜ։ൃ   $16 ϨτϩήʔϜػ ిࢠ޻࡞  Ϗʔϧ αοΧʔ؍ઓ ϨϯλϧΧʔτϨʔε  ΩϟϯϐϯάΧʔ ๅ௩؍ܶʜ ϥΠϑϫʔΫ 𝕏

Slide 4

Slide 4 text

長谷川智希 @tomzoh デシリアライゼーションを理解する 4 デシリアライゼーションを理解する

Slide 5

Slide 5 text

長谷川智希 @tomzoh デシリアライゼーションを理解する React2Shell • 2025年12月に発見された超絶インパクトの脆弱性 • React + Next.js の React Server Components の脆弱性 • 認証されていない攻撃者がHTTPリクエストを1つ送信するだけで任意コード実行できる • React Server Components • 通常のWebアプリでは /api/update-user みたいなAPIエンドポイントを作ってフロントエ ンドからコールする • Next.jsではプログラマが定義した関数に対応する内部APIエンドポイントが自動で用意され る機能がある • function updateUser(formData){} 的にサーバで定義しておき、 フロントエンドで 的にする • この処理に脆弱性があった • この脆弱性は安全でないデシリアライゼーションに分類される 5

Slide 6

Slide 6 text

長谷川智希 @tomzoh デシリアライゼーションを理解する OWASP Top 10 2017 A1: 2017-Injection A2: 2017-Broken Authentication A3: 2017-Sensitive Data Exposure A4: 2017-XML External Entities (XXE) A5: 2017-Broken Access Control A6: 2017-Security Miscon fi guration A7: 2017-Cross-Site Scripting (XSS) A8: 2017-Insecure Deserialization A9: 2017-Using Components with Known Vulnerabilities A10: 2017-Insuf fi cient Logging & Monitoring 6 安全でないデシリアライゼーション CSRF SQLインジェクション XSS

Slide 7

Slide 7 text

長谷川智希 @tomzoh デシリアライゼーションを理解する PHPでのデシリアライゼーション 7

Slide 8

Slide 8 text

長谷川智希 @tomzoh デシリアライゼーションを理解する PHPの serialize() と unserialize() 8 オブジェクトをDBやファイルに保存できて便利

Slide 9

Slide 9 text

長谷川智希 @tomzoh デシリアライゼーションを理解する PHPまわりで使われているシリアライズ • PHPのSESSION user_id|i:1042;username|s:6:"tanaka";role|s:5:"admin";login_time|i:1744185600; • LaravelでQueueを使う時のjobsテーブルのpayload {"uuid":"d6062a83-cfe3-4f13-b013-701940371442","displayName":"App\\Jobs\ \FooBarJob","job":"Illuminate\\Queue\ \CallQueuedHandler@call","maxTries":null,"maxExceptions":null,"failOnTimeout":fa lse,"backoff":null,"timeout":14400,"retryUntil":null,"data":{"commandName":"App\ \Jobs\\FooBarJob","command":"O:18:\"App\\Jobs\\FooBar\":1: {s:10:\"\u0000*\u0000queueId\";i:5963;}"}} • WordPressのwp_optionsテーブルのoption_value a:2:{i:0;s:18:"pagespeed-insights";i:1;s:11:"analytics-4";} 9

Slide 10

Slide 10 text

長谷川智希 @tomzoh デシリアライゼーションを理解する 脆弱性への道 • unserialize() では新しいクラスやメソッドは作れないが 既存のクラスのインスタンスは作れる • 脆弱性のあるクラスのインスタンスをいい感じのプロパティで作ってやる • PHPには特定のタイミングで自動実行されるメソッドがある • __unserialize() / __wakeup() • __destruct() • __toString() • 自動実行されるメソッドで狙った効果を発動させる 10

Slide 11

Slide 11 text

長谷川智希 @tomzoh デシリアライゼーションを理解する 脆弱性のデモ 11

Slide 12

Slide 12 text

長谷川智希 @tomzoh デシリアライゼーションを理解する 何が起きたか • ToDoアプリ • データはファイルに保存 • テキストでエクスポート/インポートできる • 悪意ある文字列をインポート • httpでリクエスト可能な場所にファイルが生成された 12

Slide 13

Slide 13 text

長谷川智希 @tomzoh デシリアライゼーションを理解する TodoListクラス 基本設計 13

Slide 14

Slide 14 text

長谷川智希 @tomzoh デシリアライゼーションを理解する TodoListクラス export/import 14

Slide 15

Slide 15 text

長谷川智希 @tomzoh デシリアライゼーションを理解する import.php 15

Slide 16

Slide 16 text

長谷川智希 @tomzoh デシリアライゼーションを理解する LogWriterクラス 16

Slide 17

Slide 17 text

長谷川智希 @tomzoh デシリアライゼーションを理解する ここまでで脆弱性のパーツは揃いました 17 何をどうやって攻撃するか、わかりました…?

Slide 18

Slide 18 text

長谷川智希 @tomzoh デシリアライゼーションを理解する 攻撃ペイロード 18 O:9:"LogWriter":2:{ s:4:"path";s:28:"/var/www/html/data/ shell.php";s:7:"content";s:30:"";} 禍々しい文字列が見えるけど… 整形すると…

Slide 19

Slide 19 text

長谷川智希 @tomzoh デシリアライゼーションを理解する 攻撃ペイロード 19 O:9:"LogWriter":2:{ s:4:"path";s:28:"/var/www/html/data/shell.php"; s:7:"content";s:30:""; } LogWriterクラスをプロパティ付きでシリアライズしたもの ▼./src/data/shell.php

Slide 20

Slide 20 text

長谷川智希 @tomzoh デシリアライゼーションを理解する LogWriter の __destruct() 20 脆弱なクラスのインスタンスを攻撃者の指定したプロパティで デシリアライズしてしまった = 安全でないデシリアライゼーション

Slide 21

Slide 21 text

長谷川智希 @tomzoh デシリアライゼーションを理解する 安全でないデシリアライゼーション 破壊力絶大の脆弱性 • 一撃で任意コード実行に行き着くことが多く破壊力の大きな脆弱性になりがち • 脆弱なクラス(ガジェット / gadget)と組み合わせて攻撃に使われる • デモの場合 LogWriter がガジェット • TodoList をデシリアライズすることを意図した処理に LogWriter をデシリアライズさせていた 21

Slide 22

Slide 22 text

長谷川智希 @tomzoh デシリアライゼーションを理解する どうしたら良かった…? • システム外部から来たデータを信じない • 聞き覚えが…? • コンピュータシステムのセキュリティ問題、だいたいこれ • XSS, CSRF, SQLインジェクション, … • 本当に serialize() / unserialize() でないといけないか (= オブジェクトとしてシリ アライズする必要があるか) をよく考える • ToDoの配列を json_encode() / json_decode() で良くない? • 我々が書くようなWebアプリの世界では serialize() / unserialize() でないといけな いケースは無いと言って過言では無いのでは • 「こういう時に必要だったんだよね」という体験談ある方ぜひ聞かせて! 22

Slide 23

Slide 23 text

長谷川智希 @tomzoh デシリアライゼーションを理解する serialize() の allowed_classes オプション 23

Slide 24

Slide 24 text

長谷川智希 @tomzoh デシリアライゼーションを理解する 24 あ、はい… これ、どういうケースを想定してるんだろ? 「今安全でもそのうちおまえらどうせやらかすだろ」ということ…? 今回のケースだと、もともとプロパティもユーザ入力の信頼できない値なので unserialize() で受け入れて良い気がするんだけどな〜 意図がわかる方おしえてください…!

Slide 25

Slide 25 text

長谷川智希 @tomzoh デシリアライゼーションを理解する 余談: unserialize()できなかったオブジェクト • allowed_classes に書かれていないクラスをアンシリアライズするとどうなるか • __PHP_Incomplete_Class というクラスのインスタンスができる object(__PHP_Incomplete_Class)#1 (3){ ["__PHP_Incomplete_Class_Name"]=> string(9) "LogWriter" ["path"]=> string(28) "/var/www/html/data/shell.php" ["content"]=> string(30) "" } • プロパティに触ろうとするとWarningになりNULLが返ってくる Warning: TodoList::import(): The script tried to access a property on an incomplete object. Please ensure that the class definition "LogWriter" of the object you are trying to operate on was loaded _before_ unserialize() gets called or provide an autoloader to load the class definition in /var/www/html/classes/TodoList.php on line 63 25

Slide 26

Slide 26 text

長谷川智希 @tomzoh デシリアライゼーションを理解する まとめ 26

Slide 27

Slide 27 text

長谷川智希 @tomzoh デシリアライゼーションを理解する まとめ • 安全でないデシリアライゼーションは重大な脆弱性を引き起こす • 特に外部システムからの入力をデシリアライズするのはとても危険 • PHPでオブジェクトをシリアライズしたくなったら serialize() を使う前に json_encode() ではダメかを考えると良い • ほぼすべてのケースで json_encode() で良いはず • unserialize() を使う場合も allowed_classes オプションでクラスを指定する • 指定クラス以外を unserialize() すると普通でないクラスができあがるのでそれを考慮 に入れる • 安全に使えば serialize() / unserialize() は割と便利 • うまく付き合っていきましょう 27

Slide 28

Slide 28 text

長谷川智希 @tomzoh デシリアライゼーションを理解する 28 デシリアライズ 適材適所で ご安全に

Slide 29

Slide 29 text

長谷川智希 @tomzoh デシリアライゼーションを理解する まとめ • 安全でないデシリアライゼーションは重大な脆弱性を引き起こす • 特に外部システムからの入力をデシリアライズするのはとても危険 • PHPでオブジェクトをシリアライズしたくなったら serialize() を使う前に json_encode() ではダメかを考えると良い • ほぼすべてのケースで json_encode() で良いはず • unserialize() を使う場合も allowed_classes オプションでクラスを指定する • 指定クラス以外を unserialize() すると普通でないクラスができあがるのでそれを考慮 に入れる • 安全に使えば serialize() / unserialize() は割と便利 • うまく付き合っていきましょう 29

Slide 30

Slide 30 text

長谷川智希 @tomzoh デシリアライゼーションを理解する おまけ 30

Slide 31

Slide 31 text

長谷川智希 @tomzoh デシリアライゼーションを理解する デシリアライゼーション…? • 保存・送信に便利な形式(文字列やバイト列)に変換されたデータを、元のオブジェクトや データ構造に戻す処理 • シリアライズ ⁵ デシリアライズ • エンコード ⁵ デコード と似てる • Eメールは7bit ASCIIしか通せない • 添付ファイル(バイナリ)をBase64でエンコードして7bit ASCIIにして送信 • 受信した7bit ASCIIのメールをBase64でデコードしてバイナリに戻す • オブジェクトをテキスト形式にしたい場合にシリアライズ/デシリアライズを使う • PHPにはそのものズバリの serialize() / unserialize() 関数がある 31