質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.78%

二重投稿を阻止するために、htmlを書き換えるかphpで制御したい。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 166

score 1

前提・実現したいこと

PHP(7.x系)で掲示板システムを作っています。
投稿ボタンを押した後リロードや戻るボタンを押してしまい、二重投稿になってしまうことがありました。
そのため、投稿ボタンにリダイレクトをする文を記述しました。
ですが、うまくいかず、2回戻るボタンを押すと二重投稿ができてしまいます。
誰か知恵を貸してくれる人はいませんか?また、どう書き換えればいいか教えてください。

コード

<?php
// recaptchaを使うので、試してみるために組んでいます。
// よって、使わない部分が多いです。
// ほぼ確認用のコードです。
require '../recaptcha/recaptcha_vars.php';
// サイトキー
$siteKey = V2_SITEKEY;
// シークレットキー
$secretKey = V2_SECRETKEY;
$result_status = '';  // 結果を初期化
if ( isset( $_POST[ 'g-recaptcha-response' ] ) ) {
//GoogleのAPIを指定
$url = 'https://www.google.com/recaptcha/api/siteverify';
//パラメータを指定
$data = array(
'secret' => $secretKey,
'response' =>  $_POST[ 'g-recaptcha-response' ]
);
//POST メソッドを使用
$context = array(
'http' => array(
'method'  => 'POST',
'header'  => implode("\r\n", array('Content-Type: application/x-www-form-urlencoded',)),
'content' => http_build_query($data)
)
);
//設定したパラメータからAPIを使って取得する
$api_response = file_get_contents($url, false, stream_context_create($context));
$result = json_decode( $api_response );
if ( $result->success ) {
$result_status = '成功しました';
//デバッグ用。使わないのでコメントアウト。
//$re = 'OK';
} else {
$result_status = '失敗しました。: ';
$result_status .= $result->{'error-codes'}[ 0 ];
//デバッグ用。使わないのでコメントアウト。
//$re = 'NG';
}
}
print($re);
?>
<html> 
<script>
var onloadCallback = function() {
grecaptcha.render('recaptcha', {
'sitekey' : "<?php echo $siteKey; ?>",
'callback' : verifyCallback,
'expired-callback' : expiredCallback
});
};
var verifyCallback = function(response) {
document.getElementById("warning").textContent = '';
document.getElementById("send").disabled = false;
};
var expiredCallback = function() {
document.getElementById("warning").textContent = '投稿するにはチェックを入れてください。';
document.getElementById("send").disabled = true;
};
</script>
</head>
<head>
<link rel="icon" type="image/png" href="../icon.png">
<title>掲示板</title></head>
<body>
<body bgcolor="#181a19" text="#cccccc" alink="#ffffff">
<p><h1>掲示板</h1></p><br> 
<a href="https://***.**/">***.**(トップページ)</a><br>
<p>テスト的な意味もありますが、ちょっと作ってみました。</p> 
削除・変更の依頼は<a href="./delete.php" target="_blank">こちら</a>から
<form method="POST" action="<?php print($_SERVER['PHP_SELF']) ?>">
<input type="text" name="personal_name"><br><br>
<textarea name="contents" rows="8" cols="40"></textarea><br><br>
<div id="recaptcha"></div>
<p id="warning">投稿するにはチェックを入れてください。</p>
<input id="send" type="submit" name="btn1" class="btn btn-primary" disabled value="投稿!!" onclick="location.href='***.**/keiji/keiji_all.php'">
</form>
<?php
if($_SERVER["REQUEST_METHOD"] == "POST"){ 
writeData();
}
// ファイルを読み込んで表示
readData();
function readData(){
$keijban_file = 'keiji_all.txt';
$fp = fopen($keijban_file, 'rb');
if ($fp){
// ファイルロック
if (flock($fp, LOCK_SH)){
while (!feof($fp)) {
$buffer = fgets($fp);
print($buffer); 
}
flock($fp, LOCK_UN); 
}else{
// できないなら
print('ファイルロックに失敗しました');
print('何度も失敗する場合は知らせてください。');
}
}
fclose($fp);
}
function writeData(){
$personal_name = $_POST['personal_name'];
$contents = $_POST['contents'];
$contents = nl2br($contents);
// HTMLの構成
$data = "<hr>\r\n";
$data = $data."<p>投稿者:".$personal_name."</p>\r\n";
$data = $data."<p>内容:</p>\r\n";
$data = $data."<p>".$contents."</p>\r\n";
// ファイルへ書き込み開始
$keijban_file = 'keiji_all.txt';
$fp = fopen($keijban_file, 'ab'); 
if ($fp){
if (flock($fp, LOCK_EX)){
if (fwrite($fp,  $data) === FALSE){
print('ファイル書き込みに失敗しました');
print('何度も失敗する場合は知らせてください。');
}
// ファイルロック解除
flock($fp, LOCK_UN);
}else{
print('ファイルロックに失敗しました'); 
print('何度も失敗する場合は知らせてください。'); 
}
}
fclose($fp);
}
?>
</body>
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script> 
</html>

