จะป้องกัน XSS ด้วย HTML / PHP ได้อย่างไร?


256

ฉันจะป้องกัน XSS (การเขียนสคริปต์ข้ามไซต์) โดยใช้ HTML และ PHP ได้อย่างไร

ฉันเคยเห็นโพสต์อื่น ๆ อีกมากมายในหัวข้อนี้ แต่ฉันไม่พบบทความที่ชัดเจนและรัดกุมว่าจะป้องกัน XSS ได้อย่างไร


3
โปรดทราบว่านี่จะไม่ช่วยแก้ปัญหากรณีที่คุณอาจต้องการใช้การป้อนข้อมูลผู้ใช้เป็นแอตทริบิวต์ HTML ตัวอย่างเช่น URL ต้นทางของรูปภาพ ไม่ใช่กรณีทั่วไป แต่เป็นเรื่องง่ายที่จะลืม
Michael Mior

@MichaelMior ที่นี่เป็นวิธีการป้องกัน XSS ในhrefหรือsrcแอตทริบิวต์ HTML: stackoverflow.com/questions/19047119/…
baptx

มีบทความที่ดีเป็นที่นี่ที่อธิบาย XSS และวิธีการป้องกันไม่ให้มันในภาษาต่างๆ (รวม. PHP)
XCore

คำตอบ:


296

โดยทั่วไปคุณจำเป็นต้องใช้ฟังก์ชั่นhtmlspecialchars()เมื่อใดก็ตามที่คุณต้องการส่งออกบางสิ่งไปยังเบราว์เซอร์ที่มาจากการป้อนข้อมูลของผู้ใช้

วิธีที่ถูกต้องในการใช้งานฟังก์ชั่นนี้เป็นดังนี้:

echo htmlspecialchars($string, ENT_QUOTES, 'UTF-8');

Google Code University ยังมีวิดีโอเพื่อการศึกษาเกี่ยวกับความปลอดภัยบนเว็บเหล่านี้:


7
@TimTim: สำหรับกรณีส่วนใหญ่ใช่ อย่างไรก็ตามเมื่อคุณต้องการอนุญาตให้สิ่งที่ใส่ HTML ได้รับเล่ห์เหลี่ยม
Alix Axel

@Alix แอ็กเซิลจึงเป็นคำตอบของคุณเพื่อใช้ htmlspecialchars หรือใช้htmlpurifier.org ?
TimTim

3
หากคุณต้องการที่จะยอมรับการใช้ปัจจัย HTML HTML htmlspecialchars()เพียวริฟายเออถ้าไม่ใช้
Alix Axel

9
htmlspecialchars หรือ htmlentities ตรวจสอบที่นี่stackoverflow.com/questions/46483/…
kiranvj

4
เวลาส่วนใหญ่นั้นถูกต้อง แต่ไม่ง่ายอย่างนั้น คุณควรพิจารณาใส่สตริงที่ไม่น่าเชื่อถือลงใน HTML, Js, Css และพิจารณานำสตริงที่ไม่น่าเชื่อถือไปไว้ใน HTML ดูที่นี่: owasp.org/index.php/…
ชายบรอนซ์

41

หนึ่งในการอ้างอิงOWASPที่ฉันโปรดปรานคือคำอธิบายCross-Site Scriptingเพราะในขณะที่มีเวกเตอร์จู่โจม XSS จำนวนมากการปฏิบัติตามกฎสองสามข้อต่อไปนี้สามารถป้องกันส่วนใหญ่ได้อย่างมาก!

นี่คือชีตการรักษาความปลอดภัยของ PHP


7
ฉันด้วย .. นี่คือ XSS Filter Evasion Cheat Sheet owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet

1
ไม่ใช่ XSS ที่แน่นอน แต่ฉันคิดว่า XSS และ CSRF มักผสมกันและทั้งคู่ก็เป็นอันตรายจริงๆ: owasp.org/index.php/…
Simon

