Dziś kontynuować będziemy temat formularzy w HTML5. W poprzednim poście, opisałem walidację dostarczaną bezpośrednio przez kontrolki formularzy. Jak jednak widzieliśmy, pozwalają one tylko na przeprowadzenie podstawowych walidacji, takich jak sprawdzenie czy wprowadzono dane w pola wymagane, czy w pole typu ‘email’ wprowadzono prawidłowy email. Ponadto omówiliśmy sposób na definiowanie walidacji wprowadzanych danych za pomocą wyrażeń regularnych (atrybut ‘pattern’). Bardziej zaawansowana walidacja formularzy HTML5 możliwa jest za pomocą języka JavaScript. W związku z tym, w dzisiejszym wpisie zajmę się opisaniem tworzenia wyrażeń regularnych w JavaScript i ich użyciem do walidacji wprowadzanych danych; umówię także sposoby na walidację typu wprowadzonych danych za pomocą wbudowanych w ten język funkcji; na koniec przyjrzę się możliwościom zabezpieczenia się przed wstrzykiwaniem kodu poprzez formularz. Jak zwykle zapraszam do lektury!

Walidacja za pomocą wyrażeń regularnych

Tak jak napisałem, walidacja formularzy HTML5 za pomocą JavaScript daje dużo większe możliwości, niż ta wbudowana w kontrolki formularzy w HTML5. Na początek chciałbym rozważyć prosty przykład jak można taką walidację zaimplementować. Spójrzmy więc na taki kod HTML:

<form action="#">
    <input id="textBox" type="text" placeholder="Napisz TEST"/>
    <input id="button" type="submit" value="Zatwierdź" />
</form>

Najprostsze sprawdzenie

Przyjmijmy teraz, że chcielibyśmy w momencie kliknięcia przycisku ‘Zatwierdź’, dokonać walidacji tekstu jaki został wprowadzony do kontrolki o identyfikatorze ‘textBox’. Moglibyśmy, zrobić to w następujący sposób:

var button = document.getElementById('button');

button.addEventListener('click', function(e) {
    var textBox = document.getElementById('textBox');

    if (textBox.value !== 'TEST') {
        e.preventDefault();
        textBox.setCustomValidity('Powinieneś napisać TEST!');
    } else {
        textBox.setCustomValidity('');
    }
});

Jak widać powyżej, sprawa wygląda dość prosto. Po pobraniu referencji do przycisku, rejestrujemy funkcję obsługującą jego kliknięcie - ponieważ walidować chcemy w momencie kliknięcia (można też na przykład walidować kontrolkę ‘textBox’ w momencie jej opuszczenia, wówczas moglibyśmy zarejestrować na jej rzecz, obsługę zdarzenia ‘blur’). We ciele funkcji, pobieramy referencję do kontrolki o identyfikatorze ‘textBox’, a następnie sprawdzamy, czy wprowadzona została oczekiwana wartość.

I teraz to co jest interesujące: najpierw wywołuję metodę ‘preventDefault’, w celu zablokowania standardowego zachowania zdarzenia (w tym przypadku, kliknięcie standardowo wysyła formularz do serwera). Następnie, za pomocą metody ‘setCustomValidity’ ustawiam tekst powiadomienia o błędzie - komunikat ten wyświetli się w ten sam sposób co standardowe komunikaty, na przykład gdy wprowadzimy tekst nie będący adresem email w pole typu ‘email’ (oczywiście, nie musimy stosować tego sposobu - zamiast niego możemy stworzyć własne okienko wyskakujące lub obsłużyć błąd w dowolny inny sposób).

Spójrzmy też na linię dziesiątą - w przypadku gdy wprowadzana wartość jest prawidłowa, czyszczę tekst błędu. To dlatego, że raz ustawiony tekst błędu zapamiętuje się, więc jeśli tego bym nie zrobił, komunikat nadal by się pojawiał nawet jeśli błąd zostanie już poprawiony. Polecam wypróbowanie tego przykładu na jsfiddle.net (kliknij link aby przejść do przykładu).

Walidacja formularzy HTML5 za pomocą wyrażeń regularnych

Skoro wiemy już mniej więcej w jaki sposób realizować można walidację formularzy w JavaScript, skupmy się na czymś bardziej konkretnym, a więc na walidacji za pomocą wyrażeń regularnych. W języku JavaScript, mamy dwa sposoby na utworzenie wyrażenia regularnego. Pierwszy to utworzenie obiektu ‘RegExp’ i podanie wyrażenia w regularnego (w postaci “stringa”) oraz ewentualnie modyfikatora jako parametry wywołania konstruktora (na temat tego obiektu pisałem już co nieco we wpisie o obiektach i metodach w JavaScript):

var regexp = new RegExp("^\d+$");

W przypadku tworzenia wyrażeń regularnych w ten sposób, pamiętać należy o “escape string”, czyli w powyższym przypadku zamiast “” należało użyć “\”. W związku z tym moim zdaniem lepszą metodą na tworzenie obiektu wyrażenia regularnego jest sposób uproszczony (poniżej to samo wyrażenie):

var regexp = /^\d+$/;

Tym razem do zmiennej ‘regexp’ przypisujemy nasze wyrażenie bezpośrednio - należy zauważyć, że użyta została konstrukcja: “/regex/”, pamiętajmy więc o znakach “/”, w które należy wziąć nasze wyrażenie.

Myślę, że w ramach tego wpisu nie będę zagłębiać się w “meandry” tworzenia wyrażeń regularnych, ponieważ zajęłoby to sporo miejsca. Większość programistów zapewne zna temat, jednak jeśli ktoś chciałby mimo wszystko dowiedzieć się więcej, polecam artykuł o wyrażeniach regularnych na stronach Mozilli.

