Asynchroniczne interfejsy
WWW
Walidacja danych
mgr inż. Rafał Grycuk
mgr inż. Patryk Najgebauer
Strona służbowa: http://iisi.pcz.pl/~rgrycuk/ Kontakt: rafal.grycuk@iisi.pcz.pl
Agenda
1. Co to jest Walidacja danych ?, 2. Wyrażenia regularne,
3. Wyrażenia regularne w JavaScript. 4. Walidacja danych po stronie serwera.
Walidacja danych polega na sprawdzaniu poprawności danych
wejściowych.
Walidujemy dane w celu wyeliminowania:
• błędów użytkownika np. źle podany pesel, kod pocztowy;
• danych niezgodnych z regulaminem np. wulgaryzmy, odnośniki do konkurencyjnych stron;
• treści szkodliwych dla strony np. skrypty JavaScript, zapytania SQL;
Wszystkie informacje trafiające od użytkownika na serwer są
przesyłane w formie tekstowej, więc należy sprawdzić co dokładnie przyszło od klienta.
Walidując parametry:
• typów prostych (int, float) możemy je prasować dzięki gotowym
metodom.
• typów niestandardowych (adres,email) musimy skorzystać z wyrażeń
regularnych. Istnieją również dane jak numery NIP, PESEL posiadające wartości kontrolne. Natomiast w tekstach występują też potrzeby usuwania słów niepożądanych metodą słownikową.
Są to wzorce opisujące łańcuchy znaków. Możemy za ich pomocą wyszukać,
pociąć lub porównać dane wejściowe pasujące do naszego wzorca.
Przykładowo poniższe wyrażenie regularne będzie pasować do kodów
pocztowych np. 42-200
^([0-9]{2}-[0-9]{3})$
^(
[0-9]{2}-[0-9]{3}
)$ - łańcuch na wejściu musi być
zgodny od początku do końca z wyrażeniem regularnym.
^([0-9]{2}-[0-9]{3})$ - odnosi się do dwóch początkowych cyfr kodupocztowego.
^([0-9]{2}-[0-9]{3})$ - określa zbiór znaków od 0 do 9.
^([0-9]{2}-[0-9]{3})$ - muszą wystąpić 2 znaki zgodne ze zbiorem. ^([0-9]{2}-[0-9]{3})$ - znak myślnika w kodzie pocztowym.
…|… – alternatywa.
(…) – grupowanie wyrażań regularnych
(?: …) – grupowanie bez zapamiętywania wyniku
\n - referencja do zapamiętanej grupy (back references) liczonej od
początku. Łańcuch pod referencją musi być zgodny z łańcuchem z grupy na którą jest wskazanie.
(12|21)abc – wyrażenie zgodne z łańcuchami 12abc lub 21abc
(12|21)abc\1 – wyrażenie zgodne z łańcuchami 12abc12 lub
21abc21
(4|(?:5|6))(12|21)abc\2 – bez grupowania (?: referencja \2 mogła
by odwoływać się do dwóch różnych grup.
Grupowanie wyrażeń – przykłady
wyszukiwania
Asercja pozwala badać otoczenie szukanego łańcucha znaków przy jednoczesnym nie włączaniu ich do rezultatu.
… (?= … ) – Asercja zstępna pozytywna
… (?! … ) – Asercja zstępna negatywna
(?<= … ) … – Asercja wstępna pozytywna
(?<! … ) … – Asercja wstępna negatywna
(?<=12|21)abc – wyrażenie zgodne z łańcuchami abc poprzedzonymi
12 lub 21
(?<=12|21)abc(?=12|21) – wyrażenie zgodne z łańcuchami abc
otoczonymi 12 lub 21
(?<=(12|21))abc(?=12|21)(?!\1) – wyrażenie zgodne z łańcuchami abc
otoczonymi 12 lub 21 różnymi od siebie
Klasy pozwalają określić zbiory znaków jakie mogą wystąpić w szukanym łańcuchu.
[abcd] – klasa zbioru znaków zgodnych.
[^abcd] – klasa zbioru znaków niezgodnych.
[a-z] - zakres znaków zgodnych z wzorcem.
(?<=[ +])abc(?=[ ,0-9]) – wyrażenie pasujące do łańcucha abc otoczonego z
lewej spacją lub + oraz z prawej spacją, , i znakami od 0 do 9.
(?<=[^-0-9]) [0-9] [0-9]-[0-9] [0-9] [0-9] (?![-0-9]) – wyrażenie pasujące do
kodów pocztowych nie otoczonych znakami - oraz znakami od 0 do 9.
{n} – dokładnie n powtórzeń
{n,} – co najmniej n powtórzeń
{n,m} – od n do m powtórzeń
* - zero lub więcej powtórzeń ( {0,} ).
+ - jedno lub więcej powtórzeń ( {1,} ).
? - zero lub jedno powtórzeń ( {0,1} ).
{n,}? – co najmniej n powtórzeń ale jak najmniej
{n,m}? – od n do m powtórzeń, ale jak najmniej
*? - zero lub więcej powtórzeń ( {0,} ), ale jak najmniej
+? - jedne lub więcej powtórzeń ( {1,} ), ale jak najmniej.
?? - zero lub jedno powtórzeń ( {0,1} ), ale jak najmniej.
(00){0,}abc(df)+ - wyrażenie wyszuka łańcuchy abc wraz z
parzystą ilością 0 po lewej i co najmniej raz powtórzonymi znakami df po prawej
• Abcdf00abcabcdfdfabc00000abcdfabcdf
(00){0,}?abc(df)+? - wyrażenie wyszuka łańcuchy abc wraz z
parzystą ilością 0 po lewej i co najmniej raz powtórzonymi znakami df po prawej
• abcdf00abcabcdfdfabc00000abcdfabcdf
(?<=[^-0-9]) [0-9]{2}- [0-9]{3} (?![-0-9]) – wyrażenie pasujące do
kodów pocztowych nie otoczonych znakami - oraz znakami od 0 do
9.
\0 – pusty znak, w programowaniu znak końca string-a (\u0000)
\t – znak tabulacji (\u0009)
\n – nowa linia (\u000A)
\v – znak tabulacji pionowej (\u000B)
\xnn – znak ASCI w kodowaniu hexadecymalnym nn.
\onn – znak ASCI w kodowaniu ósemkowym nn.
\unnnn – znak Unikodu w kodowaniu hexadecymalnym nn.
\cX – znak sterujący X
\ - Znak specjalny zostanie zmieniony na znak tekstu i na
odwrót.
. - dowolny znak z wyjątkiem '\n'
\w – dowolna litera, cyfra lub znak podkreślenia ([a-zA-Z0-9_])
\W – dowolny znak niebędący literą, cyfrą lub znakiem
podkreśleniem ([^a-zA-Z0-9_])
\s – znak spacji.
\S – każdy znak z wyjątkiem spacji.
\d – znaki cyfr ([0-9])
\D – każdy znak z wyjątkiem cyfr ([^0-9])
Znaki specjalne w wyrażeniach
regularnych
Znaki kotwiczne pozwalają określić obszar łańcucha znaków, dla którego ma być sprawdzane wyrażenie. Działają w podobny sposób do asercji.
^ - początek łańcucha znaków.
$ - koniec łańcucha znaków.
\b – granica wyrazu.
\B - pozycja niebędąca granicą wyrazu
\b[a-z]+\b – wyrażenie wyszuka łańcuchów składających się ze znaków od a
do z przy czym wyszukany łańcuch musi być osobnym wyrazem.
\b[0-9]+ – wyrażenie wyszuka łańcuchy cyfr, które są również początkami
wyrazów.
^ [a-z]+ – wyrażenie wyszuka ciąg składających się ze znaków od a do z przy
czym wyszukany ciąg musi być na początku łańcucha wejściowego.
^[0-9]+$ – wyrażenie wyszuka, a raczej zwaliduje łańcuch, który będzie
składał się z samych cyfr.
Niemożliwe jest określenie klasy polskich znaków przez zakres znaków ([ą-źĄ-Ź]) musimy je wypisać ([ąćęłńóśźżĄĆĘŁŃÓŚŹŻ]) spowodowane to jest tym, że znaki polskie są wspólne z innymi językami i ich kody unikodu są rozrzucone np.
ą(\u0105), ć(\u0107), ó(\u00f3), ż(\u017c). Jak widać nie da się ich potraktować jako zakres znaków.
^abc – wyrażenie zgodne z łańcuchami rozpoczynającymi się od abc.
• abcabc abc
abc$ - wyrażenie wyszuka ciąg abc, który kończy łańcuch.
• abcabc abc
^abc$ – wyrażenie pasujące do łańcuchów abc.
• abc – zgodne
• abab abc – niezgodne
^(abc|cba)k2$ - wyrażenie pasujące do łańcuchów rozpoczętych abc lub cba
i zakończonych k2.
• cbak2 – zgodne
• cbak2 w – niezgodne
^(abc|cba).+k2$ - wyrażenie pasujące do łańcuchów rozpoczętych abc lub
cba i zakończonych k2 poprzedzonego dowolnymi znakami.
• cba..k2 – zgodne • cbak2k2 – zgodne • cbak2 – niezgodne
^(abc|cba)(.+)k2\2$ - \2 oznacza, że po prawej stronie k2 musi znaleźć się
ciąg zgodny z (.+) po jego lewej stronie.
• cba...k2... – zgodne • cbak2k2k2 – zgodne • cbak2k2 – niezgodne
[0-9]{2}-[0-9]{3} – wyrażenie wyszuka kody pocztowe.
• 1242-20042-6005
45- ^([0-9]{2}-[0-9]{3} ) $ - wyrażenie pasujące do kodu pocztowego
• 20-120 – zgodny
• 1220-222 – niezgodny
[^0-9]* - wzór pasujący do wszystkich znaków z wyjątkiem cyfr.
• 12d42-200_/l?_42-600xsds5sxs45
- \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} – wzór pasuje do adresów IP.
• ws192.168.1.1sdsw dws192.168,1.1sdsw d
[a-z0-9_.-]+@[a-z0-9_.-]+\.[a-z]{2,4} - wzór pasujący do adresu
email.
• adres_adres.tf@dmm.pl fg@hj.tc
(?=([^,]*[0-9]){3,})(?=([^,]*[A-B]){2,}) [^,]{5,} – wyrażenie
wyszuka łańcuchy minimum 5 znaków rozdzielonych znakiem ,, zawierających 3 cyfry i 2 duże litery.
• 123AB,1A23B,aaaa123,a2b#AB+12,12
(?=^.{5,}$)(?=(.*[0-9]){3,})(?=(.*[A-B]){2,})^.*$ - wzór walidujący hasła
zawierające min. 5 znaków w tym 2 duże litery i 3 cyfry.
• 123AB – zgodne • 1A23Bdsdf – zgodne • aaaa123 – niezgodne
Walidacje ze względów bezpieczeństwa należy przeprowadzać po stronie
serwera. Możliwość podesłania spreparowanego formularza.
Walidacja po stronie klienta jest sprawą kosmetyczną, usprawniającą
użytkownikowi korzystanie ze strony.
• Metody klasy string obsługujące wyrażenia regularne:
– match(wzór) – metoda szuka pasujących do wzorca treści, następnie zwraca ich zbiór lub NULL jeśli nic nie znalazła.
– replace(wzór, zastępnik) – metoda zastępuje treść pasującą do wzorca zastępnikiem.
– search(wzór) – wyszukuje treści pasującej do wzorca i zwraca jej pozycje w string-u lstring-ub -1 jeśli nic nie znajdzie.
– split(wzór, limit) – dzieli string-a na podstawie wzorca, dodatkowo można nadać limit rozdzielonych elementów.
Wyrażenia regularne definiujemy wprowadzając ich treść w znakach ‘/’
var re = /^([%]?[0-9A-Za-z])$/;
str.match(re);
str.match(/^([%]?[0-9A-Za-z])$/);
namespace TestAplication.Models {
[MetadataType(typeof(osobaValidate))]
public partial class osoba { }
[Bind(Exclude = "id")]
public class osobaValidate {
[Required(ErrorMessage = "Podaj nazwisko")]
[StringLength(20, ErrorMessage="Nazwisko max 20 znaków")]
[RegularExpression("^([a-zA-ZąćęłńóśźżĄĆĘŁŃÓŚŹŻ]+)$", ErrorMessage="Nazwisko tylko litery")]
public string nazwisko { get; set; } [Required(ErrorMessage = "Podaj imie")]
[StringLength(20, ErrorMessage = "Imie max 20 znaków")]
[RegularExpression("^([a-zA-ZąćęłńóśźżĄĆĘŁŃÓŚŹŻ]+)$", ErrorMessage="Imie tylko litery")]
public string imie { get; set; }
[Required(ErrorMessage = "Podaj wiek")]
[Range(1,130, ErrorMessage = "Wiek od 1 do 130")]
[RegularExpression("^([0-9]+)$", ErrorMessage="Wiek tylko liczby")]
public string wiek { get; set; }
}
[HttpPost]
public ActionResult Dodaj(osoba os) {
if (ModelState.IsValid) // informacja o zgodności modelu
{
repo.Dodaj(os);
if (Request.IsAjaxRequest())
return PartialView("OsobaTab", repo.PobierzListe());
return RedirectToAction("Index");
}
if (Request.IsAjaxRequest())
{
ViewData["errors"] = ModelState.Values.SelectMany(v => v.Errors);
return PartialView("OsobaTab", repo.PobierzListe());
}
return View(os);
}
<% Html.EnableClientValidation(); %>
// treść wprowadzanych danych będzie na bieżąco sprawdzana u klienta <% using (Html.BeginForm()) {%> <%: Html.ValidationSummary(true) %> <fieldset> <legend>Fields</legend> <div class="editor-label"> <%: Html.LabelFor(model => model.nazwisko) %> </div> <div class="editor-field"> <%: Html.TextBoxFor(model => model.nazwisko) %> <%: Html.ValidationMessageFor(model => model.nazwisko) %> </div> * * *
W C# z wyrażeń regularnych można korzystać za pomocą klasy Regex.
Posiada ona między innym metody:
bool IsMatch(String) Match Match(String)
string Replace(String, String) string[] Split(String)
Regex patern = new Regex("^([0-9]*)$");
if (!patern.IsMatch("34,8")){
// jakaś akcja }
Nr PESEL składa się z 11 cyfr z czego ostatnia jest kontrolna. W celu walidacji nr PESEL należy:
• Uzyskać sumę 10 pierwszych cyfr mnożonych przez ich wagi
sum = 1*P1+3*P2+7*P3+9*P4+1*P5+3*P6+7*P7+9*P8+1*P9+3*P10 • Uzyskać wartość modulo 10 z sumy.
mod = sum%10
• Uzyskać wartość kontrolną i sprawdzić zgodność z 11 cyfrą nr PESEL
wynik = (10-mod)%10 == P11
Źródła
http://myregexp.com/ - strona z apletem do pisania i testowania wyrażeń