速報

JAL・JTAの予約情報約9万2千人分に外部参照の形跡|対象項目と利用者対応

CATEGORY

S3の署名付きアップロードで上書きを防ぐ If-None-Matchと期限の検証

※本ページのリンクにはプロモーションが含まれています。

最初のファイル保存と再送による上書きの拒否を時計と書類で表した編集イラスト

目次

S3へブラウザーから直接アップロードする構成では、署名付きPUT URLを短時間で失効させても、期限内の再送による上書きは防げません。対象は、添付ファイルや提出資料をS3へ保存するWebサービスの開発・運用担当者です。新規保存専用のキー、If-None-Matchによる条件付き書き込み、発行のを組み合わせ、受け付け済みファイルの差し替えを防ぐ構成を確認します。

本稿は2026年10月28日に確認したAWS公式資料に基づく恒久対策の実践解説です。新しいや攻撃事件の報告ではありません。一般用途バケットへの単一のPutObjectを扱い、以下のコードと試験手順は設計例です。実環境での動作検証や性能測定は行っていません。

スポンサーリンク

短い有効期限だけでは上書きを防げない

AWS:署名付きURLの利用と制限では、署名付きURLは有効期間中に複数回利用でき、同じキーへのアップロードは既存オブジェクトを置き換えると説明されています。URLを知る人が、そのURLに許された操作を行えるため、漏えいしたPUT URLは第三者の書き込み経路になり得ます。バケットの非公開設定だけでこの権限を取り消すことはできません。

例えば、利用者が本人確認書類を送信し、アプリが「提出済み」にした後もPUT URLが有効なら、同じキーに別の内容を送れる構成が問題になります。完了APIだけを一度きりにしても、S3への再送を止めたことにはなりません。先に正規利用者が保存したファイルを後から変更させない、という条件をS3側にも持たせます。

ただし、条件付き書き込みで防げるのは保存後の上書きです。URLを盗んだ第三者が正規利用者より先に送信する問題は残ります。URLの漏えい防止、短い期限、内容検査、提出者との対応確認も必要です。最初に書けたファイルを無条件で正しいものとして公開しない設計が前提になります。

保存先はサーバーで決めて発行記録に結び付ける

本稿の構成案では、発行APIがログイン状態と対象案件への提出権限を確認し、アップロードIDと新しい保存キーを作成します。キーはincoming/ID/アップロードIDのように、サーバーが認可済みの情報から決定します。クライアント指定のバケット名や任意のキーをそのまま署名対象にしてはいけません。

には提出者、案件、保存キー、発行時刻、有効期限、期待するファイル種別と検査状態を保存します。同じ案件への再提出も新しいキーへ受け付け、採用するファイルの参照をアプリで切り替えます。利用者が見せるファイル名を保存キーとして使い回す方式から分離すると、別人の提出物との衝突も避けやすくなります。

AWS:署名付きリクエストの認可モデルによると、S3は署名に使ったとリクエスト時点のポリシーに基づいて認可します。署名するロールは受け付け用プレフィックスへのPutObjectなど必要な権限に絞り、不要な読み取りや削除を与えない構成にします。署名URLはアプリの利用者認可を代行しないため、発行API側の案件確認を省略できません。

スポンサーリンク

If-None-Matchを署名対象に含める

AWS:条件付き書き込みでは、If-None-Matchにアスタリスクを指定すると、同じキーの現在のオブジェクトが存在する場合に書き込みが失敗します。新しいキーへの保存に使い、既存ファイルの更新には使い回しません。更新対象のETagを比較するIf-Matchとは目的が異なります。

AWS:署名付きURLによるアップロードの発行方法とAWS:Boto3 PutObjectのパラメーターを基にした例です。認可済みのキーを渡し、SigV4で署名します。認証情報は実行環境のIAMロール等から取得し、コードにアクセスキーを埋め込みません。

import boto3
from botocore.config import Config

s3 = boto3.client("s3", config=Config(signature_version="s3v4"))
# 発行APIで認可し記録したキー
key = "incoming/tenant-example/upload-example"
url = s3.generate_presigned_url(
    ClientMethod="put_object",
    Params={
        "Bucket": "amzn-s3-demo-bucket",
        "Key": key,
        "ContentType": "application/pdf",
        "IfNoneMatch": "*",
    },
    ExpiresIn=300,
)

発行結果のX-Amz-SignedHeadersにif-none-matchが含まれることを、URL全体をログに残さず確認します。ヘッダーを付けるだけで署名に含めなければ、条件を外したリクエストを防げるとは限りません。がIfNoneMatchを未対応として拒否する場合は、利用中のBoto3とbotocoreの対応状況を確認し、条件を削除して通す修正はしません。

