Làm cách nào để ngăn XSS bằng HTML / PHP?


256

Làm cách nào để ngăn XSS (tập lệnh chéo trang) chỉ sử dụng HTML và PHP?

Tôi đã thấy nhiều bài viết khác về chủ đề này nhưng tôi chưa tìm thấy một bài viết rõ ràng và chính xác làm thế nào để ngăn chặn XSS.


3
Chỉ cần lưu ý rằng điều này sẽ không giải quyết trường hợp bạn có thể muốn sử dụng đầu vào của người dùng làm thuộc tính HTML. Ví dụ: URL nguồn của hình ảnh. Không phải là một trường hợp phổ biến, nhưng một dễ dàng để quên.
Michael Mior

@MichaelMior ở đây là một giải pháp để ngăn chặn XSS trong hrefhoặc srcthuộc tính HTML: stackoverflow.com/questions/19047119/
Kẻ

Có một bài viết hay ở đây giải thích về XSS và cách ngăn chặn nó bằng các ngôn ngữ khác nhau (bao gồm PHP).
XCore

Câu trả lời:


296

Về cơ bản, bạn cần sử dụng chức năng htmlspecialchars()bất cứ khi nào bạn muốn xuất một cái gì đó ra trình duyệt xuất phát từ đầu vào của người dùng.

Cách chính xác để sử dụng chức năng này là một cái gì đó như thế này:

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

Đại học Google Code cũng có những video rất giáo dục về Bảo mật web:


7
@TimTim: Đối với hầu hết các trường hợp, yeah. Tuy nhiên, khi bạn cần cho phép nhập liệu HTML, mọi thứ sẽ phức tạp hơn một chút và nếu đây là trường hợp tôi khuyên bạn nên sử dụng một cái gì đó như htmlpurifier.org
Alix Axel

@Alix Axel, vậy câu trả lời của bạn là sử dụng htmlspecialchars hay sử dụng htmlpurifier.org ?
TimTim

3
Nếu bạn cần chấp nhận đầu vào HTML, hãy sử dụng Bộ lọc HTML, nếu không sử dụng htmlspecialchars().
Alix Axel

9
htmlspecialchars hay htmlentity? Kiểm tra tại đây stackoverflow.com/questions/46483/ từ
kiranvj

4
Hầu hết thời gian là chính xác, nhưng nó không đơn giản như thế. Bạn nên xem xét đưa chuỗi không tin cậy vào HTML, Js, Css và xem xét đưa HTML không đáng tin cậy vào HTML. Nhìn vào đây: owasp.org/index.php/
người đàn ông bằng đồng

41

Một trong những tài liệu tham khảo OWASP yêu thích của tôi là giải thích về Cross-Site Scripting bởi vì trong khi có một số lượng lớn các vectơ tấn công XSS, thì một vài quy tắc sau đây có thể chống lại phần lớn trong số chúng!

Đây là PHP Cheat Sheet


7
Tôi cũng vậy .. Đây là Bộ lọc XSS Evasion Cheat Sheet owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet

1
Không chính xác là XSS, nhưng tôi nghĩ XSS và CSRF thường bị lẫn lộn và cả hai đều thực sự nguy hiểm: owasp.org/index.php/iêu
Simon

2
Trang này không còn tồn tại
Mazzy


15

Một trong những bước quan trọng nhất là vệ sinh mọi đầu vào của người dùng trước khi nó được xử lý và / hoặc được trả lại cho trình duyệt. PHP có một số hàm " bộ lọc " có thể được sử dụng.

Hình thức mà các cuộc tấn công XSS thường có là chèn một liên kết đến một số javascript ngoài trang web có chứa mục đích xấu cho người dùng. Đọc thêm về nó ở đây .

Bạn cũng sẽ muốn kiểm tra trang web của mình - Tôi có thể giới thiệu XSS Me bổ trợ Firefox .


Tôi cần làm gì để đảm bảo tôi vệ sinh đầu vào chính xác từ đó. Có một ký tự / chuỗi cụ thể mà tôi phải coi chừng không?
TimTim

27
@TimTim - không. Tất cả đầu vào của người dùng phải luôn được coi là thù địch vốn có.
zombat

