作ったもの
Discordのファイル送信容量上限への不満や、ファイル共有サービスのプライバシーに対する懸念の高まりから、ファイルアップローダーを制作しました。
ただ作成するだけではつまらないので、暗号化の学習も考慮して技術選定を行いました。
最初は暗号化の勉強として始めましたが、実装していくうちに、認証、ストレージ、CDN、決済、公開APIまで触ることになりました。
技術スタック
NuxtとVuetifyでユーザー向けウェブサイトと管理ウェブサイトを構築しました。バックエンドは別で用意せず、Nuxtのserverディレクトリを使用することで、Cloudflare Workers上で完結させています。
ファイル配信にはCloudflare Workersで動く別のCDN用のWorkerを使用しています。これは、ストレージサービスに、Cloudflare CDNを介してデータ転送を行うことでエグレス料金が無料になるBackblaze b2(AWS S3互換ストレージ)を使用したためです。(参考: https://www.cloudflare.com/ja-jp/partners/technology-partners/backblaze/)
その他、データベースにはCloudflare D1、メール送信にはResend、ボット対策にはCloudflare Turnstileを使用することで、ランニングコストを極限まで抑えながら安定した動作を実現しています。
暗号化まわり
ファイル本体とメタデータはクライアント側でAES-GCMを使って暗号化します。サーバーに保存するのは暗号化済みのデータと、運用に必要な最小限の値です。復号キーは共有URLのフラグメントに載せる形にして、通常のHTTPリクエストやサーバーログにキーが送られないようにしました。
そのうえで、DiscordなどのEmbedからコンテンツを閲覧したいユーザーのために、フラグメントのかわりにクエリでキーを載せた場合には、専用OGエンドポイントからサーバーサイドでファイルを復号、表示できるようにしました。
また、ユーザーのプライバシーを重視する実装を目指したこと、可能な限りユーザーのパスワードを保管したくなかったことから、秘密鍵やパスキーを使ったログインを実装しました。
セッション管理にはAccessToken+RefreshTokenの実装をしています。Auth系ライブラリを使わず自前で実装したので大変でした。
アップロードと配信
アップロードは大きいファイルも扱えるように、S3互換のmultipart uploadを前提にしました。アップロード時にUIが固まらないようにWeb Workerも使っています。保存期間、ダウンロード回数制限、プランごとの容量制限、Turnstileによる濫用対策も入れました。せっかくならSaaSっぽい実装にしようと思ったのでがんばりました。
ダウンロード側はCloudflare WorkersのCDN Workerで署名付きトークンを検証し、Backblaze B2からファイルをストリームします。トークン検証にはHMACを使っています。
支払いと管理画面
仮想通貨でのチャージとPayPal決済を用意し、管理画面ではユーザー、ファイル、売上、支払い状況を見られるようにしています。決済はAPIを叩けば終わり、という感じではなく、失敗時の扱い、二重処理、決済ログ保持などトラブルを低減するための複数の施策を講じています。実際、Paypalを通じた決済をユーザーがチャージバックしたことがありましたが、決済ログを保持していたおかげで紛争に勝利しました。
生成AIの利用
開発にあたって、Codexを使用しました。ChatGPT Plusに計6つのアカウントで加入し($20/mo×6)、上限まで回し続けました。セキュリティ上の欠陥を発見する際など大いに役立ちましたが、逆に私がセキュリティ上の重大な欠陥を指摘することもありました。
所感
当初はあくまで学習目的で作成しましたが、開発を続けていくうちに複数の機能を備えたプロダクトになっていきました。今後も継続的に開発を行う予定です。

