データベース (Perl CGI & MySQL) に書き込む小さな Web アプリを作成しています。CGI スクリプトはフォームから情報を取得し、それをデータベースに書き込みます。ただし、Web ブラウザーで [再読み込み] または [戻る] をクリックすると、データがデータベースに再度書き込まれることに気付きました。私はこれをしたくありません。
この場合、データの書き換えを防ぐ最善の方法は何ですか?
データベース (Perl CGI & MySQL) に書き込む小さな Web アプリを作成しています。CGI スクリプトはフォームから情報を取得し、それをデータベースに書き込みます。ただし、Web ブラウザーで [再読み込み] または [戻る] をクリックすると、データがデータベースに再度書き込まれることに気付きました。私はこれをしたくありません。
この場合、データの書き換えを防ぐ最善の方法は何ですか?
GET リクエストを使用して変更を加えないでください。RESTfulであること。代わりに POST (または PUT) を使用すると、ブラウザーはユーザーに要求をリロードしないように警告する必要があります。POST/PUT リクエストの後に通常の GET リクエストを使用して( HTTP リダイレクトを使用して) 受信ページにリダイレクトすると、再送信に関する警告を受けずにページを更新できます。
編集:
ユーザーが何らかの方法でログインしていると想定しているため、セッションなど、ユーザーを追跡する何らかの方法がすでにあります。
タイムスタンプ(またはランダムハッシュなど)を作成して、非表示フィールド(アンチクロスサイトリクエストトークンのすぐ横)とセッション変数の両方として保存するフォームを表示できます。 (これはサーバーに安全に保存されます)、このフォームの POST/PUT リクエストを受信すると、タイムスタンプがセッションのものと同じであることを確認します。そうである場合は、セッションのタイムスタンプを可変で推測しにくいもの (たとえば、タイムスタンプを秘密の文字列と連結したもの) に設定すると、フォーム データを保存できます。誰かがリクエストを繰り返すと、セッション変数に同じ値が見つからず、リクエストが拒否されます。
これを行う際の問題は、ユーザーが何かを変更するためにクリックして戻った場合にフォームが無効になることです。更新しているのがお金でない限り、それは少し厳しいかもしれません。そのため、更新して戻るボタンをクリックして誤って何かを再投稿する「愚かな」ユーザーに問題がある場合は、POST を使用するだけでそうしないように注意し、リダイレクトすることでその可能性が低くなります。悪意のあるユーザーに問題がある場合は、タイムスタンプも使用する必要がありますが、ユーザーを混乱させることがありますが、ユーザーが意図的に同じメッセージを何度も投稿している場合は、おそらくそれらを禁止する方法を見つける必要があります. POST を使用し、タイムスタンプを取得し、データベース全体を完全に比較して重複した投稿をチェックしても、悪意のあるユーザーが、フォームをロードしてランダムなゴミを自動的に送信するスクリプトを作成するだけでは、まったく役に立ちません。(しかし、クロスサイト リクエスト保護により、それはさらに困難になります)
POST リクエストを使用すると、ブラウザはユーザーが同じリクエストを再度送信するのを防ごうとしますが、ユーザーがブラウザからの警告を無視してクエリを再送信した場合に、何らかのセッションベースのトランザクション追跡を使用することをお勧めします。アプリケーションは、データベースへの変更の重複を防ぎます。リクエストが送信され、エラーなしで処理された場合、値を暗号ハッシュに設定して送信フォームに非表示の入力を含め、そのハッシュを記録することができます。
ユーザーがセッションで実行したフォーム送信の数を追跡すると便利です。次に、フォームをレンダリングするときに、その番号を含む隠しフィールドを作成します。その後、ユーザーが [戻る] ボタンを押してフォームを再送信すると、古い # が送信され、サーバーはセッションの内容とフォームの内容を調べることで、ユーザーが既にフォームを送信したことを知ることができます。
ちょうど私の2セント。
まず第一に、ブラウザは信用できないため、GET ではなく POST を使用するという話は、ほとんどが馬鹿げた話です。はい、クライアントは「このデータをもう一度再送信するつもりでしたか?」という警告を受け取る可能性がありますが、「はい、私を放っておいてください、愚かなコンピューター」と言う可能性が非常に高いです。
当然のことですが、送信の重複を望まない場合は、ユーザーの問題ではなく、あなたの問題を解決する必要があります。
おそらく、重複提出とはどういう意味か、ある程度理解していると思います。おそらく、数秒以内に同じ IP である可能性があります。また、最近送信されたブログ投稿または URL の同じタイトルである可能性もあります。おそらく、それは値の組み合わせです-たとえば、IPアドレス、電子メールアドレス、および連絡先フォーム送信の件名の見出し. いずれにせよ、データの重複を手動で見つけた場合は、送信時にプログラムで重複を識別し、手動承認のためにフラグを立てる方法を見つけることができるはずです (確信がない場合)。または、送信者に「ダブルクリックしましたか?」と伝えるだけです。(情報が驚くほど機密でない場合は、彼らのために持っている既存の記録を提示して、「これはあなたが私たちに送るつもりだったものですか?そうなら、あなたはすでにそれを行っています-万歳」と言うことができます)
ある種のセッション管理 (フォームの送信を記録して追跡できるようにする) をまだ使用していない場合、簡単な解決策は、フォームに何らかの固有の識別子を (非表示の要素として) 含めることです。メイン DB トランザクション自体の、または別の DB テーブルで追跡されます。次に、フォームが送信されたら、一意の ID をチェックして、それが既に処理されているかどうかを確認します。フォーム自体がレンダリングされるたびに、一意の ID を持っていることを確認する必要があります。
ブラウザからの POST 警告には頼りません。ユーザーは [OK] をクリックするだけでメッセージを消去できます。
「支払いを行う」などの 1 回限りの要求がある場合はいつでも、一意のトークンを送信し、要求と共に送信されます。戻ってきたトークンを破棄すると、何かが有効な送信 (「アクティブ」でないトークンを持つもの) であることを知ることができます。ユーザー セッションが終了したときなど、X 時間後にアクティブなトークンを期限切れにします。
(代わりに戻ってきたトークンを追跡し、以前に受け取った場合は無効です。)
データを変更するたびに POST を実行しますが、投稿から HTML 応答を返すことはありません。代わりに、更新されたデータを確認ページとして取得する GET へのリダイレクトを返します。そうすれば、ページを更新する心配はありません。それらが更新された場合、発生するのは別の取得だけであり、データを変更するアクションではありません。