Envflowの構築:LaravelとUpstash Redisで環境変数を安全に共有する方法
最近、Nuno Maduro氏のプロジェクト「Pyre」に大きな刺激を受けました。Pyreは、短命の秘密メッセージを誰にでも送信できるWebアプリです。メッセージは保存時に暗号化され、復号リンクを持つ本人か共有相手だけが内容を読める仕組みになっています。
私はUpstash Redisを使って似たようなサービスを作りたいと考え、テーマとして「環境変数の共有」というニッチな領域を選びました。チームメイトと機密情報を共有しなければならず、「これを安全に行えるシステムをどう作ればいいのか?」と悩んだ経験は、誰にでも一度はあるのではないでしょうか。
そこでご紹介するのが「Envflow」です。私が構築したLaravelサンプルプロジェクトで、暗号化データの保存と共有のためにUpstash Redisをプライマリデータベースとして採用しています。
この記事では、退屈な細部は省きながら、このプロジェクトをゼロから完成まで構築した過程を解説します。
技術スタック
まずPHPを採用し、定番のTALLスタックでプロジェクトを構築することにしました。
- Tailwind CSS
- Alpine JS
- Laravel
- Livewire
- Upstash Redis
データベース
今回必要だったのは、一定時間後に自動的に期限切れとなる暗号化データを、高速かつコスト効率よく保存できる場所です。お察しの通り、Upstash Redisを選びました。
Upstash RedisはサーバーレスかつRedis互換のデータベースであり、このユースケースにはまさにうってつけでした。
ルート/エンドポイント
Envflowは3つの主要ルートと、プロジェクトの概要を説明する補助ルートで構成されています。これらのルートはroutes/web.phpファイルで定義されています。
ホームページ
ホームページはプロジェクトのランディングページとしての役割に加え、環境変数を暗号化できるLivewireフォームを備えています。このページでは、環境変数を復号できる回数と有効期間を設定できます。

Livewireページコンポーネントはapp/Livewire/EncryptEnvPage.phpに、対応するテンプレートはresources/views/livewire/encrypt-env-page.blade.phpにあります。
成功ページ
成功ページはシンプルな画面で、復号リンクを共有する2つの方法を表示します。1つは復号キーを含めた形、もう1つは復号キーをリンクから切り離した形です。

コンポーネントはapp/Livewire/SuccessPage.php、テンプレートはresources/views/livewire/success-page.blade.phpにあります。
復号ページ
復号の魔法がすべて起こるのがこのページです。暗号化キーはUpstash Redisデータベースに保存せず、意味不明な文字列となった暗号化済みの値だけを保存していることを思い出してください。復号ページの主な役割は、受け取った復号キーを使って環境変数の復号を試みることです。

コンポーネントはapp/Livewire/DecryptEnvPage.php、テンプレートはresources/views/livewire/decrypt-env-page.blade.phpにあります。
Actions(アクション)
このプロジェクトでは、ビジネスロジックの主要部分を整理するためにAction/Executorパターンを採用しました。これにより、コードが成長しても再利用性・テスト容易性・保守性が保たれ、将来的なAPIなど、アプリケーションの異なる部分間で同じ振る舞いを共有することも可能になります。
Actionsはapp/Actions配下にあります。中でも、環境変数の暗号化と復号を担う中核コンポーネントであるEncryptアクションとDecryptアクションに注目してみてください。
暗号化
Laravelには組み込みの暗号化ヘルパーがあり幸運でしたが、今回は実行時に生成されるカスタム暗号化キーを使いたかったため、少し異なるアプローチが必要でした。
調べた結果、良い方法が見つかりました。Laravelの基盤クラスIlluminate\Encryption\Encrypterを直接利用できるのです。
use Illuminate\Encryption\Encrypter;
$encrypter = new Encrypter(
'custom-encryption-key', // 実行時に生成したいキー
config('app.cipher'), // Laravelのデフォルト暗号方式を使用
);
// これで値を暗号化できます!
$encryptedValue = $encrypter->encryptString($value);
これで暗号化が実現しました。これまで紹介してきたファイルを確認していれば、このコードがEncryptアクションとDecryptアクションの中に含まれていることに気づいたかもしれません。
ストレージ
ActionとLaravelネイティブのEncrypterクラスによる暗号化・復号の方法は説明しましたが、まだUpstash Redisへの保存方法には触れていませんでした。
その処理はStoreEnvFileアクションに実装されています。特に注目すべきコードがこちらです。
// ...
RedisFacade::pipeline(function (Redis $pipe) use (...) {
$options = ['EX' => $ttl]; // ここで有効期限を設定
$pipe->set("envfile:$id", $encrypted->value, $options);
$pipe->set("envfile:$id:shareLimit", $shareLimit, $options);
});
// ...
上記のコードではRedisパイプラインを使用しており、すべてのコマンドが単一のトランザクションとして実行されることが保証されます。setコマンドが失敗した場合に暗号化済みの値が保存されないようにするため、これは非常に重要なポイントです。
デプロイ
デプロイには、 recently リリースされたLaravel Cloudを使用しました。
Laravel Cloudへのデプロイは驚くほど簡単で、リポジトリから、Redisインスタンスが接続されドメインもプロビジョニング済みの完全なアプリケーションが稼働するまで、1分もかかりませんでした。現在でもデプロイは30秒以内で完了します。🚀

本番環境は https://envflow.laravel.cloud から実際に利用できます。
まとめ
この記事を楽しんでいただき、Envflowの構築プロセスを理解する助けになっていれば嬉しいです。
Upstash Redisをプライマリデータベースとして活用できるアプリケーションは数多くあると考えています。今回RedisはKVストアとして使いましたが、それ以上の可能性を秘めており、今後取り組む別のプロジェクトでそのあたりも詳しく取り上げる予定です。
このプロジェクトのソースコードはGitHubで公開しています。アイデアや改善提案(あるいはバグの発見)があれば、ぜひコードをチェックしてコントリビュートしてみてください。
-
Upstash RedisでLaravelのセッション管理を強化する方法
HTTPはステートレス(状態を持たない)なプロトコルです。つまり、サーバーへの各リクエストは完全に独立しており、以前のリクエストに関する情報は一切保持されません。認証情報やユーザーの設定など、ユーザー固有のデータを複数のリクエストにまたがって保持する必要がある動的なWebアプリケーションを構築する際には、これが大きな課題となります。この制限を克服するために、Webアプリケーションでは「セッション」を利用して、リクエスト間でデータを保存・取得しています。 最も人気のあるPHPフレームワークの一つであるLaravelは、こうしたステートフルな要件に対応できる、柔軟かつ堅牢なセッション管理システムを
-
Grafana向けRedisデータソースプラグインの使い方を実例で解説
今月初め、RedisはGrafana向けの新しい「Redis Data Source」プラグインをリリースしました。このプラグインにより、広く使われているオープンソースの監視ツールGrafanaとRedisを簡単に接続できるようになります。本記事では、その仕組みを理解するために、少しメタな実例として「このプラグイン自体がどれだけダウンロードされたかを時系列で追跡する」方法を紹介します。実は、Grafanaのプラグインリポジトリにはこうした統計情報が標準では提供されていないのです。 さらに詳しく知りたい方は、「Introducing the Redis Data Source Plug-in