Browser Content Disposition and Downloads

最終更新: 2023-08-16 17:13:20 +0800 +0800
tags: Browser | Http | Security | Web
原文は英語で書かれています。あなたはAI翻訳版を読んでいます。

ブラウザーは複数の入力から、レスポンスを表示するか、ダウンロードするか、外部アプリケーションへ渡すかを決定します。 Content-Disposition は強い優先指定を提供しますが、コンテンツ型、ナビゲーションコンテキスト、サンドボックス、 ブラウザーの機能、ユーザー設定も結果に影響します。

inline と attachment の処理 #

attachment レスポンスはダウンロードを要求します。

http
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="guide.pdf"
Content-Length: 110352
X-Content-Type-Options: nosniff

inline レスポンスはブラウザー内での表示を許可します。

http
Content-Type: application/pdf
Content-Disposition: inline; filename="guide.pdf"

inline は表示を保証しません。ブラウザーにはメディア型向けのビューアーが必要であり、 ユーザーは一部の型を代わりにダウンロードするよう設定できます。ヘッダーを省略すると、主にメディア型とナビゲーションコンテキストが選択を決めます。

安全なファイル名の指定 #

filename は ASCII 互換のフォールバックを持ちます。filename* は RFC 5987 に代わる RFC 8187 が定義する 拡張パラメーターエンコーディングを使い、Unicode を表現できます。

http
Content-Disposition: attachment;
 filename="report.pdf";
 filename*=UTF-8''r%C3%A9sum%C3%A9.pdf

古いクライアントは filename* を無視する可能性があるため、両方のパラメーターを送信します。 準拠する受信側は両方が有効な場合 filename* を優先します。サーバーが指定したファイル名は信頼できない入力として扱います。

  • ディレクトリ区切り文字と制御文字を削除する。
  • 最終名として . と .. を拒否する。
  • 想定する拡張子をメディア型と一致させる。
  • 適切な長さの上限を設ける。
  • 値からサーバーのファイルシステムパスを直接構築しない。

ブラウザーはそれぞれ独立してファイル名をサニタイズするため、正確な改名動作は実装によって異なります。

コンテンツ型とスニッフィング #

Content-Type は表現を記述します。PDF は application/pdf, an SVG should be image/svg+xml, and arbitrary binary data should be application/octet-stream.

http
X-Content-Type-Options: nosniff

nosniff はスクリプトとスタイルの取得で互換性のないメディア型を拒否させ、その他の保護された取得経路で MIME に基づく再解釈を制限します。 誤った Content-Type を修正したり、任意のコンテンツを安全にしたり、ダウンロードを強制したりはしません。 宣言された型とリクエストコンテキストの両方にナビゲーションと埋め込みの規則が依存するため、 誤った無害な型でアクティブコンテンツを提供することは依然としてセキュリティ問題です。

HTML のダウンロードリンク #

download 属性はリンクのダウンロード意図を表します。

html
<a href="/exports/report.csv" download="report.csv">Download report</a>

HTTP(S) URL では、ブラウザーは同一オリジンのリソースに対してだけ download を尊重します。 blob: と data: URL も対象になりますが、CORS レスポンスヘッダーでクロスオリジン HTTP URL が対象になるわけではありません。 レスポンスの Content-Disposition のファイル名は提案名に影響でき、attachment はクロスオリジンのダウンロードで信頼できるサーバー側の手段です。 ボタンの背後に URL を隠しても保護にはならないため、認可はダウンロードエンドポイントに残します。

Blob URL はクライアント側で生成したファイルをサポートします。

js
const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
const url = URL.createObjectURL(blob);

const link = document.createElement("a");
link.href = url;
link.download = "report.csv";
document.body.append(link);
link.click();
link.remove();

setTimeout(() => URL.revokeObjectURL(url), 0);

各 URL は背後のデータを保持するため、ブラウザーがナビゲーションを開始した後にオブジェクト URL を解放します。 このシーケンスはユーザー起点のイベントから実行します。プログラムによるクリックでブラウザーのダウンロードポリシーを回避することはできません。

自動化とセキュリティ制約 #

サンドボックス化されたフレームには allow-downloads サンドボックストークンが必要であり、 ブラウザーは一時的なユーザーアクティベーションがないダウンロードをブロックしたり、繰り返しダウンロードを制限したりできます。 クロスオリジン分離、ポップアップポリシー、混在コンテンツポリシー、企業設定によって追加の制約が生じる場合があります。

サポートする各ブラウザーでナビゲーション全体をテストします。

  1. ステータス、Content-Type、Content-Disposition を検証する。
  2. 提案されたファイル名とダウンロードされたバイト列を確認する。
  3. 認証済み、リダイレクト、エラーのレスポンスを試す。
  4. HTML エラーページが信頼された拡張子で保存されないことを確認する。

ブラウザーが最終的なポリシー判断を行うため、ヘッダーの検査だけではユーザーに見える結果を証明できません。

コメント

GitHub で閲覧・コメントする(投稿にはログインが必要です)