การป้องกันการส่งแบบฟอร์มอีกครั้ง


107

หน้าหนึ่งมีรูปแบบ HTML หน้าที่สอง - รหัสที่จัดการข้อมูลที่ส่ง

แบบฟอร์มในหน้าที่หนึ่งจะถูกส่ง เบราว์เซอร์ถูกเปลี่ยนเส้นทางไปยังหน้าที่สอง หน้าที่สองจัดการข้อมูลที่ส่ง

ณ จุดนี้หากหน้าสองได้รับการรีเฟรชการแจ้งเตือน "ยืนยันการส่งแบบฟอร์มอีกครั้ง" จะปรากฏขึ้น

สิ่งนี้สามารถป้องกันได้หรือไม่?


ใช้กล่องโต้ตอบการยืนยัน: stackoverflow.com/questions/6457750/form-confirm-before-submit/…

3
ใช้การเปลี่ยนเส้นทางโพสต์รับตามที่อธิบายไว้ที่นี่ - >> th.wikipedia.org/wiki/Post/Redirect/Get
Meer

คุณสามารถป้องกันได้แม้จะไม่มีการเปลี่ยนเส้นทาง ดูที่นี่
Eugen Konkov

คำตอบ:


145

มี 2 ​​แนวทางที่ผู้คนใช้ที่นี่:

วิธีที่ 1:ใช้ AJAX + Redirect

วิธีนี้คุณโพสต์แบบฟอร์มของคุณในพื้นหลังโดยใช้ JQuery หรือสิ่งที่คล้ายกับ Page2 ในขณะที่ผู้ใช้ยังคงเห็น page1 แสดงอยู่ เมื่อโพสต์สำเร็จคุณเปลี่ยนเส้นทางเบราว์เซอร์ไปที่ Page2

วิธีที่ 2:โพสต์ + เปลี่ยนเส้นทางไปที่ตนเอง

นี่เป็นเทคนิคทั่วไปในฟอรัม แบบฟอร์มบน Page1 โพสต์ข้อมูลไปยัง Page2, Page2 ประมวลผลข้อมูลและทำในสิ่งที่ต้องทำจากนั้นจะทำการเปลี่ยนเส้นทาง HTTP ในตัวเอง วิธีนี้ "การดำเนินการ" ครั้งสุดท้ายที่เบราว์เซอร์จำได้ว่าเป็น GET แบบธรรมดาบนหน้า 2 ดังนั้นจึงไม่มีการส่งแบบฟอร์มอีกครั้งเมื่อ F5


2
รูปแบบของวิธีที่ 2 คือการเปลี่ยนเส้นทางไปยังหน้าอื่น ตัวอย่างเช่นคุณโพสต์ไปexample.com/save.htmlและเมื่อบันทึกเสร็จแล้วคุณเปลี่ยนเส้นทางไปยังexample.com/list.html
Guillaume

1
ด้วยวิธีที่ 1 ฉันจะใช้ปลั๊กอิน jQuery ชื่อ BlockUI เพื่อให้ลูกค้ารู้ว่ามีบางอย่างเกิดขึ้น
Shikiryu

ฉันใช้วิธีที่ 2 แต่ขอให้ส่งอีกครั้งหากฉันรีเฟรช ใครมีปัญหาคล้ายกันนี้?
กระตือรือร้น

คุณแน่ใจหรือไม่ว่าการเปลี่ยนเส้นทาง HTTP ทำงานอย่างถูกต้อง เปิดตัวตรวจสอบเครือข่าย / firebug / สิ่งที่เบราว์เซอร์ของคุณมีเพื่อตรวจสอบคำขอ HTTP ขาออกและตรวจสอบการโทร HTTP คุณควรเห็นรายการแรก (การโพสต์ข้อมูลแบบฟอร์ม) เกิดขึ้นด้วยวิธีการ POST โดยส่งคืนรหัส HTTP 301 โดยมีส่วนหัวตำแหน่งที่ชี้ไปที่ตัวเองจากนั้นคุณจะเห็นแบบสอบถาม HTTP อื่นในหน้าเดียวกันโดยใช้เมธอด GET ทันที
CodeTwice

@CodeTwice: ในกรณีของฉัน page1 โพสต์ไปที่ page2, page2 ประมวลผลข้อมูลและแสดงเพจ (ที่มีการส่งค่าไปยังเทมเพลต) .. การเปลี่ยนเส้นทางควรเกิดขึ้นที่ไหน? .. เพจไม่มีคำขอ GET
user1050619