2
ไม่มีหน้านี้อีกแล้ว
Mazzy


15

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

รูปแบบที่การโจมตี XSS มักจะมีคือการแทรกลิงค์ไปยังจาวาสคริปต์นอกไซต์ที่มีเจตนาร้ายต่อผู้ใช้ อ่านข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ที่นี่

คุณจะต้องการทดสอบเว็บไซต์ของคุณ - ฉันสามารถแนะนำ Firefox add-on XSS Meได้


ฉันต้องทำอะไรเพื่อให้แน่ใจว่าฉันทำความสะอาดอินพุตให้ถูกต้อง มีอักขระ / สตริงหนึ่งตัวที่ฉันต้องระวังหรือไม่?
TimTim

27
@TimTim - ไม่ การป้อนข้อมูลของผู้ใช้ทั้งหมดควรเสมอถือว่าเป็นศัตรูโดยเนื้อแท้
ซอมบี้

นอกจากนี้ข้อมูลภายใน (พนักงานระบบดูแลระบบและอื่น ๆ ) อาจไม่ปลอดภัย คุณควรระบุและตรวจสอบข้อมูล (พร้อมวันที่บันทึกและผู้ใช้) ที่แสดงพร้อมการตีความ
ซามูเอล Dauzon

9

ตามลำดับที่ต้องการ:

  1. หากคุณกำลังใช้เครื่องมือสร้างแรงบิด (เช่น Twig, Smarty, Blade) ให้ตรวจสอบว่ามีการหลบหนีตามบริบท ฉันรู้จากประสบการณ์ที่ทวิกทำ{{ var|e('html_attr') }}
  2. หากคุณต้องการให้ HTML ใช้HTML เพียวริฟายเออ แม้ว่าคุณคิดว่าคุณจะยอมรับเฉพาะ Markdown หรือ ReStructuredText เท่านั้นคุณยังคงต้องการทำให้ HTML ของภาษามาร์กอัปเหล่านี้บริสุทธิ์
  3. มิฉะนั้นการใช้งานและให้แน่ใจว่าส่วนที่เหลือของเอกสารของคุณใช้ตัวอักษรชุดเดียวกับhtmlentities($var, ENT_QUOTES | ENT_HTML5, $charset) $charsetในกรณีส่วนใหญ่'UTF-8'เป็นชุดอักขระที่ต้องการ

นอกจากนี้ยังให้แน่ใจว่าคุณหลบหนีในการส่งออกไม่ได้อยู่ในการป้อนข้อมูล


7

การผ่านรายการนี้เป็นการอ้างอิงแบบรวมจาก SO Documentation เบต้าซึ่งกำลังออฟไลน์

ปัญหา

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

ตัวอย่างเช่นหากฝ่ายที่สามมีไฟล์ JavaScript:

// http://example.com/runme.js
document.write("I'm running");

และแอปพลิเคชัน PHP ส่งเอาต์พุตสตริงโดยตรงเข้ามาโดยตรง:

<?php
echo '<div>' . $_GET['input'] . '</div>';

หากมีพารามิเตอร์ GET ที่ไม่ถูกตรวจสอบ<script src="http://example.com/runme.js"></script>แล้วผลลัพธ์ของสคริปต์ PHP จะเป็น:

<div><script src="http://example.com/runme.js"></script></div>

JavaScript ของบุคคลที่สามจะทำงานและผู้ใช้จะเห็น "ฉันกำลังทำงาน" บนหน้าเว็บ

สารละลาย

ตามกฎทั่วไปอย่าเชื่อถืออินพุตที่มาจากลูกค้า GET, POST และคุกกี้ทุกค่าสามารถเป็นอะไรก็ได้และควรได้รับการตรวจสอบ เมื่อแสดงผลของค่าใด ๆ เหล่านี้ให้หลีกเลี่ยงเพื่อไม่ให้มีการประเมินในลักษณะที่ไม่คาดคิด

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