クライアントは署名時と同じContent-TypeとIf-None-Matchを送ります。次は検証用ファイルを送る例です。URLは発行APIから受け取った値をに保持する想定で、実際のURLをコマンド履歴やチケットに貼らない運用にします。

curl --request PUT \
  --header 'Content-Type: application/pdf' \
  --header 'If-None-Match: *' \
  --data-binary @sample.pdf \
  "$UPLOAD_URL"

Content-Typeを固定しても、本文が本当にPDFか、安全な内容かは証明できません。アップロード先は検査前領域として扱い、内容確認後にアプリの公開・配信対象へ昇格させます。チェックサムを使う場合も、転送内容の整合性確認と検査は別の役割です。

署名の古さもバケットポリシーで制限する

ExpiresInは発行側で短く設定しますが、発行処理の誤設定に備えて、s3:signatureAgeで古い署名を拒否する方法もあります。次はincoming配下へのPutObjectに対し、署名から600000ミリ秒を超えた要求を拒否するStatementの例です。これは既存ポリシーへ統合して検証する断片であり、アクセスを許可する設定ではありません。

{
  "Sid": "DenyOldUploadSignatures",
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:PutObject",
  "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/incoming/*",
  "Condition": {
    "NumericGreaterThan": {
      "s3:signatureAge": "600000"
    }
  }
}

この例のURL自体の期限は300秒なので、ポリシーを加えても10分へ延長されません。一時認証情報が先に失効した場合もURLは使えなくなります。署名の古さへの制限は対象範囲の他の署名付き書き込みにも影響し得るため、共有プレフィックスへ適用する前に通常の転送処理を洗い出します。待ち時間や通信環境に合う期限を決め、失効時は認可を再確認して再発行します。

スポンサーリンク

再送試験は成功応答だけを見ない

では、新しいキーへの最初のPUT、同じURLで異なる本文を送る二度目のPUT、必須ヘッダーを外した要求、期限切れ後の要求を分けて試します。初回が成功し、同じキーへの再送が412で止まり、保存済み本文が変化しないことを確認します。ヘッダー省略も拒否されることを確認し、画面上のエラー表示だけで合格にしません。

AWS:PutObject APIは、既存キーに対する条件不成立を412 Precondition Failed、競合操作を409 ConditionalRequestConflictとして区別しています。409を受けた単一PUTは再試行できますが、同じ条件を維持してください。競合後に条件を外して成功させると、上書き防止の目的を失います。

特に注意するのは、S3では保存に成功したのにクライアントが応答を受け取れなかった場合です。再送の412だけでは、先の要求が成功したのか、別の要求が先に保存したのかを判定できません。アプリの照合APIが発行記録と保存オブジェクトを突き合わせ、期待するサイズや検査用の整合性情報を確認してから受け付け状態を確定します。

この照合APIも案件と利用者の権限を再確認し、ブラウザーから渡された「成功」「所有者」だけを信用しません。検査失敗なら受け付けを保留し、新しいアップロードIDでやり直すなど状態遷移を決めておきます。繰り返す412をすべて攻撃として通知すると正常な再送も混ざるため、発行記録と時間帯を併せて判断します。

削除とマルチパートには別の注意点がある

If-None-Matchは、キーが永久に使用済みになる仕組みではありません。現在のオブジェクトがなくなれば再び書けます。バージョニングを有効にしていても、現在のバージョンが削除マーカーなら条件を満たす場合があります。検査後にincomingのオブジェクトをすぐ削除すると、期限内のURLで同じキーへ再投入される余地が生まれます。

そのため、URLの利用可能期間と検査前オブジェクトの保持期間を合わせて設計し、削除後に届いたイベントや再投入を受け付け済みに戻さないよう、アプリ側にも完了状態を残します。元のオブジェクトを配信領域へ移す構成では、コピー後の削除時点も確認対象です。バージョニングだけを上書き拒否の代用にしないでください。

大きいファイルをマルチパートで送る場合は、ここで示した単一PUTの試験結果を流用できません。条件はCompleteMultipartUploadにも指定できますが、進行中のアップロードは既存オブジェクトとして判定されません。また、完了時の409ではマルチパートアップロード全体を作り直す必要があります。部品のURLと完了処理の権限も含め、別の手順として設計します。

403の切り分けと漏えい時の対応

403や署名エラーが出たら、まず有効期限と一時認証情報の失効、、メソッド、署名済みヘッダーの値を確認します。次にIAMとバケットポリシー等の明示的なDenyを調べます。がヘッダーやクエリーを変更していないかも確認し、失敗理由を確かめず期限延長や広いAllowを追加しないことが大切です。

AWS:署名付きURLとログは、署名付きURLを機密情報として扱い、ログのX-Amz-Signatureやクエリー全体をマスクするよう説明しています。S3側のログで署名が伏せられても、アプリ、プロキシ、監視ツールの記録まで安全になるとは限りません。運用ログはアップロードID、結果、時刻、必要なS3の要求IDを中心にし、URLを残さず追跡できる形にします。

