Jak zapobiec XSS z HTML / PHP?

256

Jak mogę zapobiec XSS (skryptom cross-site) używając tylko HTML i PHP?

Widziałem wiele innych postów na ten temat, ale nie znalazłem artykułu, który jasno i zwięźle stwierdza, jak faktycznie zapobiegać XSS.

Tim tim
źródło
3
Tylko uwaga, że ​​to nie rozwiąże przypadku, w którym możesz chcieć użyć danych wejściowych użytkownika jako atrybutu HTML. Na przykład źródłowy adres URL obrazu. Nie jest to częsty przypadek, ale łatwy do zapomnienia.
Michael Mior
@MichaelMior tutaj jest rozwiązanie, aby zapobiec XSS w hreflub srcatrybut HTML: stackoverflow.com/questions/19047119/…
baptx
Jest tutaj miły artykuł , który wyjaśnia XSS i jak temu zapobiec w różnych językach (w tym PHP).
XCore

Odpowiedzi:

296

Zasadniczo musisz użyć tej funkcji, htmlspecialchars()gdy chcesz wypisać coś do przeglądarki, które pochodzi z danych wprowadzonych przez użytkownika.

Prawidłowy sposób korzystania z tej funkcji jest mniej więcej taki:

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

Google Code University ma również te bardzo edukacyjne filmy na temat bezpieczeństwa w sieci:

Alix Axel
źródło
7
@TimTim: W większości przypadków tak. Jednak gdy chcesz zezwolić na wprowadzanie HTML, sprawy stają się nieco trudniejsze, a jeśli tak, to polecam użyć czegoś takiego jak htmlpurifier.org
Alix Axel
@Alix Axel, więc czy twoja odpowiedź na użycie htmlspecialchars lub htmlpurifier.org ?
TimTim
3
Jeśli chcesz zaakceptować wprowadzanie HTML, użyj HTML Purifier, jeśli nie, użyj htmlspecialchars().
Alix Axel
9
htmlspecialchars czy htmlentities? Sprawdź tutaj stackoverflow.com/questions/46483/…
kiranvj
4
W większości przypadków jest to poprawne, ale nie jest tak proste. Powinieneś rozważyć umieszczenie niezaufanego łańcucha w HTML, Js, Css i rozważyć umieszczenie niezaufanego HTML w HTML. Spójrz na to: owasp.org/index.php/...
brązu człowieka
41

Jednym z moich ulubionych odniesień do OWASP jest wyjaśnienie dotyczące skryptów krzyżowych, ponieważ chociaż istnieje wiele wektorów ataku XSS, przestrzeganie kilku reguł może obronić się przed większością z nich!

To jest ściągawka PHP Security

Wahyu Kristianto
źródło
7
Ja też .. To jest Ściągawka na
1
Nie do końca XSS, ale myślę, że XSS i CSRF są często pomieszane i oba są naprawdę niebezpieczne: owasp.org/index.php/…
Simon
2
Ta strona już nie istnieje
Mazzy
1
@Mazzy ostatnia pamięć podręczna web.archive.org/web/20180817180409/owasp.org/index.php/…
Wahyu Kristianto
15

Jednym z najważniejszych kroków jest oczyszczenie danych wejściowych użytkownika przed ich przetworzeniem i / lub renderowaniem z powrotem do przeglądarki. PHP ma kilka funkcji „ filtrowania ”, których można użyć.

Formą, jaką zwykle mają ataki XSS, jest wstawienie linku do jakiegoś zewnętrznego javascript, który zawiera szkodliwe zamiary dla użytkownika. Przeczytaj więcej na ten temat tutaj .

Będziesz także chciał przetestować swoją witrynę - mogę polecić dodatek Firefox XSS Me .

James Kolpack
źródło
Czego potrzebuję, aby dokładnie zdezynfekować dane wejściowe. Czy jest jakiś konkretny znak / ciąg, na który muszę uważać?
TimTim
27
@TimTim - nie. Wszystkie dane wejściowe użytkownika powinny być zawsze uważane za wrogo nastawione.
zombat
Poza tym dane wewnętrzne (pracownicy, sysadmin itp.) Mogą być niebezpieczne. Należy identyfikować i monitorować (z datą rejestracji i użytkownikiem) dane wyświetlane z interpretacją.
Samuel Dauzon,
9

