公開フォームの送信をプライベートネットワークへ取り込む方法
公開フォームの送信を1件、受付レコードからプライベート側のインポーターまで追い、検証・資格情報・再試行の扱いを正しい側に置きます。
プライベートネットワークと一度も通信しない公開フォームでも、プライベート側が判断すべきデータは生まれます。トンネルが変えるのは、誰が誰に接続するかだけです。ダウンロードより後のすべては取り込みの判断であり、プライベート側に属します。この解説では、送信一件をブラウザから確定済みの内部レコードまで追います。
送信が到着する場所から始める
ブラウザは公開エンドポイントへ送信し、そこは管理された受付ストアへ一行を書き込み、受領の通知以外は返しません。そのストアが受け渡し地点であり、公開層が書き込める唯一の場所であるべきです。生のペイロードはそのまま保管し、到着時刻と安定したレコード識別子を併せて記録します。
次にプライベート側のワーカーがトンネル経由で保留中の行を取得し、それぞれをどう扱うかを決めます。ワーカーは独自の ID で動作し、その点は次の節で扱います。
curl --silent --fail \
--cert /etc/importer/client.crt --key /etc/importer/client.key \
"https://intake.corp.internal/api/pending?limit=20" \
-o /var/lib/importer/pending.json
インポーターには必要な権限だけを与える
公開ウェブのプロセスと、内部アプリケーションへ書き込む ID は、別々のプリンシパルであるべきです。公開サーバーはフォームデータを届けるという理由だけで内部の管理権限を必要としませんし、両側で一つのアカウントを共有すれば、ウェブの不具合がそのまま内部への書き込みになります。ワーカーには専用の資格情報を発行し、それを一つの取り込み操作に限定し、どのプリンシパルが各取り込みを実行したかを記録します。この分離があってこそ、トンネルの境界は飾りではなく意味を持ちます。
ワーカーが読み取れる取得元も制限します。実装がキューや転送サービスを使う場合、VPN はペイロードの内部を何も認証しないため、そのサービスが独自の認証とアクセス方針を保ちます。
短命な関数も同じレビューから免除されません。動作している間は、そのネットワーク上の位置と資格情報が依然として権限を与え、寿命が短いことは窓を狭めるだけです。寿命は、その ID が実行できる操作を決めることの代わりにはなりません。
内部で何かが変わる前に検証する
公開フォームのフィールドは、反証されるまで攻撃者が制御するテキストであり、OWASP の入力検証ガイドがバックエンドの供給経路を信頼できない入力元に数えているのは、まさにそのためです。暗号化された転送は、バイトが無傷で届いたことだけを意味し、許容される要求を記述していることまでは意味しません。特権的な処理の前に解析と検証を行い、不正な行が実行可能なコード経路へ決して到達しないようにします。
許可するフィールド、その形式、サイズ、そして満たすべき業務ルールを定義します。日付は書式が正しくても要求された操作には誤っていることがあり、内部アカウントや承認状態を名指しするペイロードは、そのどちらも自分に与えられてはなりません。
拒否した行は、修正を推測したりシェルや生のデータベースインターフェースへ転送したりせず、レビューの結果へ送ります。記録するのはレコード識別子と失敗したルールであり、申請者が入力したすべてのフィールドではありません。
一つの操作に再試行点を設ける
最初の受領通知が失われたためにレコード submission-1042 が二回取得される場合、ワーカーには元のレコードと確定した結果を識別する永続的な手段が必要です。ジョブを一回実行するスケジュールは、各行が一回だけ適用されたことを証明しません。重複配信は当然の前提なので、設計はそれを受け止める必要があります。
内部アプリケーション自身のトランザクションと重複処理の仕組みを使い、元の識別子、結果、受領通知を一つの整合した順序に保ちます。意図した操作が確定または永続的に割り当てられた後にだけ通知し、バイトをダウンロードしたという理由だけで受付の行を削除してはなりません。
そして三つのテストファイル、つまり有効な行、壊れた行、同じ管理対象の元識別子を二回配信したもので証明します。いずれもワーカーが使う同じエンドポイントを通ります。
for f in fixtures/valid-submission.json fixtures/bad-date.json; do
curl --silent --fail --cert /etc/importer/client.crt \
-X POST --data @"$f" https://intake.corp.internal/api/records
done
curl --silent --fail --cert /etc/importer/client.crt \
-X POST --data @fixtures/valid-submission.json https://intake.corp.internal/api/records
最初の二行は異なる内部状態で終わらなければならず、三番目は二つ目を作るのではなく、その元レコードに対してすでに確定した識別子を返さなければなりません。それ以外であれば、取り込みはまだ再実行可能ではありません。