Ngoài ra, dữ liệu nội bộ (nhân viên, sysadmin, v.v.) có thể không an toàn. Bạn nên xác định và theo dõi (với ngày đăng nhập và người dùng) dữ liệu được hiển thị với thông dịch.
Samuel Dauzon

9

Trong thứ tự ưu tiên:

  1. Nếu bạn đang sử dụng một công cụ tạo khuôn mẫu (ví dụ Twig, Smarty, Blade), hãy kiểm tra xem nó có cung cấp khả năng thoát theo ngữ cảnh hay không. Tôi biết từ kinh nghiệm mà Twig làm.{{ var|e('html_attr') }}
  2. Nếu bạn muốn cho phép HTML, hãy sử dụng Bộ lọc HTML . Ngay cả khi bạn nghĩ rằng bạn chỉ chấp nhận Markdown hoặc ReSturationuredText, bạn vẫn muốn thanh lọc HTML các ngôn ngữ đánh dấu này.
  3. Mặt khác, sử dụng htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset)và đảm bảo phần còn lại của tài liệu của bạn sử dụng cùng một bộ ký tự như $charset. Trong hầu hết các trường hợp, 'UTF-8'là bộ ký tự mong muốn.

Ngoài ra, hãy chắc chắn rằng bạn thoát trên đầu ra, không phải đầu vào .


7

Đăng chéo này dưới dạng tài liệu tham khảo tổng hợp từ phiên bản beta Tài liệu SO đang ngoại tuyến.

Vấn đề

Kịch bản chéo trang là việc thực thi mã từ xa ngoài ý muốn của máy khách web. Bất kỳ ứng dụng web nào cũng có thể tự hiển thị với XSS nếu nó nhận đầu vào từ người dùng và xuất trực tiếp trên trang web. Nếu đầu vào bao gồm HTML hoặc JavaScript, mã từ xa có thể được thực thi khi nội dung này được hiển thị bởi máy khách web.

Ví dụ: nếu bên thứ 3 chứa tệp JavaScript:

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

Và một ứng dụng PHP trực tiếp xuất ra một chuỗi được truyền vào nó:

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

Nếu tham số GET không được kiểm tra chứa <script src="http://example.com/runme.js"></script>thì đầu ra của tập lệnh PHP sẽ là:

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

JavaScript của bên thứ 3 sẽ chạy và người dùng sẽ thấy "Tôi đang chạy" trên trang web.

Giải pháp

Theo nguyên tắc chung, không bao giờ tin tưởng đầu vào đến từ khách hàng. Mọi giá trị GET, POST và cookie đều có thể là bất cứ thứ gì, và do đó nên được xác nhận. Khi xuất bất kỳ giá trị nào trong số này, hãy thoát chúng để chúng không bị đánh giá theo cách không mong muốn.

Hãy nhớ rằng ngay cả trong các ứng dụng đơn giản nhất, dữ liệu có thể được di chuyển xung quanh và sẽ khó theo dõi tất cả các nguồn. Vì vậy, đó là một thực hành tốt nhất để luôn luôn thoát đầu ra.

PHP cung cấp một vài cách để thoát đầu ra tùy thuộc vào ngữ cảnh.

Chức năng lọc

Các hàm lọc của PHP cho phép dữ liệu đầu vào của tập lệnh php được vệ sinh hoặc xác nhận theo nhiều cách . Chúng rất hữu ích khi lưu hoặc xuất dữ liệu đầu vào của khách hàng.

Mã hóa HTML

htmlspecialcharssẽ chuyển đổi bất kỳ "ký tự đặc biệt HTML" nào thành mã hóa HTML, nghĩa là chúng sẽ không được xử lý như HTML tiêu chuẩn. Để sửa ví dụ trước của chúng tôi bằng phương pháp này:

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

Sẽ xuất:

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

Mọi thứ bên trong <div>thẻ sẽ không được trình duyệt hiểu là thẻ JavaScript mà thay vào đó là một nút văn bản đơn giản. Người dùng sẽ thấy an toàn:

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

Mã hóa URL

Khi xuất ra một URL được tạo động, PHP cung cấp urlencodechức năng để xuất các URL hợp lệ một cách an toàn. Vì vậy, ví dụ, nếu người dùng có thể nhập dữ liệu trở thành một phần của tham số GET khác:

<?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>';

