タイムテーブル Timetable

タイムテーブルに戻る
標準ライブラリをどこまで信じるか —900アプリを支えるプラットフォームへのファイルアップロード導入から学ぶ io・mime・multipart
ユーザーが画像をアップロードできるようにする——ありふれた要件に見えて、いざ本番に載せようとすると不安は次々わいてきます。巨大なファイルでメモリは膨らまないか、拡張子を偽装されないか、見慣れない形式が来たら壊れないか。
このセッションを通して伝えたいことは、こうした不安に対して「手軽な標準APIをそのまま信じるか、それとも一段下りて挙動を自分の目で確かめるか」をどう判断するか、という設計の勘所です。
io・mime・multipart の挙動を一つずつ読み解きながら、その判断の拠り所を一緒に考えていきます。
題材は、900以上のアプリが同居するモバイルアプリ基盤に、ユーザーからのファイルアップロードを初めて導入した実装です。
一つの実装をすべてのアプリが共有するため、どれか一つの考慮事項を見逃しても影響は全アプリに波及するため、その対応は自ずと慎重にならざるを得ません。
ただ、扱う課題の多くはファイルアップロードを書いたことのあるGoエンジニアなら出会うものです。
実装で向き合った勘所のうち3つを、要点を抜き出したサンプルコードを交えて共有します。
1. r.ParseMultipartForm() に丸投げしてよいか — メモリと向き合うストリーミング処理
ParseMultipartForm() は手軽で、多くのユースケースでは妥当な選択肢です。一方でリクエスト全体をメモリ/一時ファイルに展開するため、不特定多数からのファイルを受ける今回の要件では別の手段を選びました。
代わりに r.MultipartReader() から NextPart() でパートを1つずつストリーミング処理し、1MBバッファで読み進めながら io.EOF を検知する前にサイズを累積チェックして上限超過を即座に弾く実装を紹介します。
そして、読み込んだバイト列を io.Reader から io.ReadSeeker へ昇格させておくことが、後段の検証・アップロードでどう効くのかを掘り下げます。
2. ファイルの拡張子を信じてよいか — content sniffing による MIME 判定
ファイル名の拡張子は攻撃者が自由に詐称できます。
先頭512バイトのバイナリから実際の中身を判定する content sniffing(mimetype.Detect)の実装と、io.LimitReader での先頭読み出し、検証後に Seek(0, io.SeekStart) で位置を巻き戻す副作用の管理、defer 内で起きた seek エラーを multierr.Append で名前付き戻り値にマージするエラーハンドリングを解説します。
拡張子を偽装したファイルをテストでどう弾いているかなども合わせて示します。
3. mime はどこまで面倒を見てくれるのか — HEIC が教えてくれた OS 依存の設計
多様なアプリのユーザーが集まるプラットフォームでは、iPhone から送られてくる HEIC/HEIF は「例外的な形式」ではなく、日常的に受け止めなければならない入力です。
ところが、この iPhone 標準フォーマットは Go 標準 mime パッケージの組み込みテーブルには登録されていません。
調査を進める中で分かったのは、mime.ExtensionsByType が不足分を OS 上の MIME データベース(/etc/mime.types など)から補う設計になっていることでした。そのため、実行環境によっては同じ MIME Type に対して拡張子を解決できたりできなかったりします。
これは欠陥ではなく、mime パッケージがどのように MIME 情報を管理するかという設計によるものです。本セッションではソースコードを追いながら、mime がどこから MIME 情報を取得し、なぜ環境によって結果が変わるのかを確認します。
また、900以上のアプリが利用する共通基盤において、このような環境依存性をどのように評価し、どのような選択肢を検討したのかについてもお話しします。
まとめ
io・mime・multipart というベーシックな標準パッケージの挙動を一つずつ確かめていくという地味で実直な積み重ねが、900以上のアプリを持つプラットフォームを支える堅牢なファイルアップロードの基盤になるというお話でした。
ただ、ファイルのアップロードについてはあくまで一例にすぎず、いかなる実装においても同様かと思います。
便利な標準APIをそのまま信じる前に、その挙動を自分の手で確かめてみる姿勢によって、コードの品質が劇的に向上するかもしれません。
このセッションが、あなたの次の実装を一歩堅牢・高品質にするヒントになれば嬉しいです。
Yappli.Inc,server side developer
2017年に新卒で個別指導塾の教室長になったものの2018年半ばに退職。その後職業訓練校を経て、2020年に1990年代からある古き良きPerlのWeb求人サービスからWebエンジニアとしてのキャリアをスタートさせました。 Goエンジニアとしての始まりは2021年、Perlで書かれたWebサービスと同じようなMVCのWebサービスをGo1.17を使って開発したことがきっかけでした。 2022年にはGoを使った開発を積極的に行う名古屋の医療系スタートアップへ転身し2023年後半からはリードエンジニアとしてPJをリードしながらGoによる開発を続けました。 2024年に株式会社ヤプリへジョインし、ノーコードアプリ開発プラットフォームのサーバサイドエンジニアとしてGoを使った開発を現在も行っています。