Po co escape_javascript przed renderowaniem częściowej?

120

Patrzę na ten odcinek Railscast i zastanawiam się, dlaczego escape_javascripttutaj potrzebne jest wezwanie :

$("#reviews").append("<%= escape_javascript(render(:partial => @review)) %>");

Do czego escape_javascriptsłuży?

Zgodnie z dokumentacją Rails :

escape_javascript (javascript)

Escape Carrier zwraca oraz pojedyncze i podwójne cudzysłowy dla segmentów JavaScript.

Ale to niewiele dla mnie znaczy.


źródło
23
FYI, zaakceptowana odpowiedź tutaj nie jest poprawną odpowiedzią. Kikito's to
Steve

Odpowiedzi:

-3

Ponieważ nie chcesz, aby użytkownicy publikowali JavaScript, który faktycznie wykonuje przeglądarka?

Azeem.Butt
źródło
4
Ale co powiesz na to, że chcesz przekazać i renderować kod javascript?
berto77
Tak, właściwie to bardziej metoda formatowania. Nie uniemożliwia użytkownikom wykonywania ich własnego javascript. Nic nie może temu zapobiec.
LasagnaAndroid
3
To nie jest pomocne. Zamiast tego zobacz odpowiedź kikito .
nitsas
363

Łatwiej jest to zrozumieć, jeśli podzielisz kod na dwie części.

Pierwsza część $("#reviews").append("<%= ... %>");to javascript z erb. Oznacza to, że <%= ... %>zostanie zastąpiony przez kod ruby ​​w środku, który zwróci. Wynik tej zamiany musi być prawidłowym skryptem javascript, w przeciwnym razie zgłosi błąd, gdy klient spróbuje go przetworzyć. To pierwsza rzecz: potrzebujesz prawidłowego javascript .

Inną rzeczą, którą należy wziąć pod uwagę, jest to, że wszystko, co generuje ruby, musi być zawarte w łańcuchu javascript z podwójnymi cudzysłowami - zwróć uwagę na podwójne cudzysłowy wokół <%= ... %>. Oznacza to, że wygenerowany javascript będzie wyglądał następująco:

$("#reviews").append("...");

Teraz przyjrzyjmy się rubinowej części wewnątrz pliku <%= ... %>. Co robi render(:partial => @review)? To renderowanie częściowe - co oznacza, że ​​może renderować dowolny rodzaj kodu - html, css ... lub nawet więcej javascript!

Więc co się stanie, jeśli nasz podrzędny fragment zawiera prosty kod HTML, taki jak ten?

<a href="/mycontroller/myaction">Action!</a> 

Pamiętasz, że Twój javascript przyjmował jako parametr ciąg znaków w podwójnych cudzysłowach? Jeśli po prostu zastąpimy go <%= ... %>kodem tej części, to mamy problem - zaraz po tym href=jest podwójny cudzysłów! JavaScript nie będzie prawidłowy:

// Without escaping, you get a broken javascript string at href
$("#reviews").append("<a href="/mycontroller/myaction">Action!</a>");

Aby tak się nie stało, chcesz uniknąć tych znaków specjalnych, aby łańcuch nie został przecięty - potrzebujesz czegoś, co zamiast tego wygeneruje:

<a href=\"/mycontroller/myaction\">Action!</a>

To co escape_javascriptrobi. Zapewnia, że ​​zwracany ciąg nie „zepsuje” javascript. Jeśli go użyjesz, uzyskasz pożądane wyjście:

$("#reviews").append("<a href=\"/mycontroller/myaction\">Action!</a>")

Pozdrowienia!

kikito
źródło
4
rzeczywiście, bardzo miłe wyjaśnienie, dziękuję. Wydaje mi się, że użycie „escape_javascript” jest trochę mylące, ponieważ tak naprawdę nie używamy go do ucieczki przed prawdziwym JavaScriptem. Możesz umieścić tag skryptu w części i wykonać dowolny javascript, który chcesz.
Steve
13
@Steve zgodził się, lepsza byłaby nazwa escape_for_javascript, ponieważ często uciekasz przed innym kodem (na przykład html), aby nie zepsuć javascript.
kikito
14
escape_javascript()ma teraz krótszą metodę: simpley j()( przynajmniej w Rails 4).
mjnissim
@kikito, czy użycie tego efektu sprawia, że ​​próbuję renderować część HTML składającą się ze zdalnego formularza? Próbuję dowiedzieć się, dlaczego renderowanie częściowej za pomocą javascript, który zawiera inny formularz, który zostanie przesłany za pośrednictwem javascript, powoduje, że drugie żądanie jest html zamiast js. Jeśli masz czas, czy mógłbyś spojrzeć na pytanie, które już opublikowałem na ten temat? Dzięki! Moje pytanie
Jake Smith
1
@JakeSmith odpowiedział w twoim pytaniu
kikito
8

użytkownicy mogą wysyłać złośliwy kod (złośliwi użytkownicy), który pozostawiony bez zmiany znaczenia zostanie potencjalnie wykonany, umożliwiając użytkownikom kontrolowanie aplikacji.

Spróbuj tego:

<% variable = '"); alert("hi there' %>
$("#reviews").append("<%= variable %>");

niezbyt zaznajomiony ze składnią railsów, ale jeśli nie uciekniesz variable, pojawi się okienko ostrzegawcze i nie sądzę, aby było to zamierzone zachowanie.

barkmadley
źródło
8

Jeśli spojrzysz na źródło tutaj , będzie znacznie jaśniejsze.

Ta funkcja realizuje następujące dwie rzeczy:

  1. Zastępuje znaki w ciągu wejściowym znakami zdefiniowanymi w JS_ESCAPE_MAP

    Ma to na celu upewnienie się, że kod javascript jest poprawnie serializowany bez zakłócania zewnętrznego ciągu, w którym jest osadzany. Na przykład, jeśli masz ciąg javascript w podwójnych cudzysłowach, wszystkie cudzysłowy w literałach ciągów muszą być w apostrofach, aby uniknąć zerwania kodu.

  2. Funkcja sprawdza również, czy otrzymany ciąg jest bezpieczny w formacie HTML. Jeśli tak nie jest, wykonuje niezbędne zmiany znaczenia, aby upewnić się, że ciąg znaków staje się bezpieczny w formacie HTML i zwraca wynik.

Kiedy używasz escape_javascript, zwykle jest on osadzany dynamicznie w innym łańcuchu lub istniejącym html. Musisz się upewnić, że wykonanie tej czynności nie spowoduje, że cała strona nie zostanie wyrenderowana.

Niektóre aspekty tej odpowiedzi zostały wskazane w innych odpowiedziach, ale chciałem zebrać wszystkie elementy razem, w tym różnicę między ucieczką javascript a ucieczką HTML. Ponadto niektóre odpowiedzi wspominają, że ta funkcja pomaga uniknąć wstrzyknięcia skryptu. Nie sądzę, że jest to celem tej funkcji. Na przykład w Twojej recenzji, jeśli Twoja recenzja zawiera alert („Cześć”), samo dołączenie go do węzła nie spowoduje uruchomienia wyskakującego okienka. Nie osadzasz go w funkcji, która jest wyzwalana podczas ładowania strony lub przez inne zdarzenie. Samo ostrzeżenie („cześć”) jako część kodu HTML nie oznacza, że ​​będzie on wykonywany jako javascript.

To powiedziawszy, nie zaprzeczam, że wstrzyknięcie skryptu nie jest możliwe. Ale aby tego uniknąć, musisz podjąć kroki podczas pobierania danych użytkownika i przechowywania ich w bazie danych. Funkcja i przykład, które podajesz, są związane z renderowaniem informacji, które zostały już zapisane.

Mam nadzieję, że to pomoże i odpowie na Twoje pytanie.

Tabrez
źródło