Bất kỳ đầu vào độc hại sẽ được chuyển đổi thành một tham số URL được mã hóa.

Sử dụng các thư viện bên ngoài chuyên biệt hoặc danh sách OWASP AntiSamy

Đôi khi bạn sẽ muốn gửi HTML hoặc các loại đầu vào mã khác. Bạn sẽ cần duy trì một danh sách các từ được ủy quyền (danh sách trắng) và không được ủy quyền (danh sách đen).

Bạn có thể tải xuống các danh sách tiêu chuẩn có sẵn tại trang web OWASP AntiSamy . Mỗi danh sách phù hợp với một loại tương tác cụ thể (ebay api, tinyMCE, v.v ...). Và nó là nguồn mở.

Có các thư viện hiện có để lọc HTML và ngăn chặn các cuộc tấn công XSS cho trường hợp chung và thực hiện ít nhất cũng như các danh sách AntiSamy với việc sử dụng rất dễ dàng. Ví dụ: bạn có Bộ lọc HTML


5

Nhiều khung giúp xử lý XSS theo nhiều cách khác nhau. Khi tự lăn hoặc nếu có một số lo ngại về XSS, chúng ta có thể tận dụng bộ lọc_input_array (có sẵn trong PHP 5> = 5.2.0, PHP 7.) Tôi thường sẽ thêm đoạn mã này vào Trình điều khiển phiên của mình, bởi vì tất cả các cuộc gọi đều đi qua đó trước bất kỳ bộ điều khiển nào khác tương tác với dữ liệu. Theo cách này, tất cả đầu vào của người dùng được vệ sinh ở 1 vị trí trung tâm. Nếu điều này được thực hiện khi bắt đầu dự án hoặc trước khi cơ sở dữ liệu của bạn bị nhiễm độc, bạn không nên có bất kỳ vấn đề nào tại thời điểm đầu ra ... dừng rác vào, bỏ rác.

/* 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;

Ở trên sẽ loại bỏ TẤT CẢ các thẻ HTML & script. Nếu bạn cần một giải pháp cho phép các thẻ an toàn, dựa trên danh sách trắng, hãy xem Bộ lọc HTML .


Nếu cơ sở dữ liệu của bạn đã bị nhiễm độc hoặc bạn muốn xử lý XSS tại thời điểm đầu ra, OWASP khuyên bạn nên tạo một hàm bao bọc tùy chỉnh cho echovà sử dụng nó MỌI THỨ bạn xuất ra các giá trị do người dùng cung cấp:

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

2

Bạn cũng có thể đặt một số tiêu đề phản hồi HTTP liên quan đến XSS thông qua header(...)

X-XSS-Bảo vệ "1; mode = block"

để chắc chắn, chế độ bảo vệ XSS của trình duyệt được bật.

Chính sách bảo mật nội dung "default-src 'self'; ..."

để cho phép bảo mật nội dung phía trình duyệt. Xem chi tiết này để biết chi tiết về Chính sách bảo mật nội dung (CSP): http://content-security-policy.com/ Đặc biệt là thiết lập CSP để chặn các tập lệnh nội tuyến và các nguồn tập lệnh bên ngoài rất hữu ích đối với XSS.

để biết một loạt các tiêu đề phản hồi HTTP hữu ích liên quan đến bảo mật của ứng dụng web của bạn, hãy xem OWASP: https://www.owasp.org/index.php/List_of_usiously_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
Bạn không nên sử dụng preg_replacevì nó sử dụng evaltrên đầu vào của bạn. owasp.org/index.php/PHP_Security_Cheat_Sheet#Code_Injection
CrabLab

0

Sử dụng htmlspecialcharstrên PHP. Trên HTML cố gắng tránh sử dụng:

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

nơi varđược kiểm soát bởi người dùng .

Ngoài ra, rõ ràng là cố gắng tránh eval(var), nếu bạn phải sử dụng bất kỳ trong số chúng thì hãy thử JS thoát chúng, HTML thoát chúng và bạn có thể phải làm thêm một số nhưng với những điều cơ bản thì điều này là đủ.


0

Cách tốt nhất để bảo vệ đầu vào của bạn là sử dụng htmlentitieschức năng. Thí dụ:

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

Bạn có thể nhận thêm thông tin ở đây .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.