Spis treści
- Wprowadzenie
- Implementacja
- Działająca aplikacja na Github Pages
- Kod aplikacji na Github
- Podsumowanie
Wprowadzenie
Content dynamicznie generowany, takie jak wiadomości użytkownika, niosą ze sobą ryzyko niebezpieczeństw takich jak wykonanie kodu JS. Do tej pory by się przed tym zabezpieczyć używaliśmy bibliotek, takich jak sanitize-html.
Jednak od Chrome 105 dostępne dla wszystkich jest Sanitizer API, do tej pory w Chrome i FF dostępne tylko po włączeniu odpowiedniej flagi.
Zapraszam do przeczytania artykułu o tym, jak używać nowego API 🙂
Implementacja
Na dzień pisania posta (19.12.2022) nadal większość funkcjonalności Sanitizera jest ukryta w przeglądarkach za flagami. Chrome w wersji 105 jednak dostarczył metodę Element.setHTML() – następcę innerHTML, która to metoda jako parametr przyjmuje obiekt sanitizera, a jeśli ten nie zostanie podany to użyje domyślnego obiektu. Domyślna implementacja usunie tylko tagi script, zostawiając pozostałe.
Skorzystamy więc z tego sposobu w naszym przykładzie. W prostej aplikacji będzie znajdować się pole do wpisania tekstu zawierającego kod HTML a także lista checkboxów odpowiadających tagom HTML na które chcemy zezwolić. Jeśli żaden z nich nie zostanie zaznaczony, będziemy mieć w pełni domyślne zachowanie sanitizera.
Element textarea zawierać będzie predefiniowany tekst z tagami HTML:
<label for="text"> Text to insert </label> <textarea id="text"> Test <b>bold</b> <i>italic</i> <s>stroke</s> <img src="dummyimage.png"> <script>alert('Test')</script> <u>underscore</u> </textarea>
A lista checkboxów umieszczona będzie wewnątrz tagu fieldset:
<fieldset> <legend> Which tags do you want to allow? </legend> <label> Allow all (will remove script tag only) <input type="checkbox" name="allowAll" id="allowAll"> </label> <label> img <input type="checkbox" name="allowed" value="img"> </label> </fieldset>
Tagi które zostały wybrane pobierzemy za pomocą metodą querySelectorAll:
const checkedSelector = 'input[name="allowed"]:checked'; const checkedCheckboxes = [ ...document.querySelectorAll(checkedSelector), ].map(el => el.value);
Ponieważ metoda ta zwraca obiekt typu NodeList który nie jest do końca tablicą, musimy ręcznie przetworzyć obiekt na tablicę by móc ją przemapować na wartości wybranych tagów.
Metodę tą opisałem w osobnym wpisie:
Potrzebujemy też sprawdzić, czy przypadkiem nie została kliknięcia opcja pozwalająca na wybór wszystkich elementów:
const allowAll = document.querySelector('#allowAll'); const allowElements = allowAll.checked ? undefined : checkedCheckboxes;
Następnie listę tą przekażemy przy tworzeniu nowego obiektu Sanitizer:
const sanitizer = new Sanitizer({ allowElements, });
allowElements to opcja pozwalająca na ustawienie tagów, które chcemy zostawić w przekazanym tekście:
const sanitizer = new Sanitizer({ allowElements, });
Następnie korzystamy ze wspomnianego już API setHTML:
const text = document.querySelector('#text'); const dom = document.querySelector('#dom'); dom.setHTML( text.value, { sanitizer, } );
Tym sposobem po wpisaniu tekstu i kliknięciu przycisku zobaczymy sformatowany tekst przefiltrowany wybranymi przez nas opcjami.
Działająca aplikacja na Github Pages
Link do działającej aplikacji:
https://radek-anuszewski.github.io/sanitizer-example/
Kod aplikacji na Github
Pełen kod aplikacji:
https://github.com/radek-anuszewski/sanitizer-example
Podsumowanie
Dzięki rozwijającemu się API Sanitizer, zamiast polegać na bibliotekach pisanych przez pojedynczych programistów, w temat bezpieczeństwa contentu na stronach możemy liczyć na ogromne organizacje stojące za samymi przeglądarkami internetowymi. Z czasem wsparcie będzie rosło i coś co do tej pory wymagało customowej obsługi stanie się darmowym z punktu widzenia programisty standardem.