Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> [php] Walidacja URL - najlepszy sposób?
tomilipin
post
Post #1





Grupa: Zarejestrowani
Postów: 70
Pomógł: 0
Dołączył: 28.01.2004
Skąd: że znowu ლ(ಠ益ಠლ

Ostrzeżenie: (0%)
-----


Cześć,
poszukuję najlepszego sposobu na walidację adresów stron w skrypcie PHP. Robię klasę więc wersja obiektowa będzie mile widziana.

Próbowałem już funkcji parse_url oraz filter_var - niestety, obie przepuszczają niepoprawne adresy (pomijając fakt, że parse_url nie służy do walidacji). Chciałbym się więc skierować w stronę wyrażeń regularnych, ale w wielu przypadkach użytkownicy to odradzają. Dlaczego?

Znalazłem takie wyrażenie regularne:
  1. '%^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@|\d{1,3}(?:\.\d{1,3}){3}|(?:(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+)(?:\.(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+)*(?:\.[a-z\x{00a1}-\x{ffff}]{2,6}))(?::\d+)?(?:[^\s]*)?$%iu'

z jednej strony jest okropnie długie i jego wykonanie może być zasochłonne, ale z drugiej strony - nie przepuści żadnego błędnego URLa.

Jeśli mogę to samo uzyskać krótszym i prostszym kodem, to chętnie skorzystam, tylko błagam... nie odsyłajcie do szukajek - większość linków (nawet do Zenda w innym temacie na forum) jest już nieaktualnych, albo proponują tam rozwiązanie przepuszczające np. http://www.. (dwie kropki na końcu)

Na dłuższy czas porzuciłem tę kwestię, ale znowu muszę się nią zająć... Nadal poszukuję niezawodnego sposobu parsowania URLi.
Wyrażenie, które podałem w powyższym poście przepuszcza adresy typu:
Kod
http://www.kbrg.
http://www.kbrg
(z kropką i bez kropki na końcu)

Próbowałem też regexpy ze strony http://mathiasbynens.be/demo/url-regex ale nie potrafię dostosować ich do PHP - ciągle wywala mi jakiś błąd (IMG:style_emoticons/default/sad.gif)

Jakieś pomysły?

Ten post edytował tomilipin 31.08.2013, 15:30:58
Go to the top of the page
+Quote Post
timon27
post
Post #2





Grupa: Zarejestrowani
Postów: 578
Pomógł: 69
Dołączył: 15.04.2007
Skąd: Wrocław

Ostrzeżenie: (0%)
-----


Ja bym to zrobił curlem:

  1. function urlExists($url=NULL)
  2. {
  3. if($url == NULL) return false;
  4. $ch = curl_init($url);
  5. curl_setopt($ch, CURLOPT_TIMEOUT, 5);
  6. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
  7. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  8. $data = curl_exec($ch);
  9. $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  10. curl_close($ch);
  11. if($httpcode>=200 && $httpcode<300){
  12. return true;
  13. } else {
  14. return false;
  15. }
  16. }
Go to the top of the page
+Quote Post
Crozin
post
Post #3





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Co jest nie tak z filter_varem? Poprawnie odrzuca pierwszy podany przez Ciebie URL (z kropką na końcu) i przepuszcza drugi (prawidłowy URL).
Go to the top of the page
+Quote Post
tomilipin
post
Post #4





Grupa: Zarejestrowani
Postów: 70
Pomógł: 0
Dołączył: 28.01.2004
Skąd: że znowu ლ(ಠ益ಠლ

Ostrzeżenie: (0%)
-----


timon, Twój kod akceptuje taki adres http://www.kbrg.

Ja już nie rozumiem... czy http://www.kbrg. jest poprawnym URLem?

Crozin, filter_var przepuszcza np. to
Kod
http://example.com/"><script>alert(document.cookie)</script>


Generalnie skłaniałem się do użycia tego regexpa
Kod
_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:/[^\s]*)?$_iuS

ale dostaję takie błędy:
Cytat
Notice: Undefined variable: _iuS in kaka.php on line 43
Warning: preg_match(): No ending delimiter '_' found in kaka.php on line 44


od razu zaznaczam, że nie znam się na regexp - bazuję na kopiuj-wklej ze strony http://mathiasbynens.be/demo/url-regex
Go to the top of the page
+Quote Post
timon27
post
Post #5





Grupa: Zarejestrowani
Postów: 578
Pomógł: 69
Dołączył: 15.04.2007
Skąd: Wrocław

Ostrzeżenie: (0%)
-----


Mój kod NIE przepuszcza adresu z kropką na końcu. Coś źle wkleiłeś.

  1. if(urlExists("http://www.kbrg.")) echo 'dobry'; else echo 'zly';


wklej to za definicją funkcji.
Bez kropki zwraca prawdę, bo to dobry adres

Ten post edytował timon27 16.03.2014, 17:22:06
Go to the top of the page
+Quote Post
Crozin
post
Post #6





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


@timon27: Twój sposób jest wyjątkowo słaby. 1) Wymaga wykonania żądania HTTP, które samo w sobie może się wykonywać dosłownie godzinami 2) Ogranicza się do kodów odpowiedzi z zakresu 200-300, czyli jak zew. strona padnie to nie będzie się dało zapisać jej URL-a?
@tomilipin: Ale URL, który podałeś jest przecież poprawnym URL-em, który jak najbardziej może istnieć. A ochronę przed XSS (bo jak rozumiem to chciałeś tutaj przedstawić) uzyskuje się przy pomocy htmspecialchars, które musi zostać użyte w momencie wstawiania danych do dokumentu HTML.
Go to the top of the page
+Quote Post
tomilipin
post
Post #7





