จะป้องกันไม่ให้ส่งแบบฟอร์มหลายครั้งจากฝั่งลูกค้าได้อย่างไร?


97

บางครั้งเมื่อการตอบกลับช้าอาจมีการคลิกปุ่มส่งหลายครั้ง

จะป้องกันไม่ให้เกิดขึ้นได้อย่างไร?


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

52
Paolo: ใช่หากคุณสนใจเรื่องความปลอดภัยของข้อมูลที่สมบูรณ์ควรทำฝั่งเซิร์ฟเวอร์ แต่บางครั้งคุณแค่ต้องการป้องกันไม่ให้คุณยายดับเบิลคลิกที่ปุ่มส่งเมื่อต้องการเพียงแค่คลิกเดียว ในกรณีเหล่านี้จาวาสคริปต์ก็ใช้ได้ดี
Simon East

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

ไม่ไม่ดับเบิลส่งแบบคุกกี้การโจมตีการป้องกัน CSRF ยังป้องกันการส่งแบบฟอร์ม mutliple?
Martin Thoma

คำตอบ:


100

ใช้จาวาสคริปต์ที่ไม่สร้างความรำคาญเพื่อปิดใช้งานการส่งเหตุการณ์ในแบบฟอร์มหลังจากที่ส่งไปแล้ว นี่คือตัวอย่างการใช้ jQuery

แก้ไข: แก้ไขปัญหาในการส่งแบบฟอร์มโดยไม่คลิกปุ่มส่ง ขอบคุณ ichiban

$("body").on("submit", "form", function() {
    $(this).submit(function() {
        return false;
    });
    return true;
});

4
จะเกิดอะไรขึ้นหากส่งแบบฟอร์มโดยกด Enter ในกล่องข้อความ?
ichiban

2
คุณต้องการแม้กระทั่งreturn true? ฉันคิดว่าการส่งครั้งแรกควรใช้งานได้หากไม่มีสิ่งนั้น
Simon East

ฉันเชื่อว่าreturn trueโดยนัยหากละเว้น
Derek

16
ฉันลองวิธีแก้ปัญหานี้ด้วยการตรวจสอบความถูกต้องและ MVC ที่ไม่สร้างความรำคาญ JS จะถูกเรียกก่อนซึ่งจะส่งคืนเท็จเสมอจากนั้นจึงเรียกการตรวจสอบความถูกต้อง ดังนั้นหากแบบฟอร์มของฉันมีข้อผิดพลาดในการตรวจสอบความถูกต้องจะไม่ถูกส่งแบบฟอร์ม !!
bjan

6
ใช้งานได้ดีจริงๆ ฉันต้องการปิดการใช้งานปุ่มส่งและแทนที่ข้อความปุ่มส่งเพื่อแสดงคำใบ้ผู้ใช้เช่นกัน $(this).find("input[type='submit']").attr('disabled', 'disabled').val('submiting'); return true; });
Cam Song

44

ฉันลองใช้โซลูชันของ vansteeพร้อมกับการตรวจสอบความถูกต้องของ asp mvc 3 ที่ไม่สร้างความรำคาญและหากการตรวจสอบความถูกต้องของไคลเอ็นต์ล้มเหลวโค้ดจะยังคงทำงานอยู่และการส่งแบบฟอร์มจะถูกปิดใช้งาน ฉันไม่สามารถส่งใหม่ได้หลังจากแก้ไขช่องแล้ว (ดูความคิดเห็นของ bjan)

ดังนั้นฉันจึงแก้ไขสคริปต์ของ vanstee ดังนี้:

$("form").submit(function () {
    if ($(this).valid()) {
        $(this).submit(function () {
            return false;
        });
        return true;
    }
    else {
        return false;
    }
});

ผลข้างเคียงอื่น ๆ ที่ควรทราบหรือมีผลให้คุณนำไปใช้ได้ทุกที่หรือไม่?
Ciaran Gallagher

24

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

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

ในหมายเหตุอื่น llimllib เป็นจุดที่ถูกต้องมาก การตรวจสอบแบบฟอร์มทั้งหมดจะต้องเกิดขึ้นที่ฝั่งเซิร์ฟเวอร์ ซึ่งรวมถึงการตรวจสอบการส่งหลายรายการ อย่าไว้ใจลูกค้า! นี่ไม่ใช่เฉพาะกรณีที่จาวาสคริปต์ถูกปิดใช้งาน คุณต้องทราบว่าโค้ดฝั่งไคลเอ็นต์ทั้งหมดสามารถแก้ไขได้ มันค่อนข้างยากที่จะจินตนาการ แต่ html / javascript ที่คุยกับเซิร์ฟเวอร์ของคุณไม่จำเป็นต้องเป็น html / javascript ที่คุณเขียน

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

