เขียนโปรแกรมป้องกัน Spam-Comment บนเว็บไซท์

Spam ถือเป็นปัญหาน่ารำคาญ และกำจัดได้ยากบนโลกอินเทอร์เน็ตสมัยนี้ ดังที่คุณเชกูเวรา เคยเขียนรายละเอียดทั้งหมดให้เราอ่านกันมาแล้วครั้งหนึ่ง ใน
Spam, Spim, Spit and Spandy ภาค 1,ภาค 2 และ ภาค 3

BioLawCom.De เอง ก็ประสบปัญหาสแปมบ้างเป็นครั้งคราว แต่เจอแต่ละครั้ง หนักหนาสาหัสไม่น้อย ต้องคอยตามล้างตามเช็ด หาวิธีป้องกันใหม่ ๆ อยู่เรื่อย เหมือนกันครับ ผมคิดว่าในหน้าเว็บไซท์หลาย ๆ แห่งก็ประสบปัญหาเช่นเดียวกัน เลยอยากแลกเปลี่ยนความรู้ในเรื่องนี้กับเพื่อน ๆ ดูบ้าง

ก็อย่างที่คุณเช เคยเขียนไว้ในบล็อกไปแล้วครับว่า การป้องกัน สแปมมีหลายวิธี อาทิ …


                 Canceln, UDP ,NoCeM แล้วก็สติ๊กเกอร์ ซึ่งสามวิธีแรกใช้ได้เฉพาะกับ Newsgroup และต้องติดตั้งโปรแกรม เพิ่มเติม ส่วนการติดสติ๊กเกอร์นั้นได้ผลในระดับหนึ่งเท่านั้น กล่าวคือหาก หุ่นยนต์ (Robot) (โปรแกรมที่เหล่า Spammer ใช้ในการหาลิ้งค์ตามหน้าเวบไซท์ต่าง ๆ) สามารถเก็บลิ้งก์ของเราไปได้เรียบร้อยแล้ว การติดสติ๊กเกอร์ก็จะไม่สามารถทำอะไรกับเจ้าหุ่นยนต์นั้นได้

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

                 ส่วนวิธีการอื่น ๆ ในการป้องกัน Spam-Comment ที่นิยมทำกันได้แก่ regular expression โดยการหารูปแบบ ข้อมูลที่หุ่นยนต์ส่งเข้ามา เมื่อมีการส่งความคิดเห็นไปยังเวบเซิพเวอร์โปรแกรมต้องตรวจสอบทุกครั้ง ว่าความคิดเห็นที่ส่งมาเป็น Spam หรือไม่ โดยใช้ regular expression ผลที่ได้จากวิธีนี้ ไม่คุ้มกับแรงงาน ที่เสียไป เพราะการหารูปแบบข้อมูลที่หุ่นยนต์ส่งเข้ามานั้น ต้องใช้ตรรกะและระยะเวลาในการคิดมาก แต่ Spammer สามารถเปลี่ยนแปลงข้อมูลเพียงเล็กน้อยก็สามารถเล็ดรอดระบบเข้าไปได้

                 วิธีที่นิยมใช้และได้ผลมากคือการแยกหุ่นยนต์ออกจากคน โดยใช้ robot.txt แต่วิธีการนี้จะใช้ไม่ได้ผล กับหุ่นยนต์ที่ปลอมตัวเก่ง สามารถปลอมเป็นคนเข้ามาส่ง spam ได้ วิธีการใช้วิธีแยกหุ่นยนต์ออกจากคน

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

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

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

                 สุดท้ายไม่ว่าเจ้าหุ่นยนต์ส่งสแปมจะยอมแพ้หรือไม่ ประสิทธิภาพการทำงานของมันก็จะลดลงไปเรื่อย ๆ โดยที่ระบบของเราแทบไม่ได้รับผลกระทบใด ๆ เลย เพื่อให้ง่ายต่อการเขียน ผมขอเรียกวิธีนี้ว่า Poison URL แล้วกันครับ