Przejdźmy zatem dalej i zacznijmy korzystać z wyrażeń regularnych do walidacji formularzy. Generalnie, w JavaScript mamy kilka sposobów wykorzystania wyrażeń regularnych. Spójrzmy na poniższe metody klasy ‘String’:

  • replace - jako parametr przyjmuje wyrażenie regularne, i zwraca część tekstu na rzecz którego została wywołana, która pasuje do tego wyrażenia
  • search - podobna do poprzedniej, z tym że zwraca index miejsca, w którym zaczyna się tekst pasujący do wyrażenia regularnego (w przeciwnym razie zwraca -1)
  • split - służy do dzielenia tekstów; wyrażenie regularne traktowane jest w tym przypadku jak separator (tzn. część tekstu pasująca do wyrażenia jest separatorem)
  • match - zwraca tablicę zawierającą wszystkie kawałki tekstu, który pasuje do podanego wyrażenia; w przeciwnym razie zwraca ‘null’

Ostateczny przykład

Jak widać, najbardziej przydatnymi metodami w kontekście walidacji będą metody ‘search’, a przede wszystkim ‘match’. Spójrzmy więc na poniższą modyfikację poprzedniego przykładu, z użyciem wyrażenia regularnego:

var button = document.getElementById('button');

button.addEventListener('click', function(e) {
    var textBox = document.getElementById('textBox');

    if (textBox.value.match(/^TEST$/) == null) {
        e.preventDefault();
        textBox.setCustomValidity('Powinieneś napisać TEST!');
    } else {
        textBox.setCustomValidity('');
    }
});

W linii szóstej widać sposób na wykorzystanie metody ‘match’, jako parametr po prostu podajemy wyrażenie regularne i sprawdzamy czy zwracana jest wartość ‘null’ (tak jak pisałem, jeśli zwracany jest ‘null’ oznacza to, że w tekście brakuje jakiegokolwiek dopasowania). Powyższy przykład poklikać można tutaj.

Walidacja typu wprowadzonych danych

Do sprawdzania typu danych w JavaScript służy instrukcja ‘typeof’. Jednak specyfika kontrolek formularzy powoduje, że wszystkie dane wprowadzane do nich są typu ‘string’. W związku z tym, w celu walidacji typu, należy zastosować odpowiednie wyrażenia regularne, sprawdzające czy wprowadzany ciąg znaków przedstawia tekst, liczbę, datę itp. Poniżej propozycje wyrażeń regularnych, które mogłyby być użyte do sprawdzenia typu wprowadzonej wartości:

var noNumbersExpression = /^[a-zA-Z]+$/;
var numbersOnlyExpression = /^[0-9]+$/;
var moneyExpression = /^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(\.[0-9]{2})?$/;

Powyżej mamy przykłady wyrażeń, które można użyć do sprawdzenia czy wartość nie zawiera cyfr, czy zawiera cyfry oraz czy jest to wartość typu ‘currency’. Oczywiście to tylko propozycje i tworząc walidację typu mamy pełną dowolność w konfigurowaniu wyrażeń regularnych, których w danym momencie potrzebujemy.

Zabezpieczanie formularzy przed wstrzykiwaniem kodu

Wstrzykiwanie kodu JavaScript (ang. JavaScipt code injection), jest to taki atak, w którym atakujący wprowadza do kontrolek formularza kod JavaScript, licząc że późniejsza próba wyświetlenia danych zapisanych z formularza spowoduje wykonanie tak wstrzykniętego skryptu. Przykład: atakujący wprowadza do formularza tekst “<script>alert(‘HA!’);</script>”. Na następnej stronie wyświetlanej po zatwierdzeniu formularza wyświetlane jest podsumowanie, zawierające wszystkie wprowadzone dane. Gdy strona taka się wyświetla, jako jedna z informacji z formularza zawiera tekst podany przez atakującego, a że jest to kod JavaScript, oczom użytkownika ukazuje się komunikat “HA!”.

Oczywiście najlepszym sposobem jest “escape’owanie” danych z formularza po stronie serwera, przed ich ewentualną obróbką i zapisaniem do bazy danych. Można jednak również, dodać po stronie klienta filtry/walidacje, nie pozwalające użytkownikowi na wprowadzanie nieprawidłowych/niebezpiecznych danych. Najprostszą walidacją zabezpieczającą przed takim atakiem mogłoby być sprawdzenie czy wprowadzony tekst zawiera znaki “<” oraz “>”, to w większości przypadków załatwia problem ze wstrzyknięciem tagów HTML do formularza.

Dobry zbiór reguł związanych z tym problemem znaleźć można na stronie html5sec.org - zachęcam do zapoznania się z tym materiałem, dobrze pokazuje on jakie są możliwości wykonania takiego ataku ;)

Walidacja formularzy HTML5 - podsumowanie

Myślę, że walidacja formularzy HTML5 po stronie klienta pozwala przede wszystkim na tworzenie dobrego “user experience”. Dzięki temu można ułatwić użytkownikowi wypełnianie formularza informując go w jaki sposób ma wypełniać poszczególne pola i ewentualnie podając wskazówki co robi on źle. Jednakże, walidacja taka nie jest w stanie w 100% zabezpieczyć nas przed atakami hakerskimi, dlatego powinna ona zawsze iść w parze z walidacją po stronie serwera!!

To tyle na dziś, w kolejnym wpisie zajmę się kolejnym zagadnieniem dotyczącym dostępu i zabezpieczania danych jakim jest “konsumowanie danych”. Zapraszam więc jak zwykle!