PHP มีวิธีหนีผลลัพธ์ขึ้นอยู่กับบริบท

ฟังก์ชั่นตัวกรอง

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

การเข้ารหัส HTML

htmlspecialcharsจะแปลง "อักขระพิเศษ HTML" เป็นการเข้ารหัส HTML ซึ่งหมายความว่าจะไม่ถูกประมวลผลเป็น HTML มาตรฐาน ในการแก้ไขตัวอย่างก่อนหน้าของเราโดยใช้วิธีนี้:

<?php
echo '<div>' . htmlspecialchars($_GET['input']) . '</div>';
// or
echo '<div>' . filter_input(INPUT_GET, 'input', FILTER_SANITIZE_SPECIAL_CHARS) . '</div>';

จะส่งออก:

<div>&lt;script src=&quot;http://example.com/runme.js&quot;&gt;&lt;/script&gt;</div>

ทุกสิ่งที่อยู่ภายใน<div>แท็กจะไม่ถูกตีความเป็นเบราว์เซอร์ของแท็ก JavaScript แต่จะเป็นโหนดข้อความธรรมดา ผู้ใช้จะเห็นอย่างปลอดภัย:

<script src="http://example.com/runme.js"></script>

การเข้ารหัส URL

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

<?php
$input = urlencode($_GET['input']);
// or
$input = filter_input(INPUT_GET, 'input', FILTER_SANITIZE_URL);
echo '<a href="http://example.com/page?input="' . $input . '">Link</a>';

อินพุตที่เป็นอันตรายใด ๆ จะถูกแปลงเป็นพารามิเตอร์ URL ที่เข้ารหัส

ใช้ไลบรารีภายนอกแบบพิเศษหรือรายการ OWASP AntiSamy

บางครั้งคุณอาจต้องการส่ง HTML หรือใส่รหัสอื่น ๆ คุณจะต้องเก็บรายการคำที่ได้รับอนุญาต (บัญชีขาว) และไม่ได้รับอนุญาต (บัญชีดำ)

คุณสามารถดาวน์โหลดรายการมาตรฐานที่สามารถใช้ได้ที่เว็บไซต์ OWASP AntiSamy แต่ละรายการเหมาะสำหรับการโต้ตอบเฉพาะประเภท (ebay api, tinyMCE, ฯลฯ ... ) และมันก็เป็นโอเพนซอร์ซ

มีห้องสมุดที่มีอยู่เพื่อกรอง HTML และป้องกันการโจมตี XSS สำหรับกรณีทั่วไปและดำเนินการอย่างน้อยเช่นเดียวกับรายการ AntiSamy ที่ใช้งานง่ายมาก ตัวอย่างเช่นคุณมีตัวกรอง HTML


5

เฟรมเวิร์กจำนวนมากช่วยจัดการ XSS ในรูปแบบต่างๆ เมื่อเปิดใช้งานของคุณเองหรือหากมีข้อกังวล XSS เราสามารถใช้filter_input_array (มีให้ใน PHP 5> = 5.2.0, PHP 7) โดยทั่วไปฉันจะเพิ่มส่วนย่อยนี้ใน SessionController ของฉันเพราะการโทรทั้งหมดผ่านตัวควบคุมอื่น ๆ โต้ตอบกับข้อมูล ด้วยวิธีนี้อินพุตของผู้ใช้ทั้งหมดจะได้รับการทำให้บริสุทธิ์ใน 1 ตำแหน่งศูนย์กลาง หากการดำเนินการนี้เกิดขึ้นที่จุดเริ่มต้นของโครงการหรือก่อนที่ฐานข้อมูลของคุณจะถูกวางยาพิษคุณไม่ควรมีปัญหาใด ๆ ในขณะที่ส่งออก ... หยุดขยะในทิ้งขยะ

/* Prevent XSS input */
$_GET   = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING);
$_POST  = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
/* I prefer not to use $_REQUEST...but for those who do: */
$_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST;