วิธีการนี้ตรงข้ามกับระบบ Friendly URL ที่ใช้บน CMS (Content Management System) หลาย ๆ ตัว Friendly URL ถูกพัฒนาขึ้นเพื่อให้ Search Engine ต่าง ๆ เก็บข้อมูลที่มีอยู่ในเวบไซท์ของเราได้ง่ายขึ้น

                 คำถามที่เกิดขึ้น คือ หากใช้ Poison URL แล้วจะส่งผลกระทบต่อ rating ของเวบไวท์บน Search Engine หรือไม่ ผมตอบอย่างไม่ลังเลครับว่า เกิดขึ้นอย่างแน่นอน แต่ที่สำคัญคือค่อนข้างน้อย เนื่องจาก Search Engine ใช้ meta-data พวก title, keywords และ description ในการเก็บข้อมูลมากกว่าการใช้ URL ซึ่งเท่าที่ผมทดลองใช้ Poison URL มาเป็นระยะเวลาเกือบสามเดือน ก็ไม่เห็นผลกระทบที่ชัดเจนสำหรับ rating ของเวบไซท์บน Search Engine (Google และ Yahoo! ยังคงเก็บข้อมูลจากเวบไซท์ของเราอย่างต่อเนื่อง)

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

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

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

                 สิ่งที่เราต้องการในการผลิตและเปลี่ยนแปลงข้อมูลตรวจสอบคือ เครื่องมือสำหรับสุ่มข้อมูล (random) ในผลิตข้อมูลตรวจสอบที่มีประสิทธิภาพ และ เครื่องมือสำหรับตรวจสอบเวลา (cron) เป็นเหมือนนาฬิกาปลุก เพื่อให้ระบบเปลี่ยนแปลงข้อมูลตรวจสอบได้ในระยะเวลาที่เหมาะสม

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

 