漏えい時は新規発行を止め、対象キーと発行記録から未検査オブジェクトを隔離します。署名元の認証情報の無効化や権限変更を検討する場合は、同じロールを使う他の処理への影響も確認します。条件付き書き込みを導入しても発行APIの認可や内容検査は残るため、発行・保存・検査・確定の四段階を通して防御が成立しているかを確認してください。

関連記事

S3の非公開設定と共有・配信経路の確認では、ダウンロード側を含むの全体像を扱っています。

スポンサーリンク

参考資料

AWS:署名付きURLの利用と制限

AWS:条件付き書き込み

AWS:署名付きURLによるアップロード

AWS:Boto3 PutObject

AWS:PutObject API

AWS:署名付きリクエストの認可モデル

AWS:署名付きURLとログ

この記事を書いた人

Ryohei

Webエンジニア / アプリ開発者 / 技術ブログ運営者

福岡のWeb制作会社に勤務するWebエンジニアです。10年以上にわたりWeb制作・開発に携わり、ヒアリングや進行管理から、コーディング、CMSの構築、システム開発、サーバーの構築・保守・運用まで、Webサイト制作の工程を幅広く担当しています。Webサイトは、これまでに100以上の構築・保守を手がけてきました。

個人でもWebサイトの制作・改修やWebアプリの開発・運用を行い、AIを活用した開発にも取り組んでいます。近年はWebセキュリティにも注力しています。

2015年から技術メディア「Into the Program」を運営し、実務や開発で得た知識をもとに、プログラミングの実装方法やWebサイトの運用・セキュリティ情報を発信しています。

主な分野
Webサイト構築・保守・運用 / HTML/CSS / JavaScript / PHP / WordPress / サーバー構築・保守・運用 / アプリ開発 / セキュリティ対策
資格・試験合格
情報セキュリティマネジメント試験
基本情報技術者試験
AWS認定ソリューションアーキテクト アソシエイト
PHP8技術者認定ウィザード
メディア運営
2015年〜

記事の作成・更新について

セキュリティ記事は、ベンダーの公式アドバイザリやCVE・JVNなどの一次情報を参照し、出典と情報確認日を記載します。実環境での検証を行った場合は、その環境と確認範囲を明示します。重要な追加情報や誤りを把握した場合は、内容を確認して追記・訂正します。

関連する記事

おすすめの書籍

キーワードから探す

スポンサーリンク

アクセス制御

誰が、どの情報や機能に、どこまでアクセスできるかを決めて制限する仕組み。

詳しく見る

プロキシ

通信の間に立ち、利用者の代わりにリクエストを送ったり受け取ったりする中継の仕組みやサーバー。

詳しく見る

HTTP

ブラウザとWebサーバーがやりとりするための決まりごと(プロトコル)。暗号化したものはHTTPSと呼ぶ。

詳しく見る

リージョン

クラウドのデータセンターが置かれている地域の単位。東京・大阪など、どこでサービスを動かしデータを保存するかを選ぶ。

詳しく見る

ステージング

本番とほぼ同じ構成で用意する検証用の環境。更新や変更を本番に反映する前に、動作を確かめるために使う。

詳しく見る

マルウェア

コンピューターに被害を与える目的で作られた悪意のあるソフトウェアの総称。ウイルス、ワーム、トロイの木馬、ランサムウェアなど。

詳しく見る

環境変数

OSや実行環境に設定しておき、プログラムから読み取って使う値。環境ごとの設定や秘密の値を渡すのに使う。

詳しく見る

SDK

ソフトウェア開発キット。特定のサービスや環境向けの開発に必要なライブラリやツール、ドキュメントをまとめたもの。

詳しく見る

IAM

ID・アクセス管理。誰が、どの資源に、どんな操作ができるかを管理する仕組み。AWSなどクラウドの権限管理を指すことも多い。

詳しく見る

認証情報

本人であることを証明するための情報。IDとパスワード、APIキー、アクセストークン、秘密鍵など。

詳しく見る

データベース

データを決まった形式で整理して保存し、検索や更新をしやすくした仕組み。WordPressではMySQLやMariaDBを使う。

詳しく見る

テナント

クラウドやSaaSで、利用する組織ごとに分けられた区画。一つの仕組みを複数の組織で共有する形をマルチテナントと呼ぶ。

詳しく見る

脆弱性

ソフトウェアや設定の不備のうち、攻撃者に悪用されると情報漏えいや不正操作などにつながる弱点。

詳しく見る

認可

認証で確認した利用者に、どの情報や機能へのアクセスを許すかを決める仕組み。

詳しく見る

API

ソフトウェアやサービスの機能を、ほかのプログラムから決まった方法で呼び出して使えるようにした窓口。

詳しく見る