浜村拓夫(・∀・)作品集

頭の中にあるイメージを表現できるデザイン力が欲しいです(><)

PHPから別言語への変更を検討

最近、PHPの重大な脆弱性が発覚して話題になっていました。

ja.wikipedia.org

PHP2Shell(ピーエイチピー・ツー・シェル)は、プログラミング言語PHPの内部に存在する、2026年10月に公表された、2件の重大な脆弱性の総称である。 共通脆弱性識別子(CVE)ではCVE-2026-109618およびCVE-2026-109619として知られ、特定の条件下で認証を受けていない攻撃者による遠隔コード実行(RCE)を可能にする。 いずれも共通脆弱性評価システム(CVSS)バージョン4.0において深刻度9.2と評価された。

この機会にPHPの利用を見直し、別のプログラミング言語でWebアプリを開発しようと思いました。

以下に、AIで検討した内容を備忘録として残します。
(ChatGPT、Claude、Geminiで検討した内容をClaudeにまとめてもらいました。)

PHPから別言語への変更を検討


PHPの重大な脆弱性をきっかけに、Webアプリのバックエンド言語を考えた(TypeScript・Go・Rust比較メモ)

2026年10月27日時点の備忘録です。 今回は、Webアプリのバックエンド開発における言語選定について考えました。

結論(先に)

項目 判断
当面の最優先 稼働中のPHPを修正版へ更新する(言語移行より先)
バックエンドの第一候補 Go(+PostgreSQL)
Rust 本番の移行先とは決めず、並行して学習して、必要に応じて部分的に採用する
TypeScript 有力な選択肢だが、今回はGoのシンプルさとレビューのしやすさを優先する
AIの使い方 AIにコードを書かせつつ、自分で読んで検証できる言語を選ぶ。テストは必須
基本方針 安全性を最優先する

1. きっかけ:PHPの重大な脆弱性

2026年10月21日、PHPの2件の脆弱性(CVE-2026-109618、CVE-2026-109619)に対応した修正版が緊急リリースされました。

  • 深刻度はいずれもCVSS v4.0で9.2(Critical)
  • 認証なしでリモートコード実行(RCE)につながる恐れがある
  • 修正版はPHP 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35
  • 設定による暫定的な緩和策はあるが、完全な対策には更新が必要

今回学んだのは、問題は言語そのものよりも、「修正版をどれだけ速く適用できるか」だということです。

大規模サービスの対応記事では、修正版の公開から約30分でメンテナンスを解除できた事例が紹介されていました。 日頃から迅速に更新できる仕組みを整えていた成果です。

言語を乗り換える前に、まず現在のPHPを更新する。 どの言語を使うにしても、「修正版を迅速に適用できる体制」を先に整える。

2. TypeScript・Go・Rustの比較

比較項目 TypeScript Go Rust
主な特徴 JavaScriptとの互換性、フロントエンドとの型共有 シンプルさと開発生産性 メモリ安全性と制御性
メモリ管理 JSランタイムのGC GC 所有権・借用(GCなし)
データ競合 実行モデルに依存 コンパイル時には防げない(go test -raceで実行時に検出) safe Rustではコンパイル時に防止
学習の難しさ 中程度 比較的易しい 比較的高い
コンパイル 速い 速い フルビルドは遅い(cargo checkは速い)
主な注意点 npmの供給網リスク、型は実行時の検証にならない データ競合、GC・ランタイムのバグ 学習・実装の複雑さ

共通する点もあります。

  • GoとRustは、米国のNSAやCISAがメモリ安全な言語として挙げている。
  • どの言語も、アプリ全体の安全性を保証するものではない。言語やライブラリの更新を継続する必要がある。

ja.wikipedia.org

ja.wikipedia.org

ja.wikipedia.org

ja.wikipedia.org

3. 安全第一なら、Rustを選ぶべきか?

Rustのメモリ安全性は魅力的です。 ただし、Rustを選べば自動的に安全になるわけではありません。

次のような問題は、言語に関係なく発生します。

  • SQLインジェクション、XSS
  • 認証・認可の不備
  • 決済処理の二重実行、データベースの不整合
  • 依存ライブラリへの供給網攻撃
  • 設定ミス、バックアップ・復旧体制の不備

Rustのコンパイラが検出できるのは、特定の種類の問題です。 unsafeや依存クレートについても注意が必要です。

安全性を最優先するなら、言語選択だけでなく、テスト・監視・更新・バックアップを含めた運用体制が重要だと考えました。

4. なぜGoを第一候補にしたのか?