試したこと

cookieとjavascriptを使ってエラーメッセージを出すようにし、リダイレクトさせる
→どちらか一方を拒否されていると機能しない。
上記のように、リダイレクトさせる
→上記のような問題が発生する

補足情報

PHPバージョンはPHP7.3です。
windows 10のedgeを使って検証しました。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • m.ts10806

    2021/07/22 10:33

    コードこれだけだと再現確認無理では。
    あまり省略されると全体の流れが見えなくなるので、「現象が再現する最小構成のコード」を提示してください。
    提示のコードではそもそもエラーとなります。

    キャンセル

  • m.ts10806

    2021/07/22 10:34

    >windows 10のIE8
    >windows XPのIE6でも検証。

    そもそもIEが終わるのでここら辺は全然捨てていい範囲かと。

    キャンセル

回答 2

checkベストアンサー

+1

この手の対応方法は色々あってネットで情報漁れば色々出てくると思いますが、ワンタイムトークン的なものを利用するのが有効かと思います。

参考)
https://techacademy.jp/magazine/41842

色々な実装方法があると思うので、参考サイトに出てくる単語を使って色々検索してみてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/07/25 17:39

    ありがとうございます。
    こちらの方法でできました。

    キャンセル

+1

さいきょうの二重サブミット対策
二重投稿の質問が出たときに分かりやすいのでよく引用するのですが、随分時代遅れな記事になってしまいました^^;

記事の「二重サブミットが発生するケース」は非常に分かりやすいと思います。
記事中では対策の「どれか」を選択して使用するように読めますが、実際には複数を組み合わせて使用っするケースが多いです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/07/22 11:09

    「時代遅れ」と思われるポイントはどのへんでしょうか? (異論ではなく単なる質問です)
    記事をざっと読んだところ、不正確な箇所(例えば「X-Requested-Withでサイトに脆弱性がない場合は大丈夫そうですが、XSSの脆弱性があるとそこを突いて不正更新リクエストを送ることができます。したがって可能であればトークンによる対策と併用した方がよいでしょう」は間違い)はいくつかありそうですが、たとえば、SPAではなく古典的なマルチページのアプリケーションを前提にしているところでしょうか?

    キャンセル

  • 2021/07/23 10:12

    この質問は「double submit がどうして発生しているのかよく理解できていない」と判断して回答を作りました。

    紹介した記事は、私がプログラミングを始めた頃に読んで理解が進んだ記事なので、記事の前半の「二重サブミットが発生するケース」の図は理解しやすいと思います。

    ただ、それ以外の箇所で、現在のトレンドにおいては不足してきている情報が多くなったとも思ってます。

    特に、記事の後半の「APIによる更新」は、内容が薄く理解が怪しい記事になってしまっています。
    当時は記事にある程度の薄い理解でよかった気もしますが、今は画面遷移を伴わない投稿も多くなっているので、「さいきょう」を名乗るには情報不足かと。
    また、網羅性観点から DB 内の重複確認の手法に全く触れていない点が残念です。これは当時はあまりやらなかったと思うので仕方ない気もしますが。

    後、読み返してみると「CSRF対策」が同一の文脈で語られているのに違和感がありますね。

    新しい記事を探さなければいけない時が来たのかもしれないですw

    キャンセル

  • 2021/07/24 10:41

    回答ありがとうございます。確かにAPIの箇所は全然駄目ですね。APIの場合、二重投稿は避けにくいところがあるので、twitterのように、短期間に同内容の投稿をするとエラーにする(重複確認とも言える)などの手法が考えられますが、記事の「EV SSL証明書を使う」、「APIキーによる利用制限と不正利用の監視」は的外れですね。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.78%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る