ป้องกัน XSSI ด้วย Prefix JSON

เคยดู API ของ Facebook หรือของ Google แล้วสงสัยไหม ทำไมจะต้องมี

for(;;);

หรือ

)]}'

ขึ้นต้นด้วย… มีไว้ทำไม ?


XSSI หรือชื่อเต็ม Cross-Site Script Inclusion คือการที่เว็บอื่นต้องการดึง API หรือ JS ของเรา โดยที่เราไม่อนุญาตให้เว็บนั้นดึงข้อมูลได้

เวลาที่ยิง fetch มาจะ error เพราะติด CORS ทำให้ไม่สามารถดึงผ่าน fetch ได้

แต่สามารถข้าม CORS ได้โดยใช้วิธี

<script src="https://api.example.com/get"></script>

เพื่อดึง JSON ออกมาได้ ส่วนใหญ่ JSON ที่จะสามารถดึงได้ จะต้องอยู่ในรูปที่สามารถ parse เป็น JavaScript ได้ เช่น

{"name":"acoshift"}

ไม่สามารถ parse เป็น JavaScript ได้ เพราะ
{ => เปิด code block
"name" => string
: => คืออะไรไม่รู้

แต่

[{"name":"acoshift"}]

สามารถ parse เป็น JavaScript ได้

โดยที่ ถ้า JSON ของเราเป็น Array เราอาจจะแอบไป override constructor ของ Array ไว้ก่อน แล้วค่อยเรียก api เช่น

Array = function () {
console.log(this)
}

หรือถ้าเป็น Object ก็อาจจะใช้ defineSetter หรือ defineProperty เพื่อดึงข้อมูลตอนที่ object ถูก set ค่าได้

วิธีป้องกันคือทำให้ API เรา return ค่าออกมาให้เป็น invalid JSON โดยอาจจะใส่

for(;;); เพื่อให้เว็บที่ดึง api (ผ่าน <script>) มาติด loop แล้วค้างไปเลย หรือจะใส่ )]}' เพื่อทำให้ JSON มัน invalid แล้ว parse ไม่ได้

เวลาที่เรา fetch data มาจาก api เราก็แค่ ลบ prefix นั้นออกก่อน แล้วค่อยไปใส่ใน JSON.parse ก็จะได้เป็น JSON แบบปกติ


ข่าวดีคือ ปัจจุบันเราไม่ต้องทำแบบนี้แล้ว เย่! เพราะ browser ใหม่ ๆ เวลาสร้าง Array หรือ Object จะใช้ function built-in ที่มากับ browser เลย ทำให้ไม่สามารถใช้วิธีนี้ได้แล้ว

แต่ถ้าใครต้องทำ api เพื่อ support browser เก่า ๆ ก็อาจจะต้องลองศึกษาเรื่อง XSSI เพิ่มเติม หรือดูเรื่อง JSON Hijacking เพิ่ม


ก่อนจบแนะนำว่า ถ้าใครจะทำ API อย่าใช้ JSONP เพราะเราไม่มีทางป้องกัน XSSI ได้ 😛

Like what you read? Give acoshift a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.