理由は主に4つあります。

  1. 言語が比較的シンプル:AIが生成したコードを、人間も読みやすく、レビューしやすいと期待できる。
  2. 標準ライブラリが充実している:HTTP、JSON、暗号、並行処理を標準で扱えるため、依存ライブラリを少なくできる。
  3. 長期保守を期待できる:GoogleとOSSコミュニティが開発を続けている。Go 1には互換性維持の方針がある。(ただし、互換性はソースコードレベルのもので、セキュリティ修正などの例外もある)
  4. 既存の知識を活かせる:HTTP、SQL、業務ロジック、トランザクションに関する経験をそのまま活用できる。

TypeScriptも引き続き有力な選択肢です。 ただ、今回は以上の理由からGoを優先しました。

2026年にnpmの供給網攻撃が相次いだこと(5月・8月に数百規模のパッケージが侵害された事例など)も、検討材料の一つでした。

誤解していたこと

  • 「Googleが作ったから安全」ではない:2026年だけでも、Goの標準ライブラリには多数の修正があり、コンパイラに起因するメモリ破壊のCVE(CVE-2026-27143、CVE-2026-27144)も報告されています。ただし、これらはGCそのものの脆弱性ではありません。GCのゼロデイが発生する確率について、他言語と比較した統計は確認できませんでした。
  • 「Rustなら安全」でもない:前述のとおり、言語だけでアプリ全体の安全性が保証されるわけではありません。
  • 「Rustは低レイヤー専用」ではない:Cloudflareは、HTTPプロキシのPingoraをRustで開発しました。その理由として、C(NGINX)のメモリ安全性の問題と、Luaに静的型がないことを挙げています。ただし、これは超大規模なネットワークサービスの事例であり、一般的なCRUDアプリにそのまま当てはまるとは限りません。

5. 人間から見た視点とAIから見た視点

視点 Go Rust
人間・長所 短時間で読みやすく、レビューしやすい 重要な性質をコンパイラが保証してくれる
人間・短所 並行処理やnilに関する誤りは、自分で確認する必要がある 習得が難しく、借用エラーで手が止まることがある
AI・長所 文法がシンプルで、修正と検証を繰り返しやすい 構造化されたエラーを自己修正に活用しやすい
AI・短所 メモリ破壊やデータ競合はコンパイルだけでは見つからない 借用エラーを解消するために、不要なunsafe・.clone()・.unwrap()を使うことがある(頻度は条件による)

AIがどちらの言語をよりうまく書けるのかについて、直接比較した信頼できる数値は確認できませんでした。

また、「コンパイルが通った」ことは、仕様が正しいことを意味しません。

6. 構成と既存システムの扱い

領域 候補
バックエンド Go(標準のnet/httpを中心に使用)
データベース PostgreSQL(ドライバーはpgx)
SQL sqlc(SQLから型付きコードを生成)
フロントエンド HTML+必要に応じてTypeScript
ホスティング Linux VPSなど

構成を考えるうえで、注意したい点もあります。

  • sqlcは、SQLの論理的な誤りやN+1クエリを自動的に防いでくれるものではありません。
  • 既存システムのDBがMySQLなら、言語とDBを同時に移行しない方針です。まずGo+MySQLへ移行し、DBの移行は別の段階で行うことで、障害の原因を切り分けやすくなります。

7. Go採用後に気をつけること

一般的なGoアプリは、標準ライブラリとランタイムを実行ファイルに含めて配布します。

そのため、Go本体のセキュリティ修正を反映するには、原則として再ビルド・再デプロイが必要です。 cgoなどで外部ライブラリに依存する場合は、その部分も別途更新する必要があります。

PHPのように、OSのパッケージ更新だけで済むわけではありません。

例えば、2026年10月19日のGo 1.27.3では、特定の認証局の中間証明書を信頼しない修正が行われました。 古いGoでビルドしたままのアプリには、この修正が反映されません。

運用面では、次の点を意識します。

  • Goのセキュリティ修正は、golang-announceで事前告知されるため、購読しておく
  • 各メジャーリリースは、新しいリリースが2つ出るまでサポートされるため、おおむね年1回の更新を見込む
  • CIに次のチェックを組み込む
go vet ./...
go test ./...
go test -race ./...
govulncheck ./...

ただし、go test -raceで検出できるのは、実際に実行された経路で発生したデータ競合だけです。 テストが通ったからといって、安全性が証明されたわけではありません。

また、依存ライブラリはできるだけ少なくします。 Goのモジュールにも、偽パッケージやチェックサム検証回避のCVEなど、供給網に関する問題があります。

8. 課金を伴うサービスで事故を減らすには

課金を伴うサービスでは、言語選び以上に重要なのが、業務上の事故を防ぐ設計です。

想定される事故 対策
二重請求 課金APIの冪等性(同じ要求を繰り返しても処理は一度だけ行う)
支払い済みなのに商品が提供されない 状態管理、照合・再処理
他人の購入履歴が見える 利用者ごとのアクセス制御テスト
決済通知の偽装・重複 Webhookの署名検証、重複通知を前提とした実装
データが消える バックアップと復元テスト
決済ページの改ざん 脆弱性対策、管理者権限の管理