แน่นอนว่านาน ๆ ครั้งคุณจะต้องล้างตัวระบุที่ไม่เคยส่งข้อมูลในรูปแบบใด ๆ แต่ส่วนใหญ่แล้วเว็บไซต์ของคุณอาจใช้กลไก "เก็บขยะ" อยู่แล้ว


15
<form onsubmit="if(submitted) return false; submitted = true; return true">

6
การใช้<form onsubmit="return (typeof submitted == 'undefined') ? (submitted = true) : !submitted">หรือการเขียนที่จุดทางขวาของสคริปต์ของคุณเพื่อหลีกเลี่ยงข้อผิดพลาดต่อไปนี้:var submitted = false; Uncaught ReferenceError: submitted is not defined
TamásBolvári

15

วิธีง่ายๆในการทำมีดังนี้

<form onsubmit="return checkBeforeSubmit()">
  some input:<input type="text">
  <input type="submit" value="submit" />
</form>

<script type="text/javascript">
  var wasSubmitted = false;    
    function checkBeforeSubmit(){
      if(!wasSubmitted) {
        wasSubmitted = true;
        return wasSubmitted;
      }
      return false;
    }    
</script>

ด้วยการตรวจสอบความถูกต้อง jQuery ที่ไม่สร้างความรำคาญสคริปต์การบล็อกจะถูกเรียกก่อนและการตรวจสอบความถูกต้องถัดไปซึ่งจะไม่ส่งผลเลยหากมีข้อผิดพลาดในการตรวจสอบ
bjan

8

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


ใช่ Shoban เคล็ดลับที่ดีฉันเห็นด้วย ควรจะเป็นform page ---submits to---> form_submission_script.php ---after saving, redirects to---> form_thankyou.html
Simon East

7

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

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


อย่าดู "มากเกินไป"? ;-)
Shoban

1
ดูเหมือนว่าจะป้องกันไม่ให้ผู้ใช้ส่งแบบฟอร์มมากกว่าหนึ่งครั้งต่อวินาที ถ้าฉันส่งแบบฟอร์มในเวลา 2009-05-29 12:13:37 เซิร์ฟเวอร์จะไม่ให้ฉันส่งแบบฟอร์มอื่นที่มีแฮชเดียวกัน แต่ถ้าฉันคลิกส่งที่ 2009-05-29 12:13:38 มันจะผ่านไป นอกจากนี้เทคนิคของคุณยังป้องกันไม่ให้ผู้ใช้ 2 คนส่งแบบฟอร์มที่แตกต่างกันสองแบบพร้อมกัน: หนึ่งในนั้นจะถูกไล่ออกแม้ว่าจะเป็นการส่งครั้งแรกจากเขาก็ตาม
Sarah Vessels

2
@moneypenny การคัดค้านครั้งแรกของคุณเป็นเท็จแฮชจะเป็นเวลาที่ส่งแบบฟอร์มไปยังผู้ใช้ไม่ใช่เวลาที่พวกเขาส่ง หากคุณกังวลเรื่องที่สองอย่างแท้จริงคุณมีทางเลือกมากมาย เพิ่มรหัสเซสชันของพวกเขาในสตริงที่คุณแฮชและ / หรือตรวจสอบฟิลด์ทั้งหมดของแบบฟอร์มเพื่อหาความเหมือนกัน ผู้ใช้สองคนอาจไม่ได้ส่งแบบฟอร์มเดียวกันทั้งหมด
llimllib

@Shoban ฉันขอโทษฉันไม่ทำตามที่คุณพูด
llimllib

2
"มากเกินไป" อยู่ในสายตาของผู้มอง หากคุณต้องการป้องกันการส่งแบบฟอร์มที่ซ้ำกันจริงๆโซลูชันอย่างน้อยส่วนหนึ่งจะต้องเป็นฝั่งเซิร์ฟเวอร์ "คุณไม่สามารถไว้วางใจผู้ใช้ได้"
Ben Blank


5

การใช้ JQuery คุณสามารถทำได้:

$('input:submit').click( function() { this.disabled = true } );

&

   $('input:submit').keypress( function(e) {
     if (e.which == 13) {
        this.disabled = true 
     } 
    }
   );

1
จะเกิดอะไรขึ้นหากส่งแบบฟอร์มโดยการกด ENTER ในกล่องข้อความหลังจากปิดใช้งานปุ่ม
ichiban