24

คุณต้องใช้ PRG - รูปแบบการโพสต์ / เปลี่ยนเส้นทาง / รับและคุณเพิ่งติดตั้ง P ของ PRG คุณจำเป็นต้องเปลี่ยนเส้นทาง (ปัจจุบันคุณไม่จำเป็นต้องเปลี่ยนเส้นทางเลยดูสิ่งนี้ )

PRG เป็นรูปแบบการออกแบบการพัฒนาเว็บที่ป้องกันการส่งแบบฟอร์มที่ซ้ำกันซึ่งหมายความว่าส่งแบบฟอร์ม (โพสต์คำขอ 1) -> เปลี่ยนเส้นทาง -> รับ (คำขอ 2)

Under the hood

รหัสสถานะการเปลี่ยนเส้นทาง - HTTP 1.0 พร้อม HTTP 302 หรือ HTTP 1.1 พร้อม HTTP 303

การตอบสนอง HTTP พร้อมรหัสสถานะการเปลี่ยนเส้นทางจะให้ URL เพิ่มเติมในฟิลด์ส่วนหัวของตำแหน่ง ตัวแทนผู้ใช้ (เช่นเว็บเบราว์เซอร์) ได้รับเชิญจากการตอบกลับด้วยรหัสนี้ให้ทำการร้องขอครั้งที่สองหรือเหมือนกันกับ URL ใหม่ที่ระบุในฟิลด์ตำแหน่ง

รหัสสถานะการเปลี่ยนเส้นทางคือเพื่อให้แน่ใจว่าในสถานการณ์นี้เบราว์เซอร์ของผู้ใช้เว็บสามารถรีเฟรชการตอบสนองของเซิร์ฟเวอร์ได้อย่างปลอดภัยโดยไม่ต้องส่งคำขอ HTTP POST เริ่มต้นอีกครั้ง

Double Submit Problem

ส่งปัญหาซ้ำสอง

Post/Redirect/Get Solution

โพสต์ / เปลี่ยนเส้นทาง / รับโซลูชัน

ที่มา


2
คำอธิบายที่ดี ฉันสงสัยว่าจะเกิดอะไรขึ้นหากผู้ใช้คลิกปุ่มย้อนกลับบนเบราว์เซอร์หลังจากทำการเปลี่ยนเส้นทาง ป๊อปอัป "ยืนยันการส่งแบบฟอร์มอีกครั้ง" จะปรากฏขึ้นอีกครั้ง ฉันเดาว่าแม้ว่าป๊อปอัปการยืนยันจะไม่ปรากฏขึ้นอีก แต่คำขอโพสต์ในหน้าที่ 1 ที่มีข้อมูลเดียวกันจะถูกสร้างขึ้นอีกครั้งและอีกครั้งผู้ใช้จะถูกเปลี่ยนเส้นทางไปยังหน้าที่ 2 ฉันมีชุดฟอร์ม form1, form2, form3 จะกลับจากฟอร์ม "n" ไปเป็น "n-1" ได้อย่างไร คำถามสุ่ม: คุณศึกษารูปแบบการออกแบบ PRG
ที่ไหน

@Rpant บน chrome ไฟล์ php ที่ส่งส่วนหัวตำแหน่งจะไม่ปรากฏในประวัติของเบราว์เซอร์ด้วยซ้ำดังนั้นปุ่มย้อนกลับจะพาคุณกลับไปที่แบบฟอร์ม
FluorescentGreen5

@ แองเจลินหลังจากเปลี่ยนเส้นทางจะรับข้อมูลได้อย่างไร? เช่นเมื่อฉันโพสต์ข้อมูลบางส่วนไปที่ / some_url และเปลี่ยนเส้นทางไปที่ / some_url ซึ่งส่งคำขอ GET ฉันจะรู้ได้อย่างไรว่าคำตอบนั้นควรเป็นอย่างไรเนื่องจาก / some_url เป็นแบบทั่วไปและไม่ได้ใช้ id เช่น / some_url / <id>
Amit Tripathi

