วิธีป้องกัน Subscription Bombing บนแบบฟอร์มสมัครสมาชิก
ป้องกันการโจมตีแบบ Subscription Bombing ที่ใช้ฟอร์มของคุณส่งอีเมลขยะจำนวนมากไปยังเหยื่อ เรียนรู้วิธีใช้ระบบ Confirmed Opt-in และการตั้งค่า Rate Limits เพื่อหยุดปัญหานี้อย่างเด็ดขาด
Subscription bombing คืออะไร
Subscription bombing คือการโจมตีที่ใช้แบบฟอร์มการสมัครสมาชิกของคุณเพื่อทำให้กล่องจดหมายของผู้อื่นเต็มไปด้วยอีเมลขยะ ผู้โจมตีจะนำที่อยู่อีเมลของเหยื่อไปกรอกลงในแบบฟอร์มที่ไม่มีการป้องกันหลายร้อยหรือหลายพันรายการในช่วงเวลาสั้นๆ เว็บไซต์แต่ละแห่งจะส่งข้อความต้อนรับหรือข้อความยืนยันไปยังที่อยู่นั้น ข้อความจำนวนมหาศาลเหล่านี้จะกลบอีเมลที่เหยื่อจำเป็นต้องอ่านจริงๆ
เป้าหมายของการโจมตีคือเจ้าของกล่องจดหมายดังกล่าว ในขณะที่กล่องจดหมายเต็มไปด้วยข้อความยืนยันการสมัครสมาชิก ผู้โจมตีจะใช้จ่ายเงินผ่านบัตรเครดิตของบุคคลนั้นหรือรีเซ็ตรหัสผ่านบัญชีต่างๆ ของพวกเขา แม้ว่าการแจ้งเตือนการฉ้อโกงจากธนาคารจะถูกส่งมา แต่ข้อความนั้นจะถูกฝังอยู่ใต้ข้อความอีกสองพันฉบับที่เข้ามาในชั่วโมงเดียวกัน ทำให้ไม่มีใครเห็นการแจ้งเตือนนั้นได้ทันเวลา
เซิร์ฟเวอร์ของคุณเป็นเครื่องมือที่ใช้ในการโจมตีนี้ โดยที่ไม่มีสิ่งใดบนเซิร์ฟเวอร์ของคุณเสียหาย และไม่มีบัญชีใดของคุณถูกบุกรุก มีเพียงบุคคลหนึ่งกรอกที่อยู่อีเมลลงในแบบฟอร์มสาธารณะ และซอฟต์แวร์ของคุณก็ทำงานตามที่ถูกเขียนไว้ นั่นคือการส่งอีเมลไปยังที่อยู่นั้น สิ่งนี้ทำให้การตรวจพบทำได้ยาก เนื่องจากไม่มีการบุกรุกเกิดขึ้นใน log ของคุณ เพราะไม่มีการบุกรุกใดๆ เกิดขึ้นจริง
ลักษณะการโจมตีที่ปรากฏในฝั่งของคุณ
การโจมตีจะมาในสองรูปแบบ
รูปแบบที่รุนแรงคือการโจมตีแบบฉับพลัน (burst) โดยมีคำขอ POST หลายร้อยรายการพุ่งเข้าหาฟอร์มเดียวภายในเวลาไม่กี่นาที จาก IP แอดเดรสต้นทางจำนวนมาก ซึ่งมีที่อยู่อีเมลในโดเมนที่คุณไม่เคยส่งไปมาก่อน การโจมตีรูปแบบนี้สังเกตได้ง่ายเมื่อคุณตรวจสอบ
รูปแบบที่เงียบเชียบคือสิ่งที่มักถูกมองข้าม ผู้โจมตีถือรายการฟอร์มที่เปราะบางไว้หลายพันรายการ ดังนั้นฟอร์มของคุณจึงได้รับข้อมูลเพียงหนึ่งหรือสองรายการต่อชั่วโมงเท่านั้น Jye Cusch ได้อธิบายถึงการโจมตีในรูปแบบนี้ไว้ บนเว็บไซต์ที่เขาดูแล โดยไม่มีการพุ่งขึ้นของทราฟฟิก มีเพียงการสมัครสมาชิกที่เข้ามาอย่างต่อเนื่องในเวลาที่ไม่สอดคล้องกับกลุ่มเป้าหมายของเขา ฟอร์มเดียวอาจดูเหมือนไม่มีพิษมีภัยเพราะมันแทบไม่ได้ทำอะไรเลย แต่ความเสียหายคือผลรวมจากทุกฟอร์มในรายการของผู้โจมตี
ทั้งสองรูปแบบมีลักษณะร่วมกันอย่างหนึ่งหลังจากนั้นคือ ไม่มีอะไรเกิดขึ้นต่อจากนั้น ที่อยู่อีเมลเหล่านั้นไม่เคยยืนยันตัวตน ไม่เคยเปิดข้อความ และไม่เคยคลิกลิงก์ ในรายการที่ต้องยืนยันการสมัคร (confirmed opt-in) อีเมลเหล่านั้นจะค้างอยู่ที่สถานะ unconfirmed ตลอดไป และกองข้อมูลเหล่านั้นคือหลักฐานที่ชัดเจนที่สุดที่คุณจะได้รับ
เริ่มต้นด้วยการนับจำนวนการส่งข้อมูลต่อนาทีใน access log ของคุณ
sudo awk '/POST \/subscription\/form/ {print substr($4, 2, 17)}' \
/var/log/nginx/access.log | uniq -c | sort -rn | head$4 ในรูปแบบ log มาตรฐาน (combined log format) คือการประทับเวลาในวงเล็บ ดังนั้นคำสั่งนี้จะแสดงจำนวนนับสำหรับแต่ละนาที โดยเรียงลำดับจากมากไปน้อย ฟอร์มที่ปกติมีการสมัครสี่รายการต่อวัน แต่กลับแสดงผลหกสิบรายการในหนึ่งนาที ย่อมไม่ใช่สถานการณ์ปกติอย่างแน่นอน
การยืนยันการสมัคร (Confirmed opt-in): แนวทางป้องกันที่มีประสิทธิภาพสูงสุด
การยืนยันการสมัคร หรือที่มักเรียกกันว่า double opt-in หมายถึงที่อยู่อีเมลนั้นจะยังไม่ถือว่าเป็นสมาชิกจนกว่าเจ้าของอีเมลจะคลิกลิงก์ในข้อความที่ส่งไปยังที่อยู่นั้น หากเปิดใช้งานฟีเจอร์นี้ ที่อยู่อีเมลที่ถูกส่งเข้ามาจะได้รับข้อความเพียงฉบับเดียวเท่านั้น ที่อยู่นั้นจะไม่ถูกเพิ่มเข้าในรายชื่อสมาชิก จึงไม่ได้รับแคมเปญหรือข้อความต้อนรับใดๆ
ใน listmonk, เซิร์ฟเวอร์จดหมายข่าวแบบ self-hosted การตั้งค่านี้จะทำแยกตามรายชื่อสมาชิก โดยรายชื่อหนึ่งๆ จะเป็นแบบ single opt-in หรือ double opt-in ก็ได้ เอกสารประกอบได้อธิบายความแตกต่างไว้อย่างชัดเจนว่า ในรายชื่อแบบ double opt-in สมาชิกจะต้อง "ยอมรับการสมัครอย่างชัดแจ้งโดยการคลิกที่อีเมลยืนยันที่ได้รับ จนกว่าจะถึงตอนนั้น สมาชิกจะไม่ได้รับข้อความแคมเปญใดๆ" สมาชิกจะค้างอยู่ที่สถานะ unconfirmed และจะเปลี่ยนเป็น confirmed เมื่อคลิกยืนยัน และมีเพียงสมาชิกสถานะ confirmed ในรายชื่อแบบ opt-in เท่านั้นที่จะได้รับอีเมลแคมเปญ
ควรทำความเข้าใจให้ชัดเจนว่าสิ่งนี้ช่วยคุณได้อย่างไร การยืนยันการสมัครไม่ได้ทำให้จำนวนข้อความที่คุณส่งออกเป็นศูนย์ แต่เป็นการจำกัดไว้ที่หนึ่งข้อความต่อหนึ่งที่อยู่อีเมลเท่านั้น เหยื่อยังคงได้รับข้อความนั้นอยู่ และการได้รับข้อความหนึ่งฉบับจากเว็บไซต์นับพันแห่งก็ถือเป็นการโจมตีที่สมบูรณ์แบบแล้ว สิ่งที่การยืนยันการสมัครช่วยกำจัดออกไปคือทุกสิ่งที่ตามมาหลังจากนั้น รายชื่อสมาชิกของคุณจะสะอาด และคุณจะไม่ส่งข้อความฉบับที่สองไปยังผู้ที่ไม่เคยร้องขอฉบับแรก
มีการตั้งค่าอีกสองรายการที่มีความสำคัญและมักถูกลืม ประการแรก คือการจำกัดจำนวนการส่งอีเมลยืนยันซ้ำ หากที่อยู่อีเมลเดิมสามารถถูกส่งเข้ามาและได้รับอีเมลยืนยันใหม่ได้ทุกครั้ง ผู้โจมตีก็ไม่จำเป็นต้องใช้แบบฟอร์มจากหลายแหล่ง เพราะแบบฟอร์มของคุณเพียงอย่างเดียวก็สามารถส่งข้อความได้นับพันฉบับ ที่อยู่อีเมลที่อยู่ในสถานะ unconfirmed ในรายชื่อนั้นแล้ว ไม่ควรได้รับอะไรเพิ่มเติมเป็นเวลาอย่างน้อยหนึ่งวัน ประการที่สอง คือการลบแถวข้อมูลที่ยังไม่ได้ยืนยันตามกำหนดเวลา ที่อยู่อีเมลที่ไม่ได้ยืนยันภายใน 30 วัน ไม่ถือว่าเป็นสมาชิกที่รอการอนุมัติ การเก็บข้อมูลเหล่านี้ไว้มีแต่จะสร้างความเสี่ยงที่ระบบอาจส่งอีเมลไปหาโดยไม่ได้ตั้งใจในภายหลัง
จำกัดอัตราการเข้าถึงฟอร์มสมัครสมาชิกที่ reverse proxy
ให้กำหนดขีดจำกัดไว้ที่หน้าแอปพลิเคชันแทนที่จะกำหนดไว้ภายในตัวแอปเอง คำขอที่ถูกบล็อกที่ proxy จะไม่เปิดการเชื่อมต่อฐานข้อมูลและไม่เริ่มการสนทนา SMTP (simple mail transfer protocol) การจำกัดภายในแอปพลิเคชันจะทำงานหลังจากที่คำขอนั้นใช้ทรัพยากร worker process และ query ไปแล้ว และในหลาย stack ข้อความจะถูกนำไปเข้าคิว (queue) ก่อนที่การตรวจสอบการใช้งานในทางที่ผิดจะเริ่มทำงาน นอกจากนี้ ขีดจำกัดที่ proxy ยังคงอยู่แม้จะมีการอัปเกรดแอปพลิเคชัน เพราะไม่ได้อยู่ในโค้ดที่คุณแทนที่
ตัวอย่างด้านล่างนี้ใช้ nginx แนวคิดนี้สามารถนำไปใช้กับ reverse proxy ใดก็ตามที่คุณใช้หน้าแอป แม้ว่าชื่อ directive จะแตกต่างกันก็ตาม
ใส่สิ่งนี้ลงในบล็อก http ในไฟล์เช่น /etc/nginx/conf.d/signup-limit.conf:
map $request_method $signup_key {
POST $binary_remote_addr;
default "";
}
limit_req_zone $signup_key zone=signup:10m rate=2r/m;
limit_req_status 429;
limit_req_log_level warn;map กำลังทำงานจริง nginx จะไม่นับคำขอที่มี key เป็นสตริงว่าง ดังนั้นเฉพาะคำขอ POST เท่านั้นที่จะเข้าสู่ zone ผู้ใช้งานที่โหลดหน้าสมัครสมาชิกหลายครั้งจะไม่เสียโควตา หากไม่มี map นี้ ผู้ที่กดรีเฟรชหน้าเว็บสองครั้งจะใช้โควตาของตนเองหมดก่อนที่จะได้ส่งข้อมูลใดๆ
$binary_remote_addr คือที่อยู่ของไคลเอนต์ในรูปแบบ packed ซึ่งเป็นเหตุผลว่าทำไม zone ขนาด 10 เมกะไบต์จึงเก็บข้อมูลได้ประมาณ 160,000 รายการ rate=2r/m อนุญาตให้ส่งข้อมูลได้หนึ่งครั้งทุกสามสิบวินาที limit_req_status 429 จะส่งคืน HTTP 429 Too Many Requests แทนค่าเริ่มต้น 503 ของ nginx ซึ่งเป็นรหัสที่ถูกต้องและเป็นสิ่งที่ไลบรารีของไคลเอนต์คาดหวัง
จากนั้นในบล็อก server สำหรับเว็บไซต์ของคุณ:
location = /subscription/form {
limit_req zone=signup burst=3 nodelay;
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}burst=3 nodelay จะอนุญาตให้คนที่เผลอกดปุ่มสองครั้งผ่านไปได้ และปฏิเสธคำขอที่สี่ทันทีแทนที่จะนำไปเข้าคิว
sudo nginx -t && sudo systemctl reload nginxnginx -t ควรแสดงผล configuration file /etc/nginx/nginx.conf test is successful ตอนนี้ให้ส่งฟอร์มอย่างรวดเร็วห้าครั้งและสังเกต log ของข้อผิดพลาด:
sudo tail -f /var/log/nginx/error.logคำขอที่ถูกบล็อกจะเขียนหนึ่งบรรทัด และนี่คือสตริงที่คุณกำลังมองหา:
2026/08/13 09:14:22 [warn] 812#812: *4412 limiting requests, excess: 3.400 by zone "signup", client: 203.0.113.10, server: news.example.com, request: "POST /subscription/form HTTP/1.1", host: "news.example.com"หากไม่มีบรรทัดใดปรากฏขึ้น แสดงว่าขีดจำกัดไม่ได้ถูกนำไปใช้ สาเหตุทั่วไปคือ limit_req อยู่ในบล็อก location ที่คำขอไม่เคยเข้าถึง ดังนั้นให้ตรวจสอบด้วย curl -si -X POST https://news.example.com/subscription/form สองสามครั้งติดต่อกันและยืนยันว่าคุณได้รับ 429
มีกับดักสองประการที่ควรทราบก่อนที่คุณจะพึ่งพาขีดจำกัดต่อ IP
เมื่ออยู่หลัง CDN หรือ proxy อื่น $binary_remote_addr จะเป็น proxy นั้น ผู้เยี่ยมชมทุกคนจะถูกจัดอยู่ในกลุ่มเดียวกัน ดังนั้นการส่งข้อมูลเพียงไม่กี่ครั้งในแต่ละนาทีจะบล็อกทุกคนที่เหลือ แก้ไขปัญหานี้ด้วยโมดูล real IP: set_real_ip_from สำหรับช่วง IP แต่ละช่วงที่ CDN ของคุณประกาศไว้ (Cloudflare แสดงรายการของพวกเขาที่ cloudflare.com/ips) และ real_ip_header CF-Connecting-IP ยืนยันการแก้ไขโดยการอ่าน $remote_addr ใน access log ของคุณและตรวจสอบว่าเป็นที่อยู่ของผู้เยี่ยมชม ไม่ใช่ที่อยู่ของ CDN
IPv6 ทำให้การจำกัดต่อที่อยู่ทำได้ยาก $binary_remote_addr เก็บค่า /128 เต็มรูปแบบ และการจัดสรร IPv6 สำหรับที่อยู่อาศัยมักจะเป็น /64 หรือใหญ่กว่า ซึ่งเป็นจำนวนที่อยู่มากกว่าที่ผู้โจมตีจะใช้งานได้หมด โดยแต่ละที่อยู่จะมีโควตาของตัวเอง ให้เพิ่ม zone ที่สองเพื่อเป็นเพดานจำกัดที่ตัว endpoint เอง โดยใช้ค่าคงที่เป็น key เพื่อให้ฟอร์มมีอัตราการเข้าถึงรวมไม่ว่าจะมีที่อยู่ต้นทางกี่แห่งก็ตาม:
map $request_method $signup_total_key {
POST "signup";
default "";
}
limit_req_zone $signup_total_key zone=signup_total:1m rate=30r/m;เพิ่ม limit_req zone=signup_total burst=10 nodelay; ลงใน location เดียวกัน ตั้งค่าอัตราให้สูงกว่าชั่วโมงที่มีการใช้งานจริงสูงสุดของคุณโดยเผื่อไว้เล็กน้อย นี่เป็นการควบคุมแบบหยาบ: ในระหว่างการโจมตี มันจะปฏิเสธการสมัครสมาชิกจริงด้วยเช่นกัน ซึ่งเป็นการแลกเปลี่ยนที่ถูกต้อง เพราะทางเลือกอื่นคือการที่เซิร์ฟเวอร์ของคุณส่งอีเมลออกไปเอง
เหตุผลที่ขีดจำกัดต่อที่อยู่ไม่สามารถตั้งค่าที่พร็อกซีได้
ที่อยู่อีเมลจะถูกส่งมาภายในเนื้อหาของ POST และ Nginx ไม่ได้ทำหน้าที่แยกวิเคราะห์ (parse) เนื้อหาของคำขอ ตัวแปรทุกตัวที่ limit_req_zone สามารถใช้เป็นคีย์ได้นั้นมาจากบรรทัดคำขอ (request line), ส่วนหัว (headers) หรือการเชื่อมต่อ ดังนั้นกฎอย่างเช่น "ที่อยู่นี้สามารถรับการยืนยันได้สูงสุดหนึ่งครั้งต่อวัน" จึงต้องอยู่ในส่วนประกอบแรกที่อ่านเนื้อหาของคำขอ ซึ่งก็คือแอปพลิเคชันของคุณ
อย่าเลี่ยงปัญหานี้ด้วยการย้ายที่อยู่ไปไว้ใน query string เพื่อให้ $arg_email สามารถใช้งานได้ เพราะการทำเช่นนั้นจะทำให้ที่อยู่ของผู้สมัครทุกคนถูกบันทึกลงใน access log ของคุณในรูปแบบข้อความธรรมดา (cleartext) รวมถึงใน log shipper ใดๆ ที่อยู่ถัดไปจากนั้น ซึ่งเป็นการแลกเปลี่ยนการจำกัดอัตรา (rate limit) กับปัญหาด้านความเป็นส่วนตัว
มีข้อยกเว้นที่แท้จริงอยู่หนึ่งประการ คือโมดูล Nginx JavaScript หรือ njs ซึ่งสามารถอ่านเนื้อหาของคำขอและกำหนดค่าตัวแปรจากเนื้อหานั้นได้ ทำให้คุณสามารถสร้างคีย์ต่อที่อยู่ (per-address key) ที่ระดับพร็อกซีได้ นี่เป็นทางเลือกที่ใช้งานได้จริง แต่ก็เป็นการเพิ่มโค้ดใหม่เข้าไปในเส้นทางของคำขอของคุณ สำหรับเว็บไซต์ส่วนใหญ่ การจำกัดจำนวนต่อที่อยู่ควรทำที่ระดับใกล้กับฐานข้อมูลซึ่งทราบอยู่แล้วว่าที่อยู่นี้มีการยืนยันที่ค้างอยู่หรือไม่ ในขณะที่พร็อกซีควรทำหน้าที่จัดการขีดจำกัดต่อ IP และต่อ endpoint ซึ่งเป็นสิ่งที่พร็อกซีทำได้ดีอยู่แล้ว
ห้ามนำข้อความที่ได้รับจากผู้ใช้มาแสดงซ้ำในข้อความตอบกลับ
คุณต้องป้องกันไม่ให้สตริงที่ผู้ใช้กรอกเข้ามาปรากฏในข้อความที่คุณส่งออกไป มีเหตุผลสำคัญ 2 ประการที่ทำให้เรื่องนี้เป็นอันตรายและมีการนำไปใช้โจมตีจริงแล้ว
หากอีเมลยืนยันของคุณทักทายผู้อ่านด้วยชื่อที่ดึงมาจากแบบฟอร์ม ผู้โจมตีอาจใส่ข้อความที่เป็นอันตรายลงในช่องชื่อ จากนั้นเซิร์ฟเวอร์ของคุณจะส่งข้อความนั้นไปยังเหยื่อโดยใช้โดเมนของคุณและลงนามด้วยคีย์ DKIM (DomainKeys Identified Mail) ของคุณ ส่งผลให้เว็บไซต์ของคุณกลายเป็นเครื่องมือส่งข้อความละเมิดกฎของผู้ไม่หวังดี และผู้ให้บริการอีเมลปลายทางจะมองว่าโดเมนของคุณเป็นแหล่งที่มาของการส่งสแปม
เหตุผลประการที่สองนั้นร้ายแรงกว่า หากฟิลด์ใดๆ ที่ได้รับมาถูกนำไปต่อเข้ากับส่วนหัวของอีเมล (mail header) โดยตรง ตัวอักษรขึ้นบรรทัดใหม่ (newline) ในฟิลด์นั้นจะทำให้ผู้โจมตีสามารถแทรกส่วนหัวเพิ่มเติมได้ตามต้องการ รวมถึง Bcc ไลบรารีจัดการอีเมลสมัยใหม่จะปฏิเสธการมีอยู่ของตัวอักษรขึ้นบรรทัดใหม่ในค่าของส่วนหัว แต่โค้ดที่ส่งข้อความผ่านท่อ (pipe) ไปยัง sendmail จากเชลล์สคริปต์มักไม่ทำการตรวจสอบนี้
ข้อความยืนยันที่ปลอดภัยควรประกอบด้วยชื่อเว็บไซต์ของคุณ ลิงก์เพียงหนึ่งลิงก์ และประโยคอธิบายสั้นๆ หนึ่งประโยค ที่อยู่อีเมลควรปรากฏเฉพาะในส่วนที่ Mail Transfer Agent จำเป็นต้องใช้เท่านั้น คือในส่วนหัว To คุณควรทดสอบระบบโดยการส่งแบบฟอร์มที่ใส่ตัวอักษรขึ้นบรรทัดใหม่และลิงก์ที่ชัดเจนลงในช่องชื่อ จากนั้นให้ตรวจสอบข้อความอีเมลฉบับดิบด้วย less เพื่อยืนยันว่าไม่มีข้อมูลที่ผู้ใช้กรอกเข้ามาหลุดรอดไปได้
ในขณะเดียวกัน หน้าเว็บที่แสดงผลหลังการส่งข้อมูลสำเร็จควรแสดงข้อความเดียวกันสำหรับทุกที่อยู่อีเมล การแสดงผลที่แตกต่างกัน เช่น "คุณสมัครสมาชิกแล้ว" สำหรับอีเมลหนึ่ง และ "โปรดตรวจสอบกล่องจดหมายของคุณ" สำหรับอีกอีเมลหนึ่ง จะทำให้แบบฟอร์มของคุณกลายเป็นเครื่องมือตรวจสอบสถานะสมาชิกสำหรับผู้ที่ต้องการนำรายชื่ออีเมลมาทดสอบหาข้อมูลในระบบของคุณ
คุณควรเลือกใช้การตรวจสอบบอทแบบใด?
จงเลือกโดยคำนึงถึงการเข้าถึงได้ (accessibility) ให้เท่ากับความมีประสิทธิภาพ การตรวจสอบแบบเลือกรูปภาพไม่สามารถแก้ได้โดยผู้ใช้ที่ตาบอด และตัวเลือกสำรองแบบเสียงก็ยากเกินไปสำหรับคนที่มีการได้ยินปกติ การตรวจสอบที่ทำให้ผู้ใช้จริงสมัครสมาชิกไม่ได้ถือเป็นทั้งการป้องกันและต้นทุนที่ต้องจ่าย ต่อไปนี้คือ 4 ตัวเลือกตามลำดับที่ควรพิจารณา
Proof of work ในเบราว์เซอร์ เบราว์เซอร์จะคำนวณค่าแฮชที่เซิร์ฟเวอร์ตรวจสอบได้ง่ายโดยที่ผู้ใช้ไม่ต้องทำอะไรเลย listmonk มีฟีเจอร์นี้ใน Settings แล้วไปที่ Security โดยใช้ ALTCHA ซึ่งไม่จำเป็นต้องใช้บริการจากบุคคลที่สาม ณ เดือนสิงหาคม 2026 นี่คือคำแนะนำของ listmonk แทนตัวเลือก hCaptcha ที่เลิกใช้งานไปแล้ว ต้นทุนการประมวลผลจะตกอยู่กับผู้ที่ส่งคำขอเข้ามามากที่สุด ซึ่งก็คือผู้โจมตี
การตรวจสอบแบบจัดการโดยบุคคลที่สามที่ไม่ต้องโต้ตอบ Cloudflare Turnstile จะไม่แสดงอะไรให้ผู้เข้าชมส่วนใหญ่เห็น และจะท้าทายเฉพาะเมื่อสัญญาณที่ได้รับดูไม่น่าไว้วางใจเท่านั้น วิธีนี้มีประสิทธิภาพ แต่จะเพิ่มบุคคลที่สามเข้ามาในขั้นตอนการสมัครสมาชิกของคุณ
ฟิลด์ล่อ (Honeypot field) คือช่องกรอกข้อความที่คนทั่วไปมองไม่เห็น แต่บอทที่เขียนมาไม่ดีจะกรอกข้อมูลลงไป ให้ตั้งชื่อฟิลด์ที่ไม่ซ้ำกับฟิลด์อื่นในฟอร์ม และตั้งค่า autocomplete="off", tabindex="-1" และ aria-hidden="true" เพื่อไม่ให้โปรแกรมจัดการรหัสผ่านกรอกข้อมูลลงไปและไม่ให้โปรแกรมอ่านหน้าจอประกาศชื่อฟิลด์นั้น หากตั้งชื่อฟิลด์ว่า email2 หรือ address เบราว์เซอร์จะกรอกข้อมูลให้อัตโนมัติและทำให้คุณปฏิเสธผู้ใช้จริง
<div style="position:absolute; left:-9999px;" aria-hidden="true">
<label for="hp_ref">Leave this field empty</label>
<input type="text" id="hp_ref" name="hp_ref" autocomplete="off" tabindex="-1">
</div>การตรวจสอบเวลาในการส่งฟอร์ม (Time-to-submit check) ให้ใส่ timestamp ที่มีการลงลายเซ็นดิจิทัลไว้ใน hidden field ตอนที่หน้าเว็บแสดงผล และปฏิเสธการส่งข้อมูลที่เข้ามาภายในเวลาไม่ถึง 2 วินาที มนุษย์ไม่สามารถอ่านฟอร์มและพิมพ์ที่อยู่อีเมลได้เร็วขนาดนั้น ต้องทำการลงลายเซ็นที่ timestamp ด้วย มิฉะนั้นบอทจะส่งค่า timestamp เก่ามาแทน
สิ่งหนึ่งที่ต้องตรวจสอบไม่ว่าคุณจะเลือกวิธีใดคือ โทเค็นต้องถูกใช้งานได้เพียงครั้งเดียว หากสคริปต์สามารถแก้การตรวจสอบได้หนึ่งครั้งแล้วนำโทเค็นนั้นไปใช้ซ้ำกับที่อยู่อีเมลนับพันรายการ การตรวจสอบนั้นก็พิสูจน์ได้เพียงว่ามีการรันเบราว์เซอร์หนึ่งครั้งเท่านั้น ไม่ได้ช่วยอะไรมากกว่านั้น
คุณจะทราบได้อย่างไรก่อนที่รายงานการละเมิดจะมาถึง?
คุณควรให้กราฟของคุณเป็นผู้บอก ไม่ใช่แผนกรับเรื่องร้องเรียนของผู้ให้บริการโฮสติ้ง คุณควรเฝ้าระวังสองสิ่งนี้
นับจำนวนการส่งข้อมูลต่อหนึ่งที่อยู่ต้นทางจาก log:
sudo awk '/POST \/subscription\/form/ {print $1}' /var/log/nginx/access.log \
| sort | uniq -c | sort -rn | head -20จากนั้นให้ fail2ban อ่านบรรทัด limiting requests เดียวกับที่ nginx เขียนลงไป และทำการแบนผู้ที่กระทำผิดซ้ำ fail2ban มีตัวกรอง (filter) สำหรับเรื่องนี้โดยเฉพาะ ให้สร้าง /etc/fail2ban/jail.d/nginx-limit-req.local:
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
port = http,https
logpath = /var/log/nginx/error.log
findtime = 600
maxretry = 10
bantime = 3600sudo systemctl reload fail2ban
sudo fail2ban-client status nginx-limit-reqผลลัพธ์สถานะจะแสดงตัวกรองของ jail และจำนวนความล้มเหลวรวมถึงจำนวนที่ถูกแบนในปัจจุบัน ค่า Currently banned: 0 ในวันที่ไม่มีกิจกรรมถือเป็นเรื่องปกติ หาก jail ไม่ปรากฏขึ้นเลย แสดงว่า fail2ban ไม่ได้โหลดไฟล์ดังกล่าว และ sudo fail2ban-client -d | grep nginx-limit-req จะแสดงการตั้งค่าที่ระบบได้อ่านจริง ตัวกรองที่มาพร้อมกับโปรแกรมจะจับคู่กับทุกโซน limit_req คุณควรจำกัดให้เหลือเพียงโซนการสมัครสมาชิกของคุณโดยการตั้งค่า ngx_limit_req_zones = signup ในส่วน [Definition] ของ /etc/fail2ban/filter.d/nginx-limit-req.local โครงสร้างไฟล์ jail และคำสั่งแบนได้รับการอธิบายไว้อย่างละเอียดใน คู่มือ fail2ban สำหรับ Ubuntu 24.04
สัญญาณที่สองคืออัตราส่วนซึ่งไม่จำเป็นต้องใช้ซอฟต์แวร์ใหม่ นั่นคือจำนวนการส่งข้อมูลหารด้วยจำนวนการยืนยัน ในรายการที่ปกติ คนส่วนใหญ่ที่ส่งที่อยู่จะคลิกที่ลิงก์ ซึ่งมักจะเกินครึ่งหนึ่ง เมื่ออัตราส่วนนี้ลดลงในขณะที่จำนวนการส่งข้อมูลเพิ่มขึ้น แสดงว่าคุณกำลังถูกใช้เป็นเครื่องมือ ให้เปรียบเทียบจำนวนสมาชิก unconfirmed ที่สร้างขึ้นในชั่วโมงที่ผ่านมากับจำนวนสมาชิก confirmed ตามตารางเวลาที่คุณใช้ทำรายงานอยู่แล้ว
ต้นทุนที่คุณต้องจ่าย: ชื่อเสียงของผู้ส่งและบัญชีรายชื่อบล็อก (Blocklists)
นี่คือส่วนที่เปลี่ยนความรำคาญให้กลายเป็นภาระค่าใช้จ่าย
รายชื่อที่อยู่สำหรับอีเมลที่ถูกนำมาใช้ในการโจมตีแบบ Bombing นั้นมาจากการเก็บรวบรวมข้อมูล ซึ่งรายชื่อเหล่านี้มักมี spamtraps ปะปนอยู่ด้วย โดย spamtraps คือที่อยู่อีเมลที่ไม่เคยลงทะเบียนสมัครรับข้อมูลใดๆ จากที่ไหนเลย แต่ถูกเผยแพร่ไว้เพื่อดักจับผู้ส่งที่ส่งอีเมลโดยไม่ได้รับอนุญาต หากข้อความยืนยันของคุณส่งไปถึงที่อยู่เหล่านี้ ผู้ให้บริการบัญชีรายชื่อบล็อกบางรายอาจใช้เพียงเหตุผลนี้ในการขึ้นบัญชีดำคุณทันที
ผู้รับที่ไม่เคยร้องขอข้อความของคุณจะไม่กดปุ่มยกเลิกการสมัคร (unsubscribe) แต่พวกเขาจะกดปุ่ม "รายงานว่าเป็นสแปม" (report spam) กฎสำหรับผู้ส่งอีเมลจำนวนมากของ Google ซึ่งมีผลบังคับใช้ตั้งแต่เดือนกุมภาพันธ์ 2024 ระบุให้ผู้ส่งที่ส่งอีเมลถึง Gmail ตั้งแต่ 5,000 ฉบับต่อวันขึ้นไป ต้องรักษาอัตราการถูกรายงานว่าเป็นสแปมใน Postmaster Tools ให้ต่ำกว่า 0.3% แม้ผู้ส่งที่มีขนาดเล็กกว่าจะไม่ถูกวัดผลด้วยตัวเลขนี้ แต่สัญญาณการร้องเรียนในลักษณะเดียวกันก็จะถูกนำไปใช้ในการตัดสินใจของระบบกรองอีเมล ซึ่งจะส่งผลให้เมลของคุณถูกคัดแยกไปอยู่ในโฟลเดอร์สแปม นอกจากนี้ ที่อยู่อีเมลปลอมที่ถูกนำมาใช้ในการโจมตีจะส่งผลให้เกิด hard bounce และอัตรา hard bounce ที่เพิ่มสูงขึ้นถือเป็นสัญญาณบ่งชี้ถึงชื่อเสียงที่ไม่ดีในสายตาของผู้ให้บริการรายใหญ่ทุกแห่ง
หากคุณใช้งาน เซิร์ฟเวอร์อีเมลของคุณเองบน VPS ด้วย mailcow การถูกขึ้นบัญชีดำจะส่งผลโดยตรงต่อ IP address และโดเมนของคุณ การขอถอดชื่อออกจากบัญชีรายชื่อบล็อกกับผู้ให้บริการอย่าง Spamhaus หมายถึงการต้องกรอกแบบฟอร์มและรอคอย ซึ่งในระหว่างที่คุณรอ อีเมลแจ้งหนี้หรืออีเมลรีเซ็ตรหัสผ่านของคุณก็จะไม่ถูกส่งถึงผู้รับเช่นกัน หากคุณส่งอีเมลผ่านผู้ให้บริการแบบ shared provider ให้คาดการณ์ไว้เลยว่าพวกเขาจะระงับบัญชีของคุณก่อนแล้วค่อยรับฟังคำอธิบายของคุณในภายหลัง เนื่องจากทราฟฟิกของคุณถือเป็นความเสี่ยงต่อผู้ส่งรายอื่นทั้งหมดที่ใช้ IP address เดียวกัน
เมื่อเทียบกับผลกระทบที่เกิดขึ้น งานที่ต้องทำนั้นถือว่าน้อยมาก ให้เปิดใช้งาน confirmed opt-in วันนี้ เพราะเป็นการตั้งค่าเพียงจุดเดียวต่อหนึ่งรายชื่อ จากนั้นให้เพิ่ม rate limit ของ proxy เพราะเป็นการแก้ไขไฟล์เพียงไฟล์เดียวแล้วสั่ง reload ตามด้วยการติดตั้ง bot check และระบบแจ้งเตือนภายในสัปดาห์นี้
FAQ
การทำ double opt-in ช่วยป้องกันการโจมตีแบบ subscription bombing ได้หรือไม่
ช่วยป้องกันไม่ให้รายชื่อในระบบของคุณปนเปื้อนและจำกัดจำนวนข้อความที่ส่งออกไปได้เพียงหนึ่งฉบับต่อหนึ่งที่อยู่ที่ถูกส่งเข้ามา ซึ่งถือเป็นการปรับปรุงที่มีประสิทธิภาพสูงสุดที่คุณทำได้ อย่างไรก็ตาม วิธีนี้ไม่สามารถหยุดยั้งกล่องจดหมายของเหยื่อไม่ให้เต็มได้ เนื่องจากการโจมตีเกิดจากการรวมข้อความจากเว็บไซต์จำนวนนับพันแห่ง คุณควรใช้งานร่วมกับการจำกัดอัตรา (rate limit) ต่อ IP ที่ proxy ของคุณ และจำกัดจำนวนการส่งอีเมลยืนยันซ้ำ เพื่อไม่ให้ที่อยู่เดิมที่ถูกส่งเข้ามาสองครั้งสร้างข้อความฉบับที่สอง
ฉันจะแยกแยะการโจมตีแบบ bombing ออกจากการที่มีผู้สมัครใช้งานจริงจำนวนมากได้อย่างไร
ให้สังเกตสิ่งที่เกิดขึ้นหลังจากการส่งข้อมูล การสมัครใช้งานจริงจะมีการยืนยันและมักจะเกิดขึ้นภายในไม่กี่ชั่วโมง แต่การโจมตีแบบ bombing จะทิ้งรายชื่อที่ไม่มีการยืนยัน ไม่มีการเปิดอ่าน และไม่มีการคลิก นอกจากนี้ ข้อมูลที่ถูกส่งเข้ามามักมีความผิดปกติ เช่น มีที่อยู่ต้นทางที่คุณไม่เคยเห็นมาก่อน มีโดเมนของผู้รับที่คุณไม่เคยส่งไปถึง และช่วงเวลาที่ข้อมูลเข้ามาจะกระจายตัวสม่ำเสมอตลอดทั้งวัน แทนที่จะเป็นช่วงเวลาที่กลุ่มเป้าหมายของคุณตื่นนอน
ฉันควรลบที่อยู่ที่ถูกส่งเข้ามาเหล่านั้นหรือไม่
ควรลบ ทิ้งบันทึกที่ยังไม่ได้ยืนยันที่มีอายุเกินกว่า 30 วัน โดยให้ตั้งเวลาลบโดยอัตโนมัติแทนการทำด้วยตนเอง ห้ามส่งข้อความใดๆ ไปยังที่อยู่เหล่านั้นอีก รวมถึงข้อความขออภัยหรือข้อความถามว่า "นี่คือคุณใช่หรือไม่" เพราะนั่นถือเป็นการส่งข้อความที่ผู้รับไม่ได้ร้องขอเป็นฉบับที่สองไปยังผู้ที่ถูกโจมตีอยู่แล้ว หากที่อยู่เหล่านั้นเป็น spamtraps การส่งข้อความติดตามผลจะเป็นการยืนยันให้ผู้ดูแล blocklist ทราบว่าคุณยังคงส่งอีเมลไปยังที่อยู่ดังกล่าว
การจำกัดอัตรา (rate limiting) จะทำให้ผู้สมัครใช้งานจริงถูกปฏิเสธหรือไม่
การจำกัดอัตราที่ 1 ครั้งต่อ 30 วินาที โดยอนุญาตให้มี burst ได้ 3 ครั้ง เป็นค่าที่ผู้ใช้งานทั่วไปที่กรอกฟอร์มเพียงครั้งเดียวจะไม่รู้สึกถึงผลกระทบ แต่จะเริ่มส่งผลเมื่อมีผู้ใช้งานจริงจำนวนมากใช้ IP เดียวกัน เช่น สำนักงานที่อยู่หลัง NAT (network address translation) gateway เดียวกัน หรือเมื่อ proxy ของคุณมองเห็น IP ของ CDN แทนที่จะเป็น IP ของผู้เข้าชม ให้ตรวจสอบ $remote_addr ใน access log ของคุณก่อนที่จะปรับค่าใดๆ และควรรักษาเพดานการรับข้อมูลให้สูงกว่าชั่วโมงที่มีการใช้งานสูงสุดของคุณ
หาก IP ที่ใช้ส่งอีเมลของฉันติด blocklist หลังจากถูกโจมตี ฉันควรทำอย่างไรเป็นอันดับแรก
หยุดการส่งอีเมลจาก IP นั้นก่อนที่จะดำเนินการใดๆ ให้หยุดคิวการส่งแคมเปญ แก้ไขฟอร์ม และลบที่อยู่ที่ยังไม่ได้ยืนยันออก เพราะหากคุณขอถอดชื่อออกจาก blocklist แล้วยังคงส่ง traffic แบบเดิมเข้าไปอีก คุณจะถูกขึ้นบัญชีดำซ้ำได้เร็วกว่าเดิม จากนั้นให้ตรวจสอบว่าคุณติด blocklist ของที่ใด เนื่องจากผู้ให้บริการส่วนใหญ่จะมีหน้าตรวจสอบที่ใช้ IP ของคุณเป็นตัวค้นหา และให้ปฏิบัติตามขั้นตอนการถอดชื่อของพวกเขา โดยปกติอาจต้องรอเป็นเวลาหลายวัน ให้ใช้เวลานี้ตรวจสอบว่า record ของ SPF (sender policy framework) และการเซ็นชื่อด้วย DKIM ของคุณยังคงผ่านการตรวจสอบอยู่