W kolejności preferencji:

  1. Jeśli używasz silnika szablonów (np. Twig, Smarty, Blade), sprawdź, czy umożliwia on ucieczkę kontekstową. Wiem z doświadczenia, że ​​robi to Twig.{{ var|e('html_attr') }}
  2. Jeśli chcesz zezwolić na HTML, użyj HTML Purifier . Nawet jeśli uważasz, że akceptujesz tylko Markdown lub ReStructuredText, nadal chcesz oczyścić HTML z wyjściowych języków znaczników.
  3. W przeciwnym razie użyj htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset)i upewnij się, że reszta dokumentu używa tego samego zestawu znaków jak $charset. W większości przypadków 'UTF-8'jest pożądanym zestawem znaków.

Upewnij się także, że uciekasz na wyjściu, a nie na wejściu .

Scott Arciszewski
źródło
7

Publikowanie tego jako skonsolidowanego odniesienia z wersji beta Dokumentacji SO, która przechodzi w tryb offline.

Problem

Skrypty między witrynami to niezamierzone wykonanie zdalnego kodu przez klienta WWW. Każda aplikacja internetowa może narazić się na XSS, jeśli pobierze dane wejściowe od użytkownika i wyśle ​​je bezpośrednio na stronie internetowej. Jeśli dane wejściowe obejmują HTML lub JavaScript, zdalny kod może zostać wykonany, gdy treść ta jest renderowana przez klienta WWW.

Na przykład, jeśli strona zewnętrzna zawiera plik JavaScript:

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

A aplikacja PHP bezpośrednio przekazuje ciąg znaków do niej przekazany:

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

Jeśli niezaznaczony parametr GET zawiera, <script src="http://example.com/runme.js"></script>dane wyjściowe skryptu PHP będą następujące:

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

Uruchomiony zostanie skrypt JavaScript innej firmy, a użytkownik zobaczy „Uruchomię” na stronie internetowej.

Rozwiązanie

Zasadniczo nigdy nie ufaj wkładowi pochodzącemu od klienta. Każda wartość GET, POST i cookie może być dowolną wartością, dlatego należy ją zweryfikować. Kiedy wypisujesz którąś z tych wartości, unikaj ich, aby nie zostały ocenione w nieoczekiwany sposób.

Należy pamiętać, że nawet w najprostszych aplikacjach dane można przenosić i trudno będzie śledzić wszystkie źródła. Dlatego najlepszą praktyką jest zawsze unikanie wyjścia.

PHP oferuje kilka sposobów na uniknięcie wyjścia w zależności od kontekstu.

Funkcje filtrów

Funkcje filtrowania PHP pozwalają na dezynfekcję lub walidację danych wejściowych do skryptu php na wiele sposobów . Są przydatne podczas zapisywania lub wysyłania danych wejściowych klienta.

Kodowanie HTML

htmlspecialcharsprzekonwertuje żadnych „znaków szczególnych html” w ich kodowania HTML, co oznacza, że wtedy nie być przetwarzane w standardzie HTML. Aby naprawić nasz poprzedni przykład przy użyciu tej metody:

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

Wyprowadziłby:

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

Wszystko wewnątrz <div>znacznika nie będzie interpretowane przez przeglądarkę jako znacznik JavaScript, ale jako zwykły węzeł tekstowy. Użytkownik bezpiecznie zobaczy:

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

Kodowanie URL

Podczas generowania dynamicznie generowanego adresu URL PHP zapewnia urlencodefunkcję bezpiecznego generowania prawidłowych adresów URL. Na przykład jeśli użytkownik jest w stanie wprowadzić dane, które stają się częścią innego parametru 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>';

Wszelkie złośliwe dane wejściowe zostaną przekonwertowane na zakodowany parametr adresu URL.

Korzystanie ze specjalistycznych bibliotek zewnętrznych lub list OWASP AntiSamy

Czasami będziesz chciał wysłać HTML lub inny rodzaj kodu. Będziesz musiał prowadzić listę autoryzowanych słów (biała lista) i nieautoryzowanych (czarna lista).

Możesz pobrać standardowe listy dostępne na stronie OWASP AntiSamy . Każda lista nadaje się do określonego rodzaju interakcji (eBay api, tinyMCE itp.). I to jest open source.

Istnieją biblioteki, które filtrują HTML i zapobiegają atakom XSS w ogólnym przypadku i wykonują co najmniej tak samo dobrze, jak listy AntiSamy z bardzo łatwą obsługą. Na przykład masz HTML Purifier

Matt S.
źródło
5

Wiele platform pomaga obsługiwać XSS na różne sposoby. Podczas toczenia własnego lub jeśli istnieje problem z XSS, możemy wykorzystać filter_input_array (dostępny w PHP 5> = 5.2.0, PHP 7.) Zazwyczaj dodam ten fragment kodu do mojego SessionController, ponieważ wszystkie wywołania przechodzą tam przed jakimkolwiek innym kontrolerem współdziała z danymi. W ten sposób wszystkie dane wejściowe użytkownika są dezynfekowane w 1 centralnej lokalizacji. Jeśli dzieje się tak na początku projektu lub przed zatruciem bazy danych, nie powinieneś mieć żadnych problemów w momencie wyjścia ... zatrzymuje zatrzymywanie śmieci, usuwanie śmieci.

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

Powyższe spowoduje usunięcie WSZYSTKICH tagów HTML i skryptów. Jeśli potrzebujesz rozwiązania, które pozwala na bezpieczne tagi, oparte na białej liście, sprawdź HTML Purifier .


Jeśli twoja baza danych jest już zatruta lub chcesz poradzić sobie z XSS w momencie wyjścia, OWASP zaleca utworzenie niestandardowej funkcji opakowania dla echoi używanie jej WSZĘDZIE, gdzie wypisujesz wartości dostarczone przez użytkownika:

//xss mitigation functions
function xssafe($data,$encoding='UTF-8')
{
   return htmlspecialchars($data,ENT_QUOTES | ENT_HTML401,$encoding);
}
function xecho($data)
{
   echo xssafe($data);
}
webaholik
źródło
2

Możesz również ustawić niektóre nagłówki odpowiedzi HTTP związane z XSS za pośrednictwem header(...)

Ochrona X-XSS „1; mode = block”

dla pewności włączony jest tryb ochrony XSS przeglądarki.

Polityka bezpieczeństwa treści „default-src„ self ”; ...”

aby włączyć zabezpieczenia treści po stronie przeglądarki. Zobacz ten, aby uzyskać szczegółowe informacje na temat zasad bezpieczeństwa treści (CSP): http://content-security-policy.com/ Zwłaszcza skonfigurowanie CSP do blokowania skryptów wbudowanych i zewnętrznych źródeł skryptów jest pomocne w przypadku XSS.

ogólną garść przydatnych nagłówków odpowiedzi HTTP dotyczących bezpieczeństwa twojej aplikacji internetowej można znaleźć na stronie OWASP: https://www.owasp.org/index.php/List_of_useful_HTTP_headers

Chris
źródło
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;
}
Abdo-Host
źródło
5
Nie powinieneś używać preg_replacetak, jak używa evalna swój wkład. owasp.org/index.php/PHP_Security_Cheat_Sheet#Code_Injection
CrabLab
0

Użyj htmlspecialcharsna PHP. Na HTML staraj się unikać używania:

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

gdzie varjest kontrolowany przez użytkownika .

Oczywiście staraj się unikać eval(var), jeśli musisz użyć któregoś z nich, spróbuj JS, unikając ich, HTML uciec i być może będziesz musiał zrobić coś więcej, ale dla podstaw powinno to wystarczyć.

Pablo
źródło
0

Najlepszym sposobem ochrony danych wejściowych jest htmlentitiesfunkcja użytkowania . Przykład:

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

Możesz uzyskać więcej informacji tutaj .

Marco Concas
źródło