目次
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の非公開設定と共有・配信経路の確認では、ダウンロード側を含むの全体像を扱っています。
スポンサーリンク