Grupa: Zarejestrowani
Postów: 70
Pomógł: 0
Dołączył: 28.01.2004
Skąd: że znowu ლ(ಠ益ಠლ

Ostrzeżenie: (0%)
-----


Hmm... tak myślę nad tym, co napisał Crozin i dochodzę do wniosku, że faktycznie to może być problem. No bo jeśli strona padnie, ale jej adres jest poprawny, to niestety funkcja timona zwróci FALSE.

Co do filter_var - zastosowałem to tak:
Kod
var_dump(filter_var($url, FILTER_VALIDATE_URL));


i oto co otrzymałem...
Kod
string(13) "htp://www.k.."
string(17) "http://3628126748"
string(16) "http://224.1.1.1"
string(17) "http://a.b--c.de/"
Wszędzie powinno być FALSE


Natomiast filter_var odrzuca adresy, w których znajdują się znaki Unicode, np. http://www.broń.pl nie mówiąc już o czymś tak egzotycznym jak http://✪df.ws/123 pod którym kryje się działająca strona, ale nawet forumowy parser nie podołał wykryć w tym przypadku linka.


Ale nie poddaję się - szukam dalej rozwiązania (IMG:style_emoticons/default/smile.gif)

-- edit --

Zrobiłem (IMG:style_emoticons/default/smile.gif) Oto regexp, który perfekcyjnie filtruje URL:
Kod
%^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@|\d{1,3}(?:\.\d{1,3}){3}|(?:(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+)(?:\.(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+)*(?:\.[a-z\x{00a1}-\x{ffff}]{2,6}))(?::\d+)?(?:[^\s]*)?$%iu

Do użytku z preg_match

Pozdrawiam i dziękuję za pomoc (IMG:style_emoticons/default/smile.gif)

Ten post edytował tomilipin 17.03.2014, 17:43:44
Go to the top of the page
+Quote Post
sowiq
post
Post #8





Grupa: Zarejestrowani
Postów: 1 890
Pomógł: 339
Dołączył: 14.12.2006
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Cytat(tomilipin @ 17.03.2014, 17:41:23 ) *
Oto regexp, który perfekcyjnie filtruje URL


Pierwszy z brzegu kontrprzykład: http://test.aaaaaaaaaaaaaaa

Ja kiedyś zrobiłem wyrażenie regularne z listą wszystkich dostępnych domen końcówek domen (IMG:style_emoticons/default/smile.gif) Wydajność pewnie słaba, ale adresy jak powyżej nie przechodziły (IMG:style_emoticons/default/wink.gif)

[edit]
Zresztą można to przyspieszyć rozbijając na kilka akcji, np. najpierw jakiś strpos z listą domen, a później zawężanie wyniku przez regexp (IMG:style_emoticons/default/smile.gif)

Ten post edytował sowiq 17.03.2014, 20:03:35
Go to the top of the page
+Quote Post
emillo91
post
Post #9





Grupa: Zarejestrowani
Postów: 129
Pomógł: 13
Dołączył: 29.03.2012

Ostrzeżenie: (0%)
-----


Ja natomiast znalazłem coś takiego. Może sie przyda :
  1. preg_match("/^[a-zA-Z\-_]+\.[a-zA-Z0-9\.\-_]+\.[a-z]{2,4}$/D",$adres)
Go to the top of the page
+Quote Post
sowiq
post
Post #10





Grupa: Zarejestrowani
Postów: 1 890
Pomógł: 339
Dołączył: 14.12.2006
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


@emillo91, nowe domeny jak .travel, .museum itp. nie załapią się do Twojego regexpa.
Go to the top of the page
+Quote Post
tomilipin
post
Post #11





Grupa: Zarejestrowani
Postów: 70
Pomógł: 0
Dołączył: 28.01.2004
Skąd: że znowu ლ(ಠ益ಠლ

Ostrzeżenie: (0%)
-----


Cytat(sowiq @ 17.03.2014, 21:58:44 ) *
Pierwszy z brzegu kontrprzykład: http://test.aaaaaaaaaaaaaaa
Fuck... (IMG:style_emoticons/default/rolleyes.gif) Może uda się jakoś rozbudować tego regexpa żeby nie przepuszczał takich adresów?
Go to the top of the page
+Quote Post
sowiq
post
Post #12





Grupa: Zarejestrowani
Postów: 1 890
Pomógł: 339
Dołączył: 14.12.2006
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


Ale przecież niekoniecznie musisz to robić za pomocą jednego regexpa.

Moim zdaniem bardzo dobrym sposobem jest to, co napisałem powyżej. Wchodzisz na Wikipedię, kopiujesz listę domen i robisz z tego tablicę w PHP. Pierwszy krok to sprawdzenie, czy domena jest poprawna. A dalej lecisz z kolejnymi warunkami, chociażby przy pomocy regexpa / regexpów.
Go to the top of the page
+Quote Post
karakara
post
Post #13





Grupa: Zarejestrowani
Postów: 71
Pomógł: 6
Dołączył: 22.09.2012

Ostrzeżenie: (0%)
-----


Kilka wyrażeń i ich testy.

http://mathiasbynens.be/demo/url-regex

Tylko jeden przeszedł wszystkie testy - @diegoperini

Edit:
teraz widzę, że tomilipin to wrzucał

Ten post edytował karakara 18.03.2014, 18:24:33
Go to the top of the page
+Quote Post
Crozin
post
Post #14





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


@tomilipin: filter_var sprawdza czy dany URL jest technicznie poprawny, i faktycznie działa on jedynie w zakresie znaków US-ASCII. To dopiero pierwszy etap sprawdzania poprawności. Później powinieneś skorzystać z parse_url i sprawdzić czy poszczególnego części URL-a są przez Ciebie akceptowalne. Na przykład czy protokół to HTTP(S), albo czy host jest sensowną domeną/IP-kiem.
Go to the top of the page
+Quote Post
YourFrog
post
Post #15





Grupa: Zarejestrowani
Postów: 124
Pomógł: 22
Dołączył: 10.01.2014

Ostrzeżenie: (0%)
-----


Pozwole się wtrącić w waszą dyskusję.

Dlaczego próbujecie napisać metodę perfekcyjną do sprawdzenia poprawności adresu url ? Według mnie każdy ma inne potrzeby i robi portal dla innych ludzi. Jednemu będą pasowały linki z konkretnej domeny, innemu z całego świata (wystarczy że serwer odpowie) itd. Według mnie trzeba iść tropem dania narzędzia które w drobnym stopniu pomoże zwalidować adres jednak nie ograniczy programisty (nie wiem jak wy ale ja tych regexpów na pierwszy rzut oka nawet nie rozumiem).

Osobiście skłaniałbym się w kierunku prostej klasy która tak jak u @sowiq'a sprawdzi poprawność domen i protokołów, a resztę pozostawi do decyzji użytkownika. Według mnie sposób @timon27 jest dobry do określonych sytuacji (osobiście bym chciał aby linki na moim serwisie nie prowadziły do 404..). I nie powiniście go wykluczać całkowicie.

Poniżej zamieszczam link do repo w którym daje swoje rozwiazanie. Na 100% nie jest idealne jednak wydaje mi się że jak komuś czegoś będzie w nim brakować to sobie zdziedziczy po mojej klasie / dopisze helper i po sprawie.
https://svn.code.sf.net/p/urlvalidate/code/...k/YourFrog/Url/

Zamieściłem przykładowe testy z informacja czy przechodzą czy nie więc łatwiej można sprawdzić poprawnośc.
Go to the top of the page
+Quote Post
tomilipin
post
Post #16





Grupa: Zarejestrowani
Postów: 70
Pomógł: 0
Dołączył: 28.01.2004
Skąd: że znowu ლ(ಠ益ಠლ

Ostrzeżenie: (0%)
-----


Cześć,
znowu zacząłem grzebać w tym kodzie (IMG:style_emoticons/default/smile.gif)

Kilka osób sugerowało sprawdzanie poprawności samej domeny (TLD) - taki sposób odpada, gdyż nie przepuści adresów typu http://142.42.1.1 czy http://例子.测试
Wyrażenie regularne autorstwa @diegoperini, wspomniane wcześniej, nie jest idealne, gdyż nie sprawdza poprawności podanych domen i przepuszcza adresy typu http://asd.aaaaaaaaaaaaa
Innym pomysłem było użycie filter_var i parse_url - patrz pierwszy post i zapoznaj się z opisem tych funkcji w manualu.
Żeby sprawdzić czy "host jest sensowną domeną/IP-kiem" i tak trzeba użyć wyrażeń regularnych, nie widzę więc sensu dodatkowego użycia innych funkcji, skoro można to załatwić jedną linijką kodu.

YourFrog, myślę, że masz dużo racji. Idąc Twoim tokiem myślenia, poczyniłem już pewne kroki. Nie są mi potrzebne chińskie krzaki w nazwach TLD, więc pozbyłem się tej funkjonalności. Adresy, które będę walidował to strony producentów żywności, a nie sądzę, żeby którykolwiek reklamował się pod adresem IP, zatem nie jest mi potrzebna walidacja IPków (jednak zostwiłem ją na wszelki wypadek). Chcę mieć istniejącą domenę na końcu, więc dodałem listę akceptowanych przeze mnie nazw TLD. Nie pinguję serwera, bo w przypadku chwilowego braku odpowiedzi mój kod odrzuciłby poprawny adres. Oczywiście nie chcę, żeby linki prowadziły do 404, więc dopiero po walidacji całego adresu można sobie to sprawdzić.

Doszedłem więc do takiego regexpa:
Kod
_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z]{2}|com|org|net|inne_domeny_tutaj_wpisz)))(?::\d{2,5})?(?:/\S*)?$_iuS


ten rzeczywiście nie jest idealny, bo nie akceptuje poprawnych domen zapisanych chińskimi czy arabskimi krzakami, ale odpowiada moim potrzebom.

Co sądzicie?
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 23.08.2025 - 12:17