#204 XSS Protection in Rails 3
- Download:
- source codeProject Files in Zip (156 KB)
- mp4Full Size H.264 Video (11.8 MB)
- m4vSmaller H.264 Video (8.63 MB)
- webmFull Size VP8 Video (21 MB)
- ogvFull Size Theora Video (15.1 MB)
translated by Paweł Lenart
Wcześniej, w odcinku 27 [obejrzyj, przeczytaj] omówiliśmy temat cross-site scriptingu. Jest to istotny temat do zrozumienia z punktu widzenia osoby, która tworzy aplikacje webowe. Jednym z miejsc, w których aplikacja jest podatna na ataki typu XSS jest wyświetlanie danych wprowadzonych przez użytkownika. Należy zatem zadbać o zabezpieczenie tych danych w aplikacji (potocznie zwanym "escape'owaniem") w kontekście ich wyświetlania i zwykle w aplikacjach Rails jest to dokonywane poporzez użycie metody h
.
<%= h comment.content %>
Użycie metody h w celu zabezpieczenia danych wyjściowych.
Aczkolwiek w Rails 3 dane wyjściowe są automatyczynie zabezpieczane, więc nie ma potrzeby umieszczania metody h
w twoich widokach. W tym odcinku zostanie pokazane, jak to działa to w Rails 3.
W celu zademonstrowania, jak działa escape'owanie zostanie użyty prosty blog napisany w Rails 3. W tej aplikacji znajdują się artykuły, a każdy z artykułów posiada pewną ilość komentarzy przypisanych do nich. Żeby sprawdzić, jak system komentarzy radzi sobie z próbą przeprowadzenia ataku XSS wprowadzimy <script>alert('I steal cookies!')</script>
do każdego pola formularza i zatwierdzimy nasz szkodliwy komentarz.
Wprowadzony i zatwierdzony komentarz zostaje wyświetlony na ekranie i widać, jak Rails 3 automatycznie wyescape'ował wszystkie tagi HTML w polach, gdzie zostały one wprowadzone. Zobaczmy, jak Rails to robi.
Kod, który jest odpowiedzialny za wyświetlanie każdego z komentarzy jest zawarty w partialu i jeśli spojrzysz w źródło, to zobaczysz, że żadne dane wyjściowe nie są zabezpieczane za pomocą metody h
.
<div class="comment"> <strong><%= link_to comment.name, comment.url %></strong> <p><%= comment.content %></p> </div>
W Rails 2 oznaczałoby to, że funkcje alert byłyby wyświetlane na ekranie, ale w Rails 3 wszystkie dane wyjściowe są poprawnie escape'owane, nawet jeśli są przekazywane poprzez helpery takie jak link_to, więc nie ma potrzeby użycia metody h
w tym przypadku.
Co się jednak dzieje, jeżeli portujemy aplikację z Rails 2 i wyjście w niej jest escape'owane za pomocą h
? Możemy się dowiedzieć poprzez wyescape'owanie wyjścia w partialu powyżej i przeładowanie strony.
<div class="comment"> <strong><%= link_to h(comment.name), comment.url %></strong> <p><%= h comment.content %></p> </div>
Po przeładowaniu strony widać, że wynik wygląda tak samo i dane wyjściowe nie są escape'owane podwójnie. Rails jest tutaj sprytny - nawet, jeśli zostanie tutaj użyta metoda h
to tag <script> zostanie wyescape'owany tylko raz.
Mogłoby się wydawać, że metoda h
w Rails 3 w takim razie nie robi nic, ale to nie do końca prawda. Wciąż ma pewne przeznaczenie i zostanie ono pokazane później, ale najpierw przyjrzymy się pokrewnej funkcjonalności. To, że Rails 3 automatycznie zabezpiecza wprowadzane dane okazuje się być bardzo wygodnym, ale czasem może zajść potrzeba wyświetlenia surowej zawartości. Jeżeli wierzymy wprowadzanym danym przez użytkownika (np. administratora) i chcemy wyświetlić dane dokładnie tak jak zostały one wprowadzone, to możemy użyć w tym celu metody raw
.
<div class="comment"> <strong><%= link_to comment.name, comment.url %></strong> <p><%= raw comment.content %></p> </div>
Jeżeli strona zostanie przeładowana, to kod JavaScript wprowadzony w polu komentarza zostanie wykonany.
Tak więc każdorazowo, gdy nie chcemy, żeby zawartość była escape'owana możemy użyć raw
. Ale jak to działa? Rails 3 wydaje się być całkiem sprytne w kontekście określania, kiedy escape'ować zawartość, a kiedy nie. Zobaczmy, jak to działa.
Pokażemy escape'owanie w konsoli, którą w Rails 3 możemy wywołać za pomocą komendy rails c
.
$ rails c Loading development environment (Rails 3.0.0.beta) ruby-1.9.1-p378 >
Rails 3 ma pojęcie na temat łańcuchów bezpiecznych w HTML. To oznacza, że możemy sprawdzić, czy jakikolwiek łańcuch jest bezpieczny do wyświetlenia jako HTML poprzez wywołanie nowej metody html_safe?
na nim.
> "foo".html_safe? => false
Możemy oznaczyć łańcuch jako bezpieczny w HTML wywołując na nim metodę html_safe
.
> safe = "safe".html_safe => "safe" > safe.html_safe? => true
Żadne escape'owanie nie ma tu miejsca. Wszystko, co się dzieje to oznaczenie właściwości na łańcuchu, aby określić, czy powinien być escape'owany przed wyświetleniem.
Więc jak to się ma do naszego widoku? Rails sprawdza każdy fragment danych wyjściowych i patrzy, czy został oznaczony jako bezpieczny w HTML. Jeżeli nie jest, to zostanie automatycznie zabezpieczony, a jeżeli jest, to zostanie wyświetlony bez żadnej ingerencji. Jeśli użyjemy metody h
, to łańcuch zostanie wyescape'owany i oznaczony jako bezpieczny w HTML. To oznacza, że Rails będzie wiedział, że łańcuch jest bezpieczny i nie przeprowadzi escape'owania ponownie.
Użycie metody raw
spowoduje oznaczenie łańcucha jako bezpieczny i przekazanie go do wyjścia bez żadnych zmian.
To bardzo istotna rzecz, jeżeli używasz helperów. Wyjaśnimy to poprzez stworzenie helpera o nazwie strong
, który będzie obejmował dane wejściowe w tagi <strong>
. Zostanie on użyty w naszym szablonie w następujący sposób:
<div class="comment"> <%= strong link_to(comment.name, comment.url) %> <p><%= raw comment.content %></p> </div> <p>Stworzymy metodę <code>strong</code> w <code>ApplicationHelper</code>:</p> ``` /app/helpers/application_helper.rb module ApplicationHelper def strong(content) "<strong>#{content}</strong>" end end
Po przeładowaniu strony zobaczymy, że helper nie zadziałał tak jak tego chcieliśmy.
Automatyczne escape&aops;owanie Rails 3 zabezpieczyło tag <strong>
ponieważ nasz helper nie tworzy łańcucha bezpiecznego w HTML.
Dwa proste warunki muszą zostać spełnione podczas pracy z helperami, które zwracają HTML. Po pierwsze, musimy się upewnić, że zwracane łańcuchy są oznaczane jako bezpieczne w HTML.
module ApplicationHelper def strong(content) "<strong>#{content}</strong>".html_safe end end
To rozwiązuje kwestię taga <strong>
, ale zawartość pomiędzy tagami nie zostanie wyescape'owana. Możemy tę kwestię rozwiązać poprzez użycie metody h
na zawartości:
module ApplicationHelper def strong(content) "<strong>#{h(content)}</strong>".html_safe end end
Jak tylko będziemy pamiętać o używaniu metody h
na wszystkich danych wejściowych i oznaczać wyjściowy łańcuch jako html_safe
, to będzie wyświetlany poprawnie. Po odświeżeniu strony możemy zobaczyć, że tag <strong>
jest renderowany poprawnie, a zawarość drugiego komentarza, która zawiera potencjalnie niebezpieczny kod JavaScript została wyescape'owana.
To wszystko w tym odcinku. Automatyczne escape'owanie w Rails 3 pozwala nam nie martwić się o zabezpieczanie każdego fragmentu wprowadzanych danych za pomocą metody h
pomniejszając ryzyko ataku typu XSS na naszą aplikację.