การปิดใช้งานปุ่มลักษณะนี้จะป้องกันไม่ให้ผู้ใช้กด ENTER จากนั้น แต่จำเป็นต้องผูกแป้นกดบนอินพุตส่ง
Boris Guéry

ซึ่งจะป้องกันไม่ให้ปุ่มส่ง ดังนั้นการปิดใช้งานการตรวจสอบฝั่งเซิร์ฟเวอร์
Marko Aleksić

@Marko ใช่ แต่นั่นคือ OP ขออย่างไรก็ตามการใช้โทเค็นจะป้องกันไม่ให้ส่งแบบฟอร์มซ้ำสองครั้ง
Boris Guéry

1
เมื่อฉันลองใช้เทคนิคประเภทนี้ฉันพบว่าแบบฟอร์มจะไม่ส่งเลย (อย่างน้อยก็ไม่ใช่ใน Chrome 14) น่าจะเป็นเพราะคุณปิดใช้งานปุ่มก่อนที่เบราว์เซอร์จะเริ่มการส่งแบบฟอร์ม
Simon East

4

ฉันรู้ว่าคุณติดแท็กคำถามของคุณด้วย 'javascript' แต่นี่คือวิธีแก้ปัญหาที่ไม่ขึ้นอยู่กับจาวาสคริปต์เลย:

เป็นรูปแบบเว็บแอปชื่อPRGและนี่คือบทความดีๆที่อธิบาย


4

คุณสามารถป้องกันการส่งหลายครั้งได้ง่ายๆด้วย:

var Workin = false;

$('form').submit(function()
{
   if(Workin) return false;
   Workin =true;

   // codes here.
   // Once you finish turn the Workin variable into false 
   // to enable the submit event again
   Workin = false;

});

Workin =true;ปัญหาเกี่ยวกับคำตอบของคุณคือการที่มีสองแยกระหว่างเวลาที่ผู้ใช้คลิกที่และเวลา การส่งซ้ำยังทำได้ แต่มีโอกาสน้อยกว่า
sent1nel

4

คำตอบที่ง่ายที่สุดสำหรับคำถามนี้ตามที่ถาม: "บางครั้งเมื่อคำตอบช้าอาจมีคนคลิกปุ่มส่งหลายครั้งจะป้องกันไม่ให้เกิดเหตุการณ์นี้ได้อย่างไร"

เพียงปิดการใช้งานปุ่มส่งแบบฟอร์มเช่นรหัสด้านล่าง

<form ... onsubmit="buttonName.disabled=true; return true;">
  <input type="submit" name="buttonName" value="Submit">
</form>

จะปิดใช้งานปุ่มส่งเมื่อคลิกครั้งแรกเพื่อส่ง นอกจากนี้หากคุณมีกฎการตรวจสอบความถูกต้องก็จะใช้งานได้ดี หวังว่ามันจะช่วยได้


ฉันคิดว่าคุณขาดอะไรบางอย่างโปรดโพสต์รหัสของคุณ
Imran Khan

3

ในฝั่งไคลเอ็นต์คุณควรปิดใช้งานปุ่มส่งเมื่อส่งแบบฟอร์มด้วยรหัสจาวาสคริปต์เช่นเดียวกับวิธีการที่ @vanstee และ @chaos ให้ไว้

แต่มีปัญหาเกี่ยวกับความล่าช้าของเครือข่ายหรือสถานการณ์ที่ปิดใช้งานจาวาสคริปต์ซึ่งคุณไม่ควรพึ่งพา JS เพื่อป้องกันไม่ให้สิ่งนี้เกิดขึ้น

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


ฉันรู้ว่าสิ่งนี้ได้รับคำตอบเมื่อนานมาแล้ว แต่มีคนอธิบายได้ว่าเน็ตเวิร์กแล็กอาจป้องกันการปิดใช้งานได้อย่างไร ตอนนี้ฉันกำลังจัดการกับปัญหานี้และฉันสับสนมากว่ามีการส่งแบบฟอร์มสองครั้งได้อย่างไรแม้ว่าฉันจะปิดใช้งานปุ่มนี้ก็ตาม ฉันไม่เคยคิดเลยว่าจะปิดการใช้งาน JavaScript ฉันไม่คิดว่านั่นเป็นปัญหาในกรณีของฉัน แต่เป็นข้อมูลเชิงลึกที่น่าสนใจ
davidatthepark

ฉันกำลังพิจารณาที่จะควบคุมตัวจัดการการคลิกด้วย
davidatthepark

