要約
428 Precondition Requiredは、必要な条件を持たない状態変更要求をオリジンが拒否できるようにする。条件付き書き込みをクライアントの任意の配慮から、資源側の入場規則へ変える。- 代表例は強い ETag を用いる
If-Matchである。428 は条件がないこと、412 は提出された条件が偽であることを示す。 - 428 はロック、マージ、トランザクションではない。正しいバリデータ、比較と確定の原子性、競合後の内容調整が別途必要である。
二つの成功が一つの仕事を消す
二人の編集者が同じ文書の版 7 を開く。一人目は段落を直して版 8 を保存する。二人目は手元の版 7 を編集し続け、後から文書全体を送る。サーバーが権限、形式、宛先だけを確認すれば、どちらにも成功を返せる。それでも一人目の修正は最終状態から消えている。
これは lost update の基本形であり、攻撃者も壊れたパケットも要らない。後の要求は「何をどこへ書くか」を示したが、「どの状態を前提にその内容を決めたか」を示さなかった。「この資源を置き換えよ」と「私が読んだ版のままである場合だけ置き換えよ」は、異なる権限である。
HTTP には以前から条件付き要求があった。バリデータは、保存済みの表現を再利用できるか確かめ、本文の再送を省くキャッシュの仕組みとして語られやすい。しかし同じ証拠は書き込みにも使える。欠けていたのは、無条件の形そのものを受け入れないオリジンが返す、狭く明確な言葉だった。
2012 年 4 月の RFC 6585 は 428 Precondition Required を定義した。オリジンサーバーが要求を条件付きにするよう求めている、という意味である。典型的な用途として、クライアントが読んで変更し PUT する間に第三者が状態を変え、後の書き込みがそれを上書きする問題が挙げられている。
注意ではなく入場規則
読み取りで強い ETag "v7" を得たクライアントが、If-Match: "v7" とともに書くとする。これは単なる文字列の返却ではない。自分の判断が版 7 に基づくという検証可能な主張である。現在選ばれる表現がなお同じ強いタグを持つときだけ、オリジンはメソッドを実行する。別の変更が "v8" を作っていれば条件は偽となり、作用してはならない。
慎重なクライアントは 428 がなくてもこれを実行できた。428 の新しさは、オリジンが条件を資源の方針として強制できる点にある。RFC の例は If-Match を試すよう案内し、応答には成功する再提出方法を説明することが推奨される。
ただし、全操作に一つのフィールドを強制する規則ではない。If-Match: * は現在の表現が存在することを主張する。If-None-Match: * は存在しないことを主張し、同時作成による上書きを防げる。ETag がなければ If-Unmodified-Since という時間条件もある。どの証拠を要求するかは資源方針が明示しなければならない。
歴史的な転換は制御面にある。現在状態を所有する者が、変更者に対して「あなたが見た過去を先に示せ」と言えるようになった。
条件がない場合と、条件が外れた場合
428 と 412 は似て見えるが、処理の時点が違う。428 Precondition Required は入場判定である。方針上は条件が必要なのに、要求が受け入れ可能な条件を持っていない。作用を伴うメソッドは試されない。
412 Precondition Failed は評価結果である。条件は届いたが、オリジンが現在状態と比較すると偽だった。クライアントは過去を名乗ったものの、その過去はもう現在ではない。
運用ではこの差が重要になる。428 の増加は、古い SDK、不明瞭な文書、条件フィールドを落とすゲートウェイを示しうる。412 の増加は、長時間開いた編集画面、実際の同時更新、あるいは不要な変化でも揺れる ETag を示す。二つを一つの失敗率にすると、直すべき場所が見えない。
428 は 409 の別名でもない。409 は現在状態との意味的な競合を表せる。428 は、上書きが起きる前に証拠を要求する。WebDAV の 423 とも異なり、ロックの存在を報告せず、新しいロックも作らない。
If-Match が強い比較を使う理由
現行の HTTP セマンティクスである RFC 9110 は、If-Match に強い比較を要求する。弱いタグは、キャッシュで再利用できる程度に二つの表現が同等だと示す用途に向く。書き手は、表現データが変わったならメソッドを止めたい。目的が違う。
そのため、タグの生成は公開 API の一部になる。保護対象が変わってもタグが変わらなければ、古い書き込みを通す。意味のない直列化順序でタグが変われば、偽の競合を量産する。If-Match を必須にしながら使える強いタグを公開しないサービスは、回復手順ではなく行き止まりを作っている。
日付は精度が低い。If-Unmodified-Since は ETag がないときに同じ目的を担えるが、RFC 9110 は If-Match をより正確な代替とし、両方があれば日付条件を無視する。時計の粒度や更新時刻の作り方が、同一状態を示す力を制限する。
比較は確定から離してはならない
HTTP は前提条件をオリジンで評価させる。通常の要求検査を終えた後、メソッドの作用を実行する直前である。中間者はオリジンの現在状態を所有しないので、If-Match の真偽を独断で決めず転送する。
アプリケーション内部でも同じ近さが要る。ハンドラーがタグを比較し、トランザクションを閉じ、後で書き込むなら、その間に別の変更が入れる。HTTP 上は条件付きでも、実装は time-of-check/time-of-use の競合を再び作ってしまう。
必要なのは原子的な compare-and-act である。版番号列、compare-and-swap、または同等のトランザクションが、比較と変更を一つの権威ある判断にする。HTTP は外から見える意味を定義するが、データ層の原子性を代行しない。
範囲も一致させる必要がある。表示ページの ETag が、背後の複数レコードを守るとは限らない。アカウントを変えて外部キューにも通知する要求は、さらに別の境界を越える。428 は複数の作用を分散トランザクションに変えない。
拒否はマージではない
412 を受けたクライアントが、新しい ETag だけを取得し、古い全文に貼って再送することがある。条件は通るが、途中の変更を誰も比較していない。見えていた競合が、正式に許可された上書きへ変わる。
正しい回復は現在状態を読み直し、保留中の意図と比べ、マージ、破棄、明示的な置換を選ぶ。フィールド単位の patch や可換な操作なら競合面を小さくできる。人の判断が必要な場合もある。428 はその分岐を見せるが、結論を出さない。
RFC 6585 はこの状態コードを任意とも定め、クライアントが lost update 防止を 428 の利用だけに頼れないと明記する。API が条件付き更新を提供するなら、自分の変更を守りたいクライアントは、サーバーに叱られる前から条件を送るべきである。
キャッシュしてはいけない拒否
428 応答はキャッシュに保存してはならない。ある試行が入場方針を満たさなかったという情報であり、対象資源の再利用可能な表現ではない。古い 428 を後で返せば、中間者がオリジンの方針を勝手に代行することになる。
応答は、必要なフィールド、現在のバリデータを得る方法、再読後に調整が必要なことを説明すべきである。状態名だけでは回復経路にならない。
IANA の状態コード登録簿 は現在も 428 を Precondition Required として RFC 6585 に結び付ける。短い名前が残した原則は明瞭だ。現在を置き換える前に、書き込みは自分を正当化する過去を示さなければならない。
情報源と限界
根拠は RFC 6585、RFC 9110、IANA 登録簿である。意味、評価順、制限は確認できるが、現在の普及率、全ライブラリの実装、特定事故の損失、exactly-once は証明しない。428 はロック、マージ、トランザクションではない。
会員向け解説
プロフィールの詳細
適切な会員レベルでログインすると、解説全文と出典メモをご覧いただけます。
Strategic Circle 限定
Strategic Circle
すべての読者に公開されています。参加してログインすると プロフィール解説 を閲覧できます。
Strategic Circle に参加Leadership Alliance 会員限定
Leadership Alliance
対象となる IP 資産の所有者・管理者向けです。ログインすると Leadership Alliance の解説を閲覧できます。
Leadership Alliance に参加