@AmitTripathi คุณต้องสร้างตัวเอง เช่น: คำขอ POST อาจเป็นการแทรกข้อมูลลูกค้าในขณะที่ GET จะแสดงข้อมูลที่แทรกสำเร็จ คุณจะสามารถแสดงข้อมูลได้เหมือนเดิมนอกจากข้อมูลแบบฟอร์มหรือใช้มุมมองของหน้าลูกค้าซ้ำโดยดึงข้อมูลจากฐานข้อมูลสำหรับลูกค้ารายนั้นตามรหัสลูกค้าที่คุณได้รับหลังจากการแทรกลงในฐานข้อมูลสำเร็จ
Angelin Nadar

แต่ฉันเห็นด้วยกับ @Eugen Konkov solution
Angelin Nadar

10

โดยตรงคุณทำไม่ได้และนั่นเป็นสิ่งที่ดี การแจ้งเตือนของเบราว์เซอร์มีเหตุผล กระทู้นี้ควรตอบคำถามของคุณ:

ป้องกันไม่ให้ปุ่มย้อนกลับแสดงการแจ้งเตือนการยืนยัน POST

วิธีแก้ปัญหาหลักสองประการที่แนะนำคือรูปแบบ PRG และการส่ง AJAX ตามด้วยการย้ายตำแหน่งสคริปต์

โปรดทราบว่าหากวิธีการของคุณอนุญาตให้รับ GET และไม่ใช่วิธีการส่ง POST นั่นจะช่วยแก้ปัญหาได้และเข้ากันได้ดีกับการประชุม โซลูชันเหล่านี้มีให้ตามสมมติฐานที่คุณต้องการ / จำเป็นในการโพสต์ข้อมูล


8

วิธีเดียวที่จะมั่นใจได้ 100% ว่าแบบฟอร์มเดิมจะไม่ถูกส่งซ้ำสองครั้งคือการฝังตัวระบุที่ไม่ซ้ำกันในแต่ละรายการที่คุณออกและติดตามว่าแบบฟอร์มใดถูกส่งไปที่เซิร์ฟเวอร์ ข้อผิดพลาดคือหากผู้ใช้สำรองข้อมูลไปยังหน้าที่เป็นแบบฟอร์มและป้อนข้อมูลใหม่แบบฟอร์มเดียวกันจะไม่ทำงาน


6

คำตอบมีสองส่วน:

  1. ตรวจสอบให้แน่ใจว่าโพสต์ที่ซ้ำกันจะไม่ยุ่งกับข้อมูลของคุณทางฝั่งเซิร์ฟเวอร์ ในการดำเนินการนี้ให้ฝังตัวระบุที่ไม่ซ้ำกันในโพสต์เพื่อให้คุณสามารถปฏิเสธคำขอที่ตามมาในฝั่งเซิร์ฟเวอร์ รูปแบบนี้เรียกว่าIdempotent Receiverในแง่การส่งข้อความ

  2. ตรวจสอบให้แน่ใจว่าผู้ใช้ไม่ต้องกังวลกับความเป็นไปได้ในการส่งซ้ำจากทั้งสอง

    • เปลี่ยนเส้นทางไปยัง GET หลัง POST (รูปแบบ GET เปลี่ยนเส้นทาง POST)
    • ปิดการใช้งานปุ่มโดยใช้ javascript

สิ่งที่คุณทำภายใต้ 2 จะป้องกันการส่งซ้ำโดยสิ้นเชิง ผู้คนสามารถคลิกได้เร็วมากและแฮกเกอร์ก็สามารถโพสต์ได้ คุณต้อง 1. เสมอถ้าคุณต้องการให้แน่ใจว่าไม่มีรายการซ้ำ


2

หากคุณรีเฟรชเพจด้วยข้อมูล POST เบราว์เซอร์จะยืนยันการส่งใหม่ของคุณ หากคุณใช้ GET data ข้อความจะไม่แสดง นอกจากนี้คุณยังสามารถมีหน้าที่สองหลังจากบันทึกการส่งแล้วให้เปลี่ยนเส้นทางไปยังหน้าที่สามโดยไม่มีข้อมูล


2

ฉันพบว่าไม่มีใครพูดถึงเคล็ดลับนี้

หากไม่มีการเปลี่ยนเส้นทางคุณยังคงสามารถป้องกันการยืนยันแบบฟอร์มได้เมื่อรีเฟรช

โดยค่าเริ่มต้นรหัสฟอร์มจะเป็นดังนี้:

<form method="post" action="test.php">

ตอนนี้เปลี่ยนเป็น <form method="post" action="test.php?nonsense=1">

คุณจะได้เห็นความมหัศจรรย์

ฉันเดาว่าเป็นเพราะเบราว์เซอร์จะไม่เรียกป๊อปอัปการแจ้งเตือนการยืนยันหากได้รับเมธอด GET (สตริงการสืบค้น) ใน url


2

คุณสามารถทำได้โดยใช้ jquery

<script>
   $(document).ready(function(){
   window.history.replaceState('','',window.location.href)
   });
</script>

นี่เป็นวิธีที่ดีที่สุดในการป้องกันข้อมูลอีกครั้งหลังจากส่งเนื่องจากการโพสต์ย้อนกลับ

หวังว่านี่จะช่วยได้


0

รูปแบบ PRG สามารถป้องกันการส่งซ้ำที่เกิดจากการรีเฟรชหน้าเท่านั้น นี่ไม่ใช่มาตรการที่ปลอดภัย 100%

โดยปกติฉันจะดำเนินการด้านล่างเพื่อป้องกันการส่งซ้ำ:

  1. ฝั่งไคลเอ็นต์ - ใช้จาวาสคริปต์เพื่อป้องกันการคลิกซ้ำบนปุ่มซึ่งจะทริกเกอร์การส่งแบบฟอร์ม คุณสามารถปิดใช้งานปุ่มได้หลังจากคลิกครั้งแรก

  2. ฝั่งเซิร์ฟเวอร์ - ฉันจะคำนวณแฮชในพารามิเตอร์ที่ส่งและบันทึกแฮชนั้นในเซสชันหรือฐานข้อมูลดังนั้นเมื่อได้รับการส่งซ้ำเราสามารถตรวจพบการทำซ้ำจากนั้นจึงตอบสนองต่อไคลเอนต์อย่างเหมาะสม อย่างไรก็ตามคุณสามารถจัดการสร้างแฮชได้ที่ฝั่งไคลเอ็นต์

โดยส่วนใหญ่มาตรการเหล่านี้สามารถช่วยป้องกันการส่งซ้ำได้


0

ฉันชอบคำตอบของแองเจลินมาก แต่ถ้าคุณกำลังจัดการกับรหัสเดิมที่ใช้ไม่ได้จริงเทคนิคนี้อาจใช้ได้ผลสำหรับคุณ

ที่ด้านบนของไฟล์

// Protect against resubmits
if (empty($_POST))  {
   $_POST['last_pos_sub'] = time();
} else {
     if (isset($_POST['last_pos_sub'])){
        if ($_POST['last_pos_sub'] == $_SESSION['curr_pos_sub']) {
           redirect back to the file so POST data is not preserved
        }
        $_SESSION['curr_pos_sub'] = $_POST['last_pos_sub'];
     }
}

จากนั้นในตอนท้ายของแบบฟอร์มให้ปฏิบัติlast_pos_subดังนี้:

<input type="hidden" name="last_pos_sub" value=<?php echo $_POST['last_pos_sub']; ?>>

0

ลองทริส:

function prevent_multi_submit($excl = "validator") {
    $string = "";
    foreach ($_POST as $key => $val) {
    // this test is to exclude a single variable, f.e. a captcha value
    if ($key != $excl) {
        $string .= $key . $val;
    }
    }
    if (isset($_SESSION['last'])) {
    if ($_SESSION['last'] === md5($string)) {
        return false;
    } else {
        $_SESSION['last'] = md5($string);
        return true;
    }
    } else {
    $_SESSION['last'] = md5($string);
    return true;
    }
}

วิธีใช้ / ตัวอย่าง:

if (isset($_POST)) {
    if ($_POST['field'] != "") { // place here the form validation and other controls
    if (prevent_multi_submit()) { // use the function before you call the database or etc
        mysql_query("INSERT INTO table..."); // or send a mail like...
        mail($mailto, $sub, $body); // etc
    } else {
        echo "The form is already processed";
    }
    } else {
    // your error about invalid fields
    }
}

แบบอักษร: https://www.tutdepot.com/prevent-multiple-form-submission/


0

ใช้ js เพื่อป้องกันการเพิ่มข้อมูล:

if ( window.history.replaceState ) {
    window.history.replaceState( null, null, window.location.href );
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.