คุณจะตรวจสอบฝั่งเซิร์ฟเวอร์ได้อย่างไรหากส่งจากไคลเอนต์เดียวกัน ตามที่อยู่ IP ของผู้ใช้? ถ้าเป็นเช่นนั้นคุณจะ "บันทึก" ที่อยู่ IP ฝั่งเซิร์ฟเวอร์จากรายการที่ส่งไปก่อนหน้านี้ที่ไหน (ฉันใช้โหนดถ้าบริบทนั้นจำเป็น)
user1063287

3

คุณสามารถลองใช้ปลั๊กอินSafeform jquery

$('#example').safeform({
    timeout: 5000, // disable form on 5 sec. after submit
    submit: function(event) {
        // put here validation and ajax stuff...

        // no need to wait for timeout, re-enable the form ASAP
        $(this).safeform('complete');
        return false;
    }
})

1

วิธีแก้ปัญหาที่ง่ายและสง่างามที่สุดสำหรับฉัน:

function checkForm(form) // Submit button clicked
{
    form.myButton.disabled = true;
    form.myButton.value = "Please wait...";
    return true;
}

<form method="POST" action="..." onsubmit="return checkForm(this);">
    ...
    <input type="submit" name="myButton" value="Submit">
</form>

ลิงค์เพิ่มเติม ...


1

ใช้รหัสนี้ในแบบฟอร์มของคุณซึ่งจะรองรับการคลิกหลายครั้ง

<script type="text/javascript">
    $(document).ready(function() {
        $("form").submit(function() {
            $(this).submit(function() {
                return false;
            });
            return true;
        });     
    }); 
</script>

มันจะทำงานได้อย่างแน่นอน


1

อนุญาตให้ส่งทุก 2 วินาที ในกรณีของการตรวจสอบความถูกต้องด้านหน้า

$(document).ready(function() {
    $('form[debounce]').submit(function(e) {
        const submiting = !!$(this).data('submiting');

        if(!submiting) {
            $(this).data('submiting', true);

            setTimeout(() => {
                $(this).data('submiting', false);
            }, 2000);

            return true;
        }

        e.preventDefault();
        return false;
    });
})

0

วิธีที่ดีที่สุดในการป้องกันการส่งหลายรายการคือเพียงแค่ส่งรหัสปุ่มในวิธีการ

    function DisableButton() {
        document.getElementById("btnPostJob").disabled = true;
    }
    window.onbeforeunload = DisableButton; 

0

การทำเช่นนี้โดยใช้จาวาสคริปต์นั้นค่อนข้างง่าย ต่อไปนี้เป็นรหัสที่จะให้ฟังก์ชันที่ต้องการ:

$('#disable').on('click', function(){
    $('#disable').attr("disabled", true);
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="disable">Disable Me!</button>


0

วิธีแก้ปัญหาที่ง่ายที่สุดคือปิดการใช้งานปุ่มเมื่อคลิกเปิดใช้งานหลังจากการดำเนินการเสร็จสิ้น ในการตรวจสอบโซลูชันที่คล้ายกันบน jsfiddle:

[click here][1]

และคุณจะพบคำตอบอื่น ๆ สำหรับคำตอบนี้


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

0

วิธีนี้ใช้ได้ดีมากสำหรับฉัน ส่งฟาร์มและทำให้ปุ่มปิดใช้งานและหลังจาก 2 วินาทีเปิดใช้งานปุ่ม

<button id="submit" type="submit" onclick="submitLimit()">Yes</button>

function submitLimit() {
var btn = document.getElementById('submit')
setTimeout(function() {
    btn.setAttribute('disabled', 'disabled');
}, 1);

setTimeout(function() {
    btn.removeAttribute('disabled');
}, 2000);}

ใน ECMA6 Syntex

function submitLimit() {
submitBtn = document.getElementById('submit');

setTimeout(() => { submitBtn.setAttribute('disabled', 'disabled') }, 1);

setTimeout(() => { submitBtn.removeAttribute('disabled') }, 4000);}

0

เพียงเพื่อเพิ่มคำตอบที่เป็นไปได้โดยไม่ผ่านการตรวจสอบการป้อนข้อมูลของเบราว์เซอร์

$( document ).ready(function() {
    $('.btn-submit').on('click', function() {
        if(this.form.checkValidity()) {
            $(this).attr("disabled", "disabled");
            $(this).val("Submitting...");
            this.form.submit();
        }
    });
});

0

อีกทางเลือกหนึ่งสำหรับสิ่งที่เสนอก่อนหน้านี้คือ:

jQuery('form').submit(function(){
     $(this).find(':submit').attr( 'disabled','disabled' );
     //the rest of your code
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.