โดยปกติแล้วธรรมชาติของ PHP เวลามีการใช้งาน == เพื่อทำการเปรียบเทียบค่าของ 2 parameter ตัว PHP จะมีการพยายามแปลงสภาพของ parameter ให้เป็นลักษณะของข้อมูลเดียวกัน เช่น
การเปรียบเทียบปกติ
1 2 3 4 5 6 7 8 9 10 11 |
<?php $a = 1; $b = 1; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same\n"; } else { print "a and b are NOT the same\n"; } ?> |
ตัวอย่างต่อมาถ้าเป็น int แต่ค่าไม่เท่ากัน เราจะเขียนเป็น
1 2 3 4 5 6 7 8 9 10 11 |
<?php $a = 1; $b = 0; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same\n"; } else { print "a and b are NOT the same\n"; } ?> |
ผลลัพธ์คือไม่เท่ากัน เพราะค่า a และ b ไม่เท่ากัน
ตัวอย่างต่อมาถ้าเป็น string เราจะเขียนเป็น
1 2 3 4 5 6 7 8 9 10 11 |
<?php $a = "these are the same"; $b = "these are the same"; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same\n"; } else { print "a and b are NOT the same\n"; } ?> |
ผลลัพธ์คือเท่ากัน
ตัวอย่างต่อมาถ้าเป็นเปรียบเทียบ string ที่ไม่เหมือนกัน เราจะเขียนเป็น
1 2 3 4 5 6 7 8 9 10 11 |
<?php $a = "these are NOT the same"; $b = "these are the same"; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same\n"; } else { print "a and b are NOT the same\n"; } ?> |
ผลลัพธ์คือไม่เท่ากัน เป็นปกติ
การเปรียบเทียบแบบไม่ใช่ชนิดข้อมูลเหมือนกัน
ตัวอย่างนี้จะเป็นการเปรียบเทียบตัวแปรที่ไม่ใช่ชนิดข้อมูลเดียวกัน นั่นคือ int และ string
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php $a = "1"; $b = 1; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same\n"; } else { print "a and b are NOT the same\n"; } ?> |
แต่อย่างที่เห็น ทาง PHP เห็นว่า string ‘1’ นั้นมีข้อมูลที่น่าจะเป็นชนิด int ก็เลยแปลงให้ string เป็น int แล้วทำการเปรียบเทียบกับ int ก็เลยได้ค่าที่เท่ากัน
แต่ทีนี้มาลองค่าข้อมูลที่เป็น “0e111111111111111111111111111111” และ “0e222222222222222222222222222222” กันบ้าง
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php $a = "0e111111111111111111111111111111"; $b = "0e222222222222222222222222222222"; var_dump($a); var_dump($b); if ($a == $b) { print "a and b are the same\n"; } else { print "a and b are NOT the same\n"; } ?> |
ผลลัพธ์ที่ได้ออกมาคือ
ทำไมเป็นแบบนั้น? เพราะ PHP มองว่าทั้ง 2 ตัวแปรนั้นน่าจะเป็นตัวเลข ทีนี้ถ้าค่านั้นมันใหญ่มากกว่าค่า int ก็จะแปลงให้อยู่ในลักษณะค่า float ดังนั้นมันจึงทำการแปลงค่าทั้งหมดให้กลายเป็น float จากนั้นจึงเปรียบเทียบทั้งหมดในลักษณะ float แทนที่จะเป็น string อย่างที่เราเห็นๆกัน ซึ่งเราลองเอาค่าที่ขึ้นต้นด้วย 0e ไปแปลงเป็น float ดูจะพบว่าจะมีค่าเท่ากับ 0 ทั้งหมด ดังนั้นผลลัพธ์ที่ออกมาจึงถูกแสดงออกมาว่า “a และ b มีค่าเท่ากัน” เพราะมันคือการเปรียบเทียบ “0==0” นั่นเอง
วิธีการแก้ไขง่ายๆก็คือให้ทำการเปลี่ยนไปใช้ “===” ซึ่งนอกจากจะเป็นการเช็คค่าตัวแปรแล้ว ยังทำการเช็ค type ของตัวแปรอีกด้วย
Source::
- https://github.com/nicwl/php-ctf-thinkr/blob/master/WRITEUP.md
- https://0x90r00t.com/2016/02/23/internetwache-ctf-2016-web-50-mess-of-hash-write-up/
- https://foxglovesecurity.com/2017/02/07/type-juggling-and-php-object-injection-and-sqli-oh-my/