ดังกล่าวข้างต้นจะลบทั้งหมด HTML และแท็กสคริปต์ หากคุณต้องการโซลูชันที่ช่วยให้แท็กปลอดภัยขึ้นอยู่กับรายการที่อนุญาตให้ตรวจสอบHTML เพียวริฟายเออ


หากฐานข้อมูลของคุณมีพิษอยู่แล้วหรือคุณต้องการจัดการกับ XSS ณ เวลาที่ออกOWASPแนะนำให้สร้างฟังก์ชั่น wrapper ที่กำหนดเองสำหรับechoและใช้มันทุกที่ที่คุณส่งออกค่าที่ผู้ใช้กำหนด:

//xss mitigation functions
function xssafe($data,$encoding='UTF-8')
{
   return htmlspecialchars($data,ENT_QUOTES | ENT_HTML401,$encoding);
}
function xecho($data)
{
   echo xssafe($data);
}

2

คุณยังสามารถตั้งค่าส่วนหัวการตอบสนอง HTTP ที่เกี่ยวข้องกับ XSS ผ่าน header(...)

การป้องกัน X-XSS "1; mode = block"

เพื่อให้แน่ใจว่าเปิดใช้งานโหมดการป้องกัน XSS ของเบราว์เซอร์

นโยบายความปลอดภัยของเนื้อหา "default-src 'self'; ... "

เพื่อเปิดใช้งานความปลอดภัยของเนื้อหาเบราว์เซอร์ ดูรายละเอียดนโยบายความปลอดภัยเนื้อหา (CSP) นี้: http://content-security-policy.com/ โดยเฉพาะการตั้งค่า CSP เพื่อบล็อกอินไลน์สคริปต์และแหล่งสคริปต์ภายนอกมีประโยชน์กับ XSS

สำหรับกลุ่มทั่วไปของส่วนหัวการตอบกลับ HTTP ที่มีประโยชน์เกี่ยวกับความปลอดภัยของ webapp คุณดูที่ OWASP: https://www.owasp.org/index.php/List_of_useful_HTTP_headers


1
<?php
function xss_clean($data)
{
// Fix &entity\n;
$data = str_replace(array('&amp;','&lt;','&gt;'), array('&amp;amp;','&amp;lt;','&amp;gt;'), $data);
$data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
$data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
$data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');

// Remove any attribute starting with "on" or xmlns
$data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);

// Remove javascript: and vbscript: protocols
$data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);

// Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);

// Remove namespaced elements (we do not need them)
$data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);

do
{
    // Remove really unwanted tags
    $old_data = $data;
    $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
}
while ($old_data !== $data);

// we are done...
return $data;
}

5
คุณไม่ควรใช้preg_replaceตามที่ใช้evalกับข้อมูลที่คุณป้อน owasp.org/index.php/PHP_Security_Cheat_Sheet#Code_Inject
CrabLab

0

ใช้ในhtmlspecialchars PHPใน HTML พยายามหลีกเลี่ยงการใช้:

element.innerHTML = “…”; element.outerHTML = “…”; document.write(…); document.writeln(…);

ที่varถูกควบคุมโดยผู้ใช้

เห็นได้ชัดว่าพยายามหลีกเลี่ยงeval(var)หากคุณต้องใช้สิ่งใดสิ่งหนึ่งจากนั้นลองใช้JS กำลังหลบหนีออกไปHTML จะหลีกเลี่ยงพวกเขาและคุณอาจต้องทำอะไรมากกว่านี้ แต่สำหรับพื้นฐานที่ควรจะเพียงพอ


0

วิธีที่ดีที่สุดในการปกป้องอินพุตของคุณโดยใช้htmlentitiesฟังก์ชั่น ตัวอย่าง:

htmlentities($target, ENT_QUOTES, 'UTF-8');

คุณจะได้รับข้อมูลเพิ่มเติมที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.