カード情報については、決済代行サービスのホスト型画面やトークン方式を利用し、自分のサーバーを経由させないことを基本とします。

ただし、カード情報の非保持化を達成した加盟店でも、サイトの改ざんなどによる情報漏えいが主流になっているという業界の警告があります。

そのため、基本的な脆弱性対策は欠かせません。

9. AIの活用方針

AIコーディング

AIにはコードを書かせ、コンパイラとテストで検証します。

人間は型に関する細かな問題だけにとらわれず、認可、決済の整合性、エラー処理、異常系のテストを重点的に確認します。

「コンパイルが通った」「go vetが通った」というだけでは、他人の注文情報を返してしまうようなバグは見つかりません。

AIへの指示は、例えば次のようにします。

この機能について、正常系だけでなく、認可漏れ、重複リクエスト、同時実行、
DB障害、タイムアウト、外部APIの再試行をテストしてください。
既存の業務仕様と矛盾する実装はしないでください。
生成したコードを、既存のPHPコードと対比して説明してください。

AIを使って言語を学ぶ

学習は、次の流れで進めます。

  1. AIにコードを書かせる
  2. コンパイル・テストで確認する
  3. AIに「なぜそう書くのか」を解説させ、自分でも理解する
  4. 小さな機能から始め、段階的に範囲を広げる

Rustを学ぶ場合は、AIが不必要なunsafe・.clone()・.unwrap()を使っていないかも確認します。

10. Rustの位置づけと将来の移植

Rustは、現時点では本番環境の移行先と決めず、小さな実験を通じて並行して学びます。

部分的な採用を検討するのは、性能・遅延・メモリに関する課題や、重要な部品でデータ競合をコンパイル時に排除したいといった、具体的な必要性が生じたときです。

また、年に1回程度、次の基準で選択を見直します。

  • エコシステムの状況
  • セキュリティ修正の実績
  • 自分でコードを読んで理解できるか
  • 移行にかかるコスト

GoとRustの相互移植は、どちらの方向でも可能です。

  • Go→Rustでは、所有権・借用を考慮した設計が必要になり、実装の難易度が上がる場合がある
  • Rust→Goでは、Rustの型システムが保証していた性質を、テストやレース検出など、別の手段で補う必要がある

実際の移行事例もあります。

Discordは、GCに起因するレイテンシの急上昇を理由に、1つのサービスをGoからRustへ書き換えました。

また、GitHubは、AIエージェントを利用してCopilotのランタイムをTypeScriptからRustへ移行したと報告しています。ただし、どちらも当事者による説明です。

将来の移植に備えるうえでは、次の準備が役立ちます。

  • 言語に依存しないAPIの契約テストを先に作る。
  • Goで実装する段階からグローバル変数を排除し、状態を構造体に閉じ込める。
  • 切り替えは段階的に進め、ロールバックできるようにする。
  • 読み取り専用APIでは、新旧の応答を比較できるようにする。ただし、決済やデータ更新など、副作用のある処理は新旧の両方で実行しない。二重請求や二重注文につながる恐れがあるためです。

11. 移行ロードマップ

段階 内容
0(すぐ) 稼働中のPHPを修正版へ更新する
1 Goの基礎を学ぶ(HTTP、context、エラー処理、goroutine)
2 Go+PostgreSQLで、小さな新規アプリを一つ完成させる
3 認証・認可、決済、バックアップ、監視、CIを整える
4 既存アプリの小さな機能を、契約テストを使いながら移植する
並行 Rustを小さな実験を通じて学び、必要に応じて部分採用を判断する

12. 最終的な方針

今回の検討を踏まえ、次の方針にしました。

  • 新規Webアプリのバックエンドは、Go+PostgreSQLを第一候補にする
  • 既存のPHPアプリは、まず更新して安全に保守しながら、段階的な移行を検討する
  • AIコーディングを活用するが、自分で理解・検証できる範囲で使う
  • Rustは並行して学び、必要に応じて採用する
  • 何よりも安全性と長期的な保守のしやすさを優先する

「理論上、最も安全な言語」を探すのではなく、自分で読んで検証でき、更新し続けられる技術を選ぶ。

今回の結論は、そこに落ち着きました。

この記事の限界

  • Goのガベージコレクションに起因するゼロデイ攻撃の発生率について、他言語と比較した統計は確認できていません。
  • AIの言語ごとの得意・不得意について、直接比較した信頼できる数値は確認できていません。
  • 企業の移行事例は、当事者による説明が中心です。
  • 決済や個人情報の取り扱いについては、法令と各サービスの規約を必ず確認してください。
  • Webサーバーソフト(リバースプロキシ)の選定は別のテーマとし、今回は扱っていません。

参考リンク