function generate_key($length){
$extrakey="";
for($j=0;$j<$length;$j++){
while(true){
mt_srand((double)microtime()*1000000);
$zufall=mt_rand(48,122);
if(($zufall>=48 && $zufall<=57) ||
(
$zufall>=65 && $zufall<=90) ||
($zufall>=97 && $zufall<=122)){
$extrakey.=chr($zufall);
break;
}
}
}
return $extrakey;
}

                 พาราเมเตอร์ $length เป็นตัวกำหนดจำนวนตัวอักษรของข้อมูลตรวจสอบ และเพื่อไม่ให้เกิดปัญหาอื่น ๆ ตามมาภาพหลัง ฟังก์ชั่น generate_key จึงผลิตข้อมูลตรวจสอบเฉพาะ a-z, A-Z และ 0-9 เท่านั้น

                 ข้อมูลตรวจสอบที่ได้จะถูกเก็บไว้ในฐานข้อมูล เพื่อนำมาใช้ในภายหลัง ฐานข้อมูลนอกจากจะเป็นที่เก็บข้อมูล แล้ว ยังสามารถใช้เป็นนาฬิการปลุกในกำหนดเวลาเปลี่ยนข้อมูลตรวจสอบได้อีกด้วย

  1. function generate_daykey(){
  2. // create table daykey(
  3. // `id` int(10) unsigned NOT NULL auto_increment,
  4. // `key1` varchar(50),
  5. // `key2` varchar(50),
  6. // `key3` varchar(50),
  7. // `key4` varchar(50),
  8. // `key5` varchar(50),
  9. // `generate_on` datetime,
  10. // PRIMARY KEY (`id`)
  11. // ) TYPE=MyISAM;
  12. if(mysql_num_rows(three_query(
  13. "SELECT id FROM daykey WHERE generate_on>(NOW()-1000);"))
  14. == 0){
  15. $key1=generate_key(5);
  16. $key2=generate_key(5);
  17. $key3=generate_key(5);
  18. $key4=generate_key(5);
  19. $key5=generate_key(5);
  20. three_query("INSERT INTO daykey(key1,key2,key3,key4,key5,generate_on)
  21. VALUES('$key1','$key2','$key3','$key4','$key5',NOW());");
  22. three_query("DELETE FROM daykey WHERE generate_on<(NOW()-60000);");
  23. three_query("OPTIMIZE TABLE `daykey`;");
  24. }
  25. }

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

ฟังก์ชั่น generate_daykey() จะทำการตรวจสอบก่อนว่าถึงเวลาที่ต้องเปลี่ยนข้อมูล หรือไม่ โดยใช้คำสั่ง WHERE generate_on>(NOW()-1000) generate_on เป็นข้อมูลประเภท date_time บนฐานข้อมูล mysql สำหรับ NOW()-1000 คือเวลา 1000 วินาทีก่อนหน้านี้ ดังนั้นคำสั่ง WHERE generate_on>(NOW()-1000) จ ึงใช้ตรวจสอบว่ามีข้อมูลตรวจสอบที่มีอายุเกินกว่า 1000 วินาทีหรือไม่ หากมีให้ผลิตข้อมูลตรวจสอบใหม่ ลบข้อมูลตรวจสอบที่มีอายุเกิน 60000 วินาทีทิ้ง และ Optimize ตารางที่ใช้บันทึกข้มูลตรวจสอบ เนื่องจาก ตารางถูกเปลี่ยนแปลงข้อมูลบ่อย ดังนั้นการ Optimize จึงเป็นเรื่องที่จำเป็น

นั่นหมายความว่าข้อมูลตรวสอบจะถูกใช้งานภายในระยะเวลา 1000 พันวินาที และมีอายุการใช้งาน 60000 วินาที เราจึงมีข้อมูลตรวจสอบอยู่ในตารางประมาณ 60 ชุด โดยแต่ละชุดมีรูปแบบให้เลือกใช้งานแตกต่างกัน 5 รูปแบบ (key1 – key5)

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

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

เมื่อเราสามารถผลิตข้อมูลตรวจสอบได้แล้ว ขั้นต่อมาคือการนำมาใช้งาน การใช้งานข้อมูลตรวจสอบเราต้องการอีกสองฟังก์ชั่นคือ

  1. function get_daykey($index){
  2. return mysql_result(three_query(
  3. "SELECT key$index FROM daykey ORDER BY id DESC LIMIT 1"),0,0);
  4. }

สำหรับเรียกข้อมูลตรวจสอบเพื่อนำมาใช้งาน และ

  1. function exist_daykey($index,$key){
  2. $day_key=three_query(
  3. "SELECT id FROM daykey WHERE key$index='$key';");
  4. (mysql_num_rows($day_key) !=0)?$exist=true:$exist=false;
  5. return $exist;
  6. }

สำหรับตรวจสอบว่าข้อมูลตรวจสอบถูกต้องหรือไม่

                 การส่งข้อมูลตรวจสอบเพื่อป้องกันสแปมนั้นมีสองขั้นตอนดังที่ได้กล่าวไปแล ้ว คือ ในขั้นตอนของการตรวจสอบ ผ่านลิ้งก์ และขั้นตอนการตรวจสอบผ่าน Form ดังนั้นระบบ Poison URL จึงต้องไฟล์ php ทั้งหมดสามไฟล์

โดยไฟล์แรก เป็นไฟล์ที่แสดงลิ้งก์ไปยัง Form ที่ใช้กรอกข้อมูลสำหรับแสดงความคิดเห็น ไฟล์ที่สอง ใช้ตรวจสอบ URL และส่งข้อมูลตรวจสอบต่อไปยังไฟล์ที่สาม ส่วนไฟล์ที่สามมีหน้าที่ตรวจสอบข้อมูล และบันทึกความคิดเห็น

หน้าตาโค้ดของไฟล์แรกจะมีหน้าตาประมาณนี้

  1. <?php
  2. generate_daykey();
  3. echo("<a href='guestbook.php?daykey= ".get_daykey(1)."'>guestbook</a>");
  4. ?>

ไฟล์แรกไม่มีหน้าที่อื่นใดนอกจากผลิตข้อมูลตรวจสอบด้วย generate_daykey() และรับข้อมูลจากฟังก์ชั่น get_daykey() ส่งไปให้ไฟล์ที่สอง คือ guestbook.php ทำการจรวสอบตัวแปร $GET[daykey] ดังนี้

  1. <?php
  2. if(exist_daykey(1,$_GET[daykey])){
  3. echo("<form method='post' action='insert_guestbook.php'>
  4. <input name='daykey' value='".get_daykey(2)."' type='hidden'>
  5. <textarea name='guestbook'></textarea>
  6. <input type='submit' value='เขียนสมุดเยี่ยม'>
  7. </form>");
  8. }
  9. else{
  10. echo("<a href='guestbook.php?daykey= ".get_daykey(1)."'>guestbook</a>");
  11. }
  12. ?>

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

เมื่อผู้มาเยี่ยมชมเวบไซท์เขียนความคิดเห็นเรียบร้อยแล้ว กดปุ่ม submit เพื่อส่งข้อมูลไปยังไฟล์ที่ 3 ซึ่งมีโค้ดดังนี้

  1. <?php
  2. if(exist_daykey(2,$_POST[daykey])){
  3. insert_guestbook();
  4. }
  5. ?>

หากข้อมูลถูกต้องจึงเพิ่มความคิดเห็นลงในฐานข้อมูลผ่านฟังก์ชั่น insert_guestbook()

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

บทความที่เกี่ยวข้อง:

มาใช้ Ubuntu กันเถอะ.!
ได้ปรินเตอร์ใหม่มาลอง..!
ประวัติของถ่ายไฟฉาย แบ็ตเตอรี่ (History of Dry Cell and Battery)
เมื่อจับ Printer มาวิ่งแข่งกัน กับงาน Samsung Printer Run

Comments

comments

%d bloggers like this: