Co to znaczy, gdy mówią, że React jest chroniony przed XSS?

110

Przeczytałem to w samouczku React. Co to znaczy?

Reakcja jest bezpieczna. Nie generujemy ciągów HTML, więc ochrona XSS jest domyślna.

Jak działają ataki XSS, jeśli React jest bezpieczny? W jaki sposób osiąga się to bezpieczeństwo?

user1210233
źródło

Odpowiedzi:

181

Od tego czasu ReactJS jest całkiem bezpieczny z założenia

  1. Zmienne łańcuchowe w widokach są automatycznie zmieniane
  2. W JSX przekazujesz funkcję jako procedurę obsługi zdarzeń, a nie ciąg znaków, który może zawierać złośliwy kod

więc typowy atak taki jak ten nie zadziała

const username = "<img onerror='alert(\"Hacked!\")' src='invalid-image' />";

class UserProfilePage extends React.Component {
  render() {
    return (
      <h1> Hello {username}!</h1>
    );
  }
}

ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

ale ...

„Ostrzeżenie”

Nadal istnieje kilka wektorów ataku XSS, z którymi musisz sobie poradzić w React!

1. XSS przez dangerouslySetInnerHTML

Kiedy używasz dangerouslySetInnerHTML, musisz upewnić się, że zawartość nie zawiera żadnego javascript. React nie może tutaj nic dla Ciebie zrobić.

const aboutUserText = "<img onerror='alert(\"Hacked!\");' src='invalid-image' />";

class AboutUserComponent extends React.Component {
  render() {
    return (
      <div dangerouslySetInnerHTML={{"__html": aboutUserText}} />
    );
  }
}

ReactDOM.render(<AboutUserComponent />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

2. XSS poprzez atrybut a.href

Przykład 1: użycie javascript: code

Kliknij „Uruchom fragment kodu” -> „Moja witryna”, aby zobaczyć wynik

const userWebsite = "javascript:alert('Hacked!');";

class UserProfilePage extends React.Component {
  render() {
    return (
      <a href={userWebsite}>My Website</a>
    )
  }
}

ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Przykład 2: Korzystanie z danych zakodowanych w formacie base64:

Kliknij „Uruchom fragment kodu” -> „Moja witryna”, aby zobaczyć wynik

const userWebsite = "data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGFja2VkISIpOzwvc2NyaXB0Pg==";

class UserProfilePage extends React.Component {
  render() {
    const url = userWebsite.replace(/^(javascript\:)/, "");
    return (
      <a href={url}>My Website</a>
    )
  }
}

ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

3. XSS przez rekwizyty kontrolowane przez atakującego

const customPropsControledByAttacker = {
  dangerouslySetInnerHTML: {
    "__html": "<img onerror='alert(\"Hacked!\");' src='invalid-image' />"
  }
};

class Divider extends React.Component {
  render() {
    return (
      <div {...customPropsControledByAttacker} />
    );
  }
}

ReactDOM.render(<Divider />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Oto więcej zasobów

Konsultacje CyberPanda
źródło
13
Ta odpowiedź jest niesamowita! Z fragmentami kodu i referencjami na końcu ...! Dziękuję Ci!
Ioanna,
Czy React zajął się którymś z powyższych przykładów od czasu napisania tej odpowiedzi? Pytam, ponieważ przeczytałem na następującym slajdzie: slideshare.net/kseniadmitrieva/ ... slajd # 20, że rekwizyty kontrolowane przez użytkownika zostały naprawione w React 0.14 w listopadzie 15 '
omer
@omer no, i zdecydowałem się nie zajmować się tymi wektorami ataku na poziomie React. Oto kilka dobrych komentarzy wyjaśniających, dlaczego nie radzą sobie na poziomie React github.com/facebook/react/issues/3473 ( github.com/facebook/react/issues/3473#issuecomment-91349525 , github.com/facebook/react / Issues / 3473 # issuecomment-90594748 )
Consulting,
1
@ W przypadku problemu, o którym mówisz, był to błąd bezpieczeństwa i został on naprawiony, ale punkt 3, który wymieniłem, nie jest powiązany z tym, nadal możesz sprawdzić ten trzeci punkt, wykonując mój kod w dowolnej wersji React.
CyberPanda Consulting
60

React automatycznie wymyka zmienne za Ciebie ... Zapobiega iniekcji XSS przez ciąg znaków HTML ze złośliwym Javascriptem. Oczywiście, dane wejściowe są oczyszczane razem z tym.

Na przykład powiedzmy, że masz ten ciąg

var htmlString = '<img src="javascript:alert('XSS!')" />';

jeśli spróbujesz wyrenderować ten ciąg w reakcji

render() {
    return (
        <div>{htmlString}</div>
    );
}

dosłownie zobaczysz na stronie cały ciąg, łącznie ze <span>znacznikiem elementu. aka w przeglądarce, którą zobaczysz<img src="javascript:alert('XSS!')" />

jeśli wyświetlisz źródłowy html, zobaczysz

<span>"<img src="javascript:alert('XSS!')" />"</span>

Oto więcej szczegółów na temat tego, czym jest atak XSS

React w zasadzie sprawia, że ​​nie możesz wstawiać znaczników, chyba że sam utworzysz elementy w funkcji renderowania ... to powiedziawszy, że mają funkcję, która umożliwia takie renderowanie, nazywa się dangerouslySetInnerHTML... tutaj jest więcej szczegółów na ten temat


Edytować:

Kilka rzeczy do zapamiętania, są sposoby na obejście tego, co ucieka React. Jeszcze jednym popularnym sposobem jest definiowanie przez użytkowników właściwości Twojego komponentu. Nie rozszerzaj żadnych danych wprowadzanych przez użytkownika jako rekwizytów!

John Ruddell
źródło
13
Ucieka wszystkim? Naprawdę? Reakcja NIE jest domyślnie bezpieczna, jest wiele rzeczy, które musisz zrobić ręcznie i zaatakować wektory, które musisz zrozumieć. Wszystko, co robi React, to zamiana html na łańcuch, gdy próbujesz wstawić go za pomocą {html}. Ale jest milion innych sposobów na zezwolenie na XSS, przed którymi React NIE chroni. <a href="{...}" />, <img src = {...} />, <iframe src = "{...} /> i mnóstwo innych właściwości, które umożliwiają wstrzykiwanie wykonywalnego javascript. A potem są zastrzyki skryptów CSS poprzez styl = {...} prop. Odpowiedź poniżej autorstwa @Marty Aghajanyan faktycznie przedstawia możliwe zagrożenia.
andree,
@andree thanks za wskazanie mojej literówki. To jest post sprzed 3 lat. Oczywiście istnieją sposoby na obejście tego, przed czym ucieka React i każdy programista powinien być tym zmęczony.
John Ruddell
Dziękujemy za zmianę odpowiedzi @John Ruddell. Bez obrazy, ale twoja odpowiedź sprawiła, że ​​React wyglądał na bezpieczniejszego niż jest w rzeczywistości, a ponieważ twoja odpowiedź jest jedną z pierwszych, które pojawiają się w tym temacie, chciałem tylko na to zwrócić uwagę. Niestety jest to powszechny motyw, który widzę w ogólnych zabezpieczeniach frontendu (nie tylko React) - rzeczy wyglądają na bezpieczne lub łatwe do zabezpieczenia na powierzchni, ale kiedy się zagłębiasz, okazuje się, że są duże dziury. Podstawowe pytania bezpieczeństwa powinny mieć łatwe do znalezienia odpowiedzi, które są gdzieś podsumowane, niestety ostatnio nie jest to moje doświadczenie.
andree
No cóż… z czasem dokumentacja wychodzi, gdy testowane są zabezpieczenia. Odpowiedzi, które kiedyś były dla nas pomocne, nie są już tak pomocne. Najtrudniejsze jest to, aby wszystkie odpowiedzi były na bieżąco ze zmieniającą się technologią
John Ruddell,