เราป้องกัน Cross-site scripting (XSS) อย่างไรครับ

เริ่มโดย Boya, 31 มีนาคม 2011, 13:18:11

หัวข้อก่อนหน้า - หัวข้อถัดไป

0 สมาชิก และ 1 ผู้มาเยือน กำลังดูหัวข้อนี้

Boya

เว็บผมเขียนด้วย php + mssql

หน้า index.php มันจะส่งข้อมูลไปให้ save.php

แต่เจอปัญหาว่ามีช่องโหว่ Cross-site scripting (XSS) พอจะมีใครแนะนำการแก้ปัญหา นี้บ้างครับ ป้องกันอย่างไร

Boya

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

kennn

#2
หลักๆคือ ทำการ
1. ValidateRequest หรือตรวจสอบข้อมูล Character ตอน input ที่รับมาจาก Form ทั้งในรูปแบบ POST หรือ GET ก่อนเก็บหรือเซฟข้อมูล ข้อมูลไหนเป็นตัวเลขเราก็ทำการตรวจสอบด้วยฟังค์ชั่นตรวจสอบตัวเลขเป็นหลัก บังคับกี่หลัก ข้อความไหนเป็นสตริงเราก็ตรวจสอบโดยใช้ฟังค์ชั่นที่เกี่ยวกับสตริง การ ValidateRequest ควรตรวจสอบโดยใช้ทั้ง Javascript และโดยภาษาที่ใช้เขียนเอง เนื่องจากถ้าเราใช้ Javascript ในการตรวจสอบเท่านั้น อาจจะไม่ปลอดภัยซะทีเดียว เพราะ Javascript สามารถ disable โดยฝั่งผู้ใช้ได้

2. กำหนดรูปแบบการเก็บข้อมูลในฟิวด์ต่างๆใน Database ตัวไหนควรใช้ VARCHAR, TEXT, NUMBER ความยาวข้อมูล

3. Encode character ของข้อมูลทั้ง Input และ Output คือ การเปลี่ยนรูปแบบตัวอักษรพวก Comma, Single Qoute ให้อยู่ในรูปแบบ HTML โดยใช้ฟังค์ชั่น htmlentities() ตรงนี้ไม่จำเป็นต้องทำพร่ำเพื่อครับ เราจะใช้ก็ต่อเมื่อข้อมูลนั้นเซฟมาจากผู้ใช้งานอื่น หรือเป็นข้อมูลที่มีความเป็นไปได้ว่าจะไม่ปลอดภัย


- Character ก็เช่น เครื่องหมาย Comma, Single Qoute
- หัวใจหลักสำคัญในการป้องกันก็คงจะเป็นข้อ 1. ที่กล่าวไปครับ ถ้าผู้ไม่ประสงค์ดีคิดจะทำ XSS แต่ใส่ Parameter ในการทำ XSS ไม่ได้ก็น่าจะป้องกันได้แล้วนะครับ

ข้อมูลเพิ่มเติมครับ
hxxp://en.wikipedia.org/wiki/SQL_injection
ถ้าลองทำดู จะพบว่าการทำ XSS สามารถดึงข้อมูลออกมาจาก Database ได้ หากเว็บไหนเก็บข้อมูลบัตรเครดิตลูกค้าไว้ก็จะมีผลเสียเยอะแน่นอนครับ

สำหรับผู้ที่ผ่านไปมา แล้วยังสงสัยว่า XSS คืออะไร ลองอ่านได้จากที่นี่ครับ
hxxp://www.acisonline.net/article_prinya_web_security3.htm


ความรู้ งูๆปลาๆ ผิดถูกอย่างไรขออภัยไว้ล่วงหน้าครับ
[direct=https://www.strikeprofx.com/th/]StrikeProFx โบรกเกอร์ Forex ฝาก-ถอนรวดเร็ว[/direct]
[direct=https://www.strikeprofx.com/]StrikeProFx Best Forex Broker Fast Deposit&Withdraw[/direct]

bonshington

ทำ capcha หรือไม่ก็ทำ anti forgery token เช่น

<form>
  name:<input type="text" name="name" />
  <input type="submitt" />
  <input type="hidden" name="anti-forgery-token" value="abc-def-123-456"/>
</form>

ทีนี้ เราก็ตั้งไปว่า token เราจะให้เป็นอะไร เช่นว่า md5(salt + username + dd/mm/yyyy) หรือถ้าจะให้ดี ก็เปลี่ยน key ทุก ชม เช่น md5(salt + username + dd/mm/yy + hh) แล้วเวลาเช็ค ก็เอา hh ไป +1 -1 เผื่อพลาด

ตย สมมุติว่า ตอนนี้เวลาประมาณ 17.00 น.
md5("any_secret_string" + "admin" + "31/03/2011" + 16)
md5("any_secret_string" + "admin" + "31/03/2011" + 17)
md5("any_secret_string" + "admin" + "31/03/2011" + 18)

เราก็จะได้ unique key มา 3 อัน ก้เอามาเทียบกับ forgery token ว่าตรงกันรึเปล่า ถ้าตรง ก็จบ หรืออาจจะฝังค่าบางอย่างลงใน cookie เพื่อช่วยในการเช็คก็ได้

vii


Boya

อ้างถึงจาก: bonshington ใน 31 มีนาคม 2011, 17:04:05
ทำ capcha หรือไม่ก็ทำ anti forgery token เช่น

<form>
  name:<input type="text" name="name" />
  <input type="submitt" />
  <input type="hidden" name="anti-forgery-token" value="abc-def-123-456"/>
</form>

ทีนี้ เราก็ตั้งไปว่า token เราจะให้เป็นอะไร เช่นว่า md5(salt + username + dd/mm/yyyy) หรือถ้าจะให้ดี ก็เปลี่ยน key ทุก ชม เช่น md5(salt + username + dd/mm/yy + hh) แล้วเวลาเช็ค ก็เอา hh ไป +1 -1 เผื่อพลาด

ตย สมมุติว่า ตอนนี้เวลาประมาณ 17.00 น.
md5("any_secret_string" + "admin" + "31/03/2011" + 16)
md5("any_secret_string" + "admin" + "31/03/2011" + 17)
md5("any_secret_string" + "admin" + "31/03/2011" + 18)

เราก็จะได้ unique key มา 3 อัน ก้เอามาเทียบกับ forgery token ว่าตรงกันรึเปล่า ถ้าตรง ก็จบ หรืออาจจะฝังค่าบางอย่างลงใน cookie เพื่อช่วยในการเช็คก็ได้

งงสุดๆ มึนตรึบเลยครับท่าน

vii

อ้างถึงจาก: Boya ใน 31 มีนาคม 2011, 22:45:34
อ้างถึงจาก: bonshington ใน 31 มีนาคม 2011, 17:04:05
ทำ capcha หรือไม่ก็ทำ anti forgery token เช่น

<form>
  name:<input type="text" name="name" />
  <input type="submitt" />
  <input type="hidden" name="anti-forgery-token" value="abc-def-123-456"/>
</form>

ทีนี้ เราก็ตั้งไปว่า token เราจะให้เป็นอะไร เช่นว่า md5(salt + username + dd/mm/yyyy) หรือถ้าจะให้ดี ก็เปลี่ยน key ทุก ชม เช่น md5(salt + username + dd/mm/yy + hh) แล้วเวลาเช็ค ก็เอา hh ไป +1 -1 เผื่อพลาด

ตย สมมุติว่า ตอนนี้เวลาประมาณ 17.00 น.
md5("any_secret_string" + "admin" + "31/03/2011" + 16)
md5("any_secret_string" + "admin" + "31/03/2011" + 17)
md5("any_secret_string" + "admin" + "31/03/2011" + 18)

เราก็จะได้ unique key มา 3 อัน ก้เอามาเทียบกับ forgery token ว่าตรงกันรึเปล่า ถ้าตรง ก็จบ หรืออาจจะฝังค่าบางอย่างลงใน cookie เพื่อช่วยในการเช็คก็ได้

งงสุดๆ มึนตรึบเลยครับท่าน
อันข้างบนนั่นมันกัน csrf ครับ
กัน xss และ csrf ผมทำไว้ใน class ที่ผมแปะไว้ให้แล้ว ลองศึกษาวิธีใช้ ง่ายๆครับ

goto69

.

Boya

อ้างถึงจาก: vii ใน 01 เมษายน 2011, 04:40:05
อ้างถึงจาก: Boya ใน 31 มีนาคม 2011, 22:45:34
อ้างถึงจาก: bonshington ใน 31 มีนาคม 2011, 17:04:05
ทำ capcha หรือไม่ก็ทำ anti forgery token เช่น

<form>
  name:<input type="text" name="name" />
  <input type="submitt" />
  <input type="hidden" name="anti-forgery-token" value="abc-def-123-456"/>
</form>

ทีนี้ เราก็ตั้งไปว่า token เราจะให้เป็นอะไร เช่นว่า md5(salt + username + dd/mm/yyyy) หรือถ้าจะให้ดี ก็เปลี่ยน key ทุก ชม เช่น md5(salt + username + dd/mm/yy + hh) แล้วเวลาเช็ค ก็เอา hh ไป +1 -1 เผื่อพลาด

ตย สมมุติว่า ตอนนี้เวลาประมาณ 17.00 น.
md5("any_secret_string" + "admin" + "31/03/2011" + 16)
md5("any_secret_string" + "admin" + "31/03/2011" + 17)
md5("any_secret_string" + "admin" + "31/03/2011" + 18)

เราก็จะได้ unique key มา 3 อัน ก้เอามาเทียบกับ forgery token ว่าตรงกันรึเปล่า ถ้าตรง ก็จบ หรืออาจจะฝังค่าบางอย่างลงใน cookie เพื่อช่วยในการเช็คก็ได้

งงสุดๆ มึนตรึบเลยครับท่าน
อันข้างบนนั่นมันกัน csrf ครับ
กัน xss และ csrf ผมทำไว้ใน class ที่ผมแปะไว้ให้แล้ว ลองศึกษาวิธีใช้ ง่ายๆครับ

โทษครับ class ของท่าน มันกัน sql injection ด้วยเปล่าครับ ถ้าได้ก็แจ่มเลย

bonshington

อ้างถึงจาก: Boya ใน 01 เมษายน 2011, 11:20:43
อ้างถึงจาก: vii ใน 01 เมษายน 2011, 04:40:05
อ้างถึงจาก: Boya ใน 31 มีนาคม 2011, 22:45:34
อ้างถึงจาก: bonshington ใน 31 มีนาคม 2011, 17:04:05
ทำ capcha หรือไม่ก็ทำ anti forgery token เช่น

<form>
  name:<input type="text" name="name" />
  <input type="submitt" />
  <input type="hidden" name="anti-forgery-token" value="abc-def-123-456"/>
</form>

ทีนี้ เราก็ตั้งไปว่า token เราจะให้เป็นอะไร เช่นว่า md5(salt + username + dd/mm/yyyy) หรือถ้าจะให้ดี ก็เปลี่ยน key ทุก ชม เช่น md5(salt + username + dd/mm/yy + hh) แล้วเวลาเช็ค ก็เอา hh ไป +1 -1 เผื่อพลาด

ตย สมมุติว่า ตอนนี้เวลาประมาณ 17.00 น.
md5("any_secret_string" + "admin" + "31/03/2011" + 16)
md5("any_secret_string" + "admin" + "31/03/2011" + 17)
md5("any_secret_string" + "admin" + "31/03/2011" + 18)

เราก็จะได้ unique key มา 3 อัน ก้เอามาเทียบกับ forgery token ว่าตรงกันรึเปล่า ถ้าตรง ก็จบ หรืออาจจะฝังค่าบางอย่างลงใน cookie เพื่อช่วยในการเช็คก็ได้

งงสุดๆ มึนตรึบเลยครับท่าน
อันข้างบนนั่นมันกัน csrf ครับ
กัน xss และ csrf ผมทำไว้ใน class ที่ผมแปะไว้ให้แล้ว ลองศึกษาวิธีใช้ ง่ายๆครับ

โทษครับ class ของท่าน มันกัน sql injection ด้วยเปล่าครับ ถ้าได้ก็แจ่มเลย

มันไม่เกี่ยวกับ sql inject คับ หลักของการกัน cross site คือ เพิ่ม random key เฉพาะลงไปใน form ที่เราสามารถตรวจได้ หลักนี้ ผมเอามาจาก asp mvc อ่ะคับ

ต้นฉบับ
หน้า aspx ใส่ ข้างล่างลงไปใน form
<% var myform = Html.BeginForm("login"); %>
<%= Html.AntiForgeryToken() %>
<input name="username" type="text" />
<input type="submit" />
<% myform.EndForm(); %>

ใน controller
[AntiForgeryToken]
[HttpPost]
public ActionResult login(FormCollection form)
{
  ...
}

ผลการ render aspx จะได้
<form ...>
  <input name="__anti_forgery_token" type="hidden" value="abcd-efg-1234-5678" />
  <input type="text" name="username"/>
  <input type="submit"/>
</form>


แล้วตัว mvc มันจะ validate ให้อัตโนมัติ

แต่หลักนี้ มันใช้ได้กับเวปทั่วไป ดังนั้น ถ้าเป็น php เราก็ random key ขึ้นมาอ่ะ โดยมีหลักที่ว่า key ที่ random จะ random ตามเวลา

เช่น
md5(salt + username + date + hour)
salt = key ลับของเราเป็นอะไรก็ได้ ใส่ไว้เพื่อเพิ่ม security สมมุติว่า login ผมชื่อ admin ถ้าเราใช้ md5('admin') มันจะแกะง่ายมาก ดังนั้น เราก็ใส่ "เกลือ" เพิ่มเข้าไปให้มันแกะยาก เช่น md5('admin' + '_this is my secret salt') ทีนี้ เราจะได้ string ที่ยาวมากชุดนึง จะแกะได้ยากมาก

ในกรณีเวลาเหลื่อมล้ำ ตัว hour อาจจะไม่ตรงกัน ดังนั้น เราก็เพิ่ม +- เวลาเข้าไป เช่นว่า ตอนนี้ 10โมงเช้า ตัว hour เราก็อาจจะใส่ตั้งแต่ 7-8-9-10-11 ทีนี้ เราจะ random key มาได้ 5 ชุด ก็เอาไปเป็น forgery token ส่วนเรื่องการ random md5 ไม่ต้องห่วงเรื่อง performnace มันเร็วมากๆอยู่แล้ว

เวลาคนอื่นสร้าง form หลอกมาแปะที่เวปเรา เค้าจะไม่มีทางสร้าง forgery token ที่ถูกต้องได้เลย ทีนี้ เราก็สามารถตรวจสอบได้ว่า token ใน form นั้น มาจากเวปเราหรือไม่

vii

cross site scripting ลองอ่านดูครับ http://codesnippet.exteen.com/20081021/cross-site-scripting ภาษาไทย

อธิบายง่ายๆก็คือการแทรก script เข้ามาสู้หน้าเว็บเราในรูปแบบต่างๆกัน ดูจากเว็บเขาจะอธิบายตัวอย่างชัดเจนหมดเลย เข้าใจง่าย
การป้องกันก็คือกรองเอา script ในแบบต่างๆออก ไม่ว่าจะ <script> script: onmouseover="" on... ฯลฯ


cross site request forgery คือการสร้างฟอร์มปลอมๆเพื่อหลอกเหยื่อให้ดำเนินการผ่านฟอร์ม
เช่น
ในหน้า admin form method post และมีปุ่ม delete.
เหยื่อได้ทำการ log in admin แล้วลืม log out และเหยื่อหลงเข้ามาในหน้าหลอกเพื่อโจมตี แล้วดันไปกดลิ้งค์ในหน้าหลอกนั้น
สคริปต์ที่เขียนในหน้าหลอกนั้นจะทำหน้าที่เหมือนปุ่ม delete.
พอเหยื่อกลับไปดูหน้าเว็บตัวเอง อ้าว ข้อมูลหายวับไปแล้ว
เป็นต้น

การป้องกันก็อย่างที่คุณ bonshington  ว่าไว้


sql injection คือการโจมตีไปที่ sql
ให้ป้องกันโดยเช็คค่าของตัวเลขก่อนใส่คำสั่ง sql ที่รับค่าตัวเลขมา ในกรณีที่ sql field เป็น string (varchar, text) ให้ใช้ mysql_real_escape_string() ใส่
ตย.
$id = $_GET['id'];// ค่านี้ต้องเป็นตัวเลข
if ( ! is_numeric($id) ) {die("id ไม่เป็นตัวเลข");}
$sql = "select * from table where id = $id and name = 'my_real_escape_string($_POST['name'])'";

bonshington

อ้อ ผมอ่านแล้วเข้าใจผิด คิอว่า cross site scripting หมายถึง cross site forgery.

xss สำหรับผม ผมเรียก script injection อ่ะ ซึ่งจิงๆมันกันไม่ยาก แต่จุกจิกตรงที่ต้องมานั่ง encode ทุกจุด

ส่วน sql injection กันได้ไม่ยากนิ หลักที่ง่ายที่สุดคือ ไม่ใช้ insert update ตรงๆ เปลี่ยนไปใช้ store procedure แทน หรือว่า ถ้าเป็น pgsql ก็ใช้ $$ แทน ' string

ohmohm