Analiza kodu Drupala pod względem bezpieczeństwa
W poprzednich częściach skupiliśmy się na konfiguracji Drupala oraz na przeglądzie modułów i bibliotek. W trzeciej części z serii dotyczącej przeprowadzenia audytu bezpieczeństwa skupimy się na przeglądzie customowych modułów oraz skórek, dokonamy audytu repozytorium projektu, zidentyfikujemy oraz przeanalizujemy elementy, na które warto zwrócić uwagę podczas procesu audytowania.
Przegląd customowych modułów oraz skórek
W customowych modułach oraz skórkach istnieje największe prawdopodobieństwo zaistnienia wektorów ataku. Jest tam kod, który w przeciwieństwie do kodu modułów i skórek contribowych, nie jest szeroko stosowany. Dlatego też nie jest tak dobrze przetestowany pod kątem bezpieczeństwa. W tym artykule omówię podstawową listę kontrolną stosowaną przy audycie customowego kodu. Lista ta może być użyta jako podstawa audytu customowych modułów i skórek w Drupalu.
Routing
Zacznijmy od analizy parametrów otrzymywanych od użytkownika. Sprawdźmy, jaki jest ich typ i jak są filtrowane. Drupal pozwala na używanie parametrów w routingach. Są to dynamiczne wartości, których niepoprawne przetwarzanie może tworzyć wektory ataku. Jeśli na podstawie parametru tworzona jest kwerenda, a nie jest on filtrowany, może to powodować na przykład wektor do ataku SQL Injection.
Następnie należy przyjrzeć się konfiguracji dostępu do routingu, a konkretnie uprawnieniom, jakie musi posiadać użytkownik, aby otrzymać dostęp. Podczas deklaracji routingu należy zdefiniować wymagania, które użytkownik musi spełnić, aby otrzymać dostęp do routingu. Dla każdego routingu określonego w naszym customowym kodzie musimy przeanalizować wymagane uprawnienia i zastanowić się, czy ich poziom jest odpowiedni. Określenie zbyt niskiego lub niepoprawnego poziomu wymaganych uprawnień spowoduje, że użytkownicy będą posiadali dostęp do stron, do których nie powinni go mieć. Mogą to być zarówno strony listujące artykuły na Twojej stronie, jak również strony listujące wszystkich użytkowników wraz ze wszystkimi danymi, które są przypisane do danego konta. Z tego powodu tak ważny jest audyt uprawnień.
Formularze
W pierwszej kolejności przeanalizujemy poprawność typów elementów i sprawdzimy, czy użyto poprawnego typu dla danego pola. W trakcie analizy typów pól użytych w formularzu, możemy natrafić na pole, którego nazwa i opis sugeruje, że należy je wypełnić danymi konkretnego typu. Mimo to definicja pola może pozwalać na wypełnienie pola danymi innego typu. Należy sprawdzić, czy definicja typu elementów odpowiada ich przeznaczeniu.
Kolejnym krokiem będzie analiza użytych metod walidacji wartości pól w formularzu. Drupal pozwala na definiowanie customowych metod walidujących poprawność wprowadzonych danych. Należy przetestować poprawność customowych metod walidujących i upewnić się, że jedynie poprawne dane przechodzą walidację.
Ostatnią rzeczą będzie weryfikacja obecności i poprawności użycia Form API, dostarczonego przez Drupala. Należy przeanalizować sposób wykorzystania Form API, najlepiej posługując się dokumentacją i upewnić się, że formularze tworzone są zgodnie z zaleceniami.
Dokumentacja określa:
- przypadki, w których użycie danego typu pola jest prawidłowe,
- jak tworzyć metody walidacyjne,
- w jaki sposób używać hook_form_alter,
- w jaki sposób tworzyć formularze, aby były intuicyjne dla użytkownika.
Zapytania SQL
Na początku zajmijmy się weryfikacją obecności i poprawności użycia Database API, dostarczonego przez Drupala. Posiada ono metody zabezpieczeń przed atakami na bazę danych. Poprawne użycie API w znacznej mierze zabezpiecza przed atakami. Należy w szczególności zwrócić uwagę, czy w zapytaniach SQL używane są metody filtrowania danych wejściowych. Drupal, w przypadku potrzeby użycia danych wejściowych, pochodzących np. ze zmiennej, której wartość użytkownik określił w formularzu, rekomenduje użycie placeholderów. Oto przykład:
$foo = $this->getFormData(); $query = \Database::getConnection()->query(‘SELECT foo FROM {bar} b WHERE b.name = ‘ . $foo[‘name’]);
W kodzie powyżej możemy zauważyć, że wartość ‘name’ z tablicy $foo zostaje przypisana do kwerendy bez filtrowania. W takich przypadkach rekomenduję użycie placeholderów.
$foo = $this->getFormData(); $query = \Database::getConnection()->query(‘SELECT foo FROM {bar} b WHERE b.name = :name’, [‘:name’ => $foo[‘name’]]);
Tworząc kwerendy w ten sposób, zmienną $foo[‘name’] poddajemy filtrowaniu, które zabezpieczy zapytanie przed atakami typu SQL Injection.
Mechanizmy filtrujące
Chodzi tutaj o weryfikację obecności i poprawności filtrowania danych, otrzymywanych od użytkownika. Potrzebujemy sprawdzić, czy do renderowania zmiennych w szablonach używany jest jedynie TWIG, który domyślnie filtruje zawartość zmiennych i upewnia się, że są bezpieczne. W przypadku pracowania ze zmiennymi, używanymi następnie w ciągach znaków, które można przetłumaczyć, należy upewnić się, że te zmienne są podstawiane za odpowiednie placeholdery. Plain text pochodzący od użytkownika powinien być filtrowany za pomocą metody Html::escape(), jeśli użytkownik nie powinien mieć możliwości podawania w tekście tagów HTML oraz funkcji Xss::filterAdmin(), jeśli powinien mieć taką możliwość. Jeżeli użytkownik podaje linki, również powinny zostać przefiltrowane. Służą do tego funkcje UrlHelper::stripDangerousProtocols() oraz UrlHelper::filterBadProtocol(). Mechanizmy filtrujące mają zastosowanie także po stronie klienta. Do oczyszczania tekstu w JavaScript powinniśmy użyć funkcji Drupal.checkPlain().
Dane wrażliwe
Należy sprawdzić, czy kod nie zawiera danych uwierzytelniających lub kluczy API. W niektórych przypadkach możemy spotkać się z danymi uwierzytelniającymi pozostawionymi w kodzie customowych modułów. Ich identyfikacja nie wymaga dużych nakładów pracy.
Przegląd repozytorium
Warto przyjrzeć się repozytorium w poszukiwaniu poufnych informacji, które mogą być przechowywane w plikach. Pierwszą czynnością będzie analiza pliku settings.php i upewnienie się, że nie istnieją w nim dane uwierzytelniające dające dostęp do bazy danych lub innych składowych serwisu. Następnie przejrzyjmy pliki ze zmiennymi środowiskowymi i upewnijmy się, że nie istnieją w nich dane uwierzytelniające. Dane uwierzytelniające wymagane przez środowisko nie powinny być częścią repozytorium.
Kolejną czynnością będzie sprawdzenie, czy w “głębokim ukryciu” nie istnieją poufne pliki, zawierające na przykład klucze prywatne SSL czy kopie lub zrzuty baz danych. Czasem przez pomyłkę do repozytorium dostają się pliki, które w żadnym wypadku nie powinny się tam znaleźć. Niektóre z nich to na przykład właśnie zrzuty bazy czy klucze prywatne. Ich identyfikacja i usunięcie jest zalecane.
Analiza kodu Drupala - podsumowanie
W trzeciej części cyklu dotyczącego przeprowadzenia audytu bezpieczeństwa Drupala poznaliśmy sposoby na sprawdzenie kodu customowych modułów oraz skórek, zaudytowaliśmy repozytorium projektu, aby upewnić się, że żadne wrażliwe dane nie są ogólnodostępne, a także przeanalizowaliśmy elementy, na które warto zwrócić uwagę podczas procesu audytowania.
Zastosowanie porad, które umieściłem w tym artykule, sprawi, że Twoja aplikacja będzie bezpieczniejsza. Audyt kodu to kluczowy element prowadzący do lepszego zabezpieczenia strony. Wymaga więcej czasu oraz wiedzy niż aktualizacja, którą zajęliśmy się w pierwszej części oraz poprawna konfiguracja Drupala, którą zajęliśmy się w drugiej części cyklu, lecz korzyści płynące z jego przeprowadzenia są znacznie cenniejsze niż poświęcony czas.
W kolejnej części tej serii artykułów zapoznamy się z zewnętrznymi narzędziami pozwalającymi na zautomatyzowanie procesu audytowania. Jest to następny z kroków, które wykonuje się podczas kompleksowego audytu bezpieczeństwa. Potrzebujesz pomocy w tego typu zadaniu? Nasz zespół wsparcia Drupala ma duże doświadczenie w przeprowadzaniu audytów.