login  Naam:   Wachtwoord: 
Registreer je!
 Forum

validatie url in [url] tag

Offline Thomas - 01/08/2014 16:31
Avatar van ThomasModerator Je kent het wel: (forum)functionaliteit -zoals hier ook gebruikt wordt- waarbij je een url tussen [ url ] tags zet (maar dan zonder spaties), met optioneel een omschrijving. Deze functionaliteit zet deze informatie om naar een hyperlink in HTML.

Natuurlijk moet je wel enigszins voorzichtig omgaan met deze functionaliteit want je kunt er wel enige rottigheid mee uithalen. De URL zal op correctheid gecontroleerd moeten worden (en bij het afdrukken zal alles ge-escaped moeten worden uiteraard).

Maar hoe valideer ik mijn URL? Er zijn een aantal mogelijkheden.

regexp from hell (RFH)
Je zou een reguliere expressie kunnen opstellen die (zo goed mogelijk) de RFC's volgt, maar soms heb je dan nog steeds valse positieven en valse negatieven.

filter_var in combinatie met FILTER_VALIDATE_URL
Maar deze werkt niet met multibyte karakters zoals hier en ook op PHP.net wordt opgemerkt. De auteur uit het eerder gelinkte artikel haalt wel input filtering en output escaping door elkaar...

andere opties?
...

En dan heb je nog "inline detectie" van URL's (vaak met dezelfde RFH). Maar die neemt mogelijk punten van de eindes van zinnen mee, of kapt deze mogelijk op een raar punt af doordat er een of andere lijpe word-cut-wrap bewerkking in de rest van de UBB-functionaliteit zit .

Dit brengt mij tot de volgende gedachtengang/strategie:
* Ik weet niet hoe URL's van gebruikers er uitzien, niet (op voorhand) waar deze beginnen of eindigen. Als het de intentie is van een gebruiker om een link te plaatsen, dan moet deze tussen [ url ] tags staan.
* De enige harde eis die ik heb is dat deze begint met http of https.
* Het lijkt mij de verantwoordelijkheid van de gebruiker zelf om een kloppende URL in te vullen (derp).

En de volgende (vereenvoudigde) implementatie (aanname: karakterset = UTF-8):
  1. <?php
  2. header('Content-Type: text/html; charset=UTF-8');
  3.  
  4. function escape($in) {
  5. return htmlspecialchars($in, ENT_QUOTES, 'UTF-8');
  6. }
  7.  
  8. function callback($matches) {
  9. if (empty($matches[2])) {
  10. $url = $matches[3];
  11. $description = $matches[3];
  12. } else {
  13. $url = $matches[2];
  14. $description = $matches[3];
  15. }
  16.  
  17. // do not use /s switch - we do not allow newlines
  18. if (preg_match('#^https?://(.*)$#', $url) == 1) {
  19. return '<a href="'.$url.'">'.$description.'</a>';
  20. } else {
  21. return $matches[0];
  22. }
  23. }
  24.  
  25. $input = 'your (unclean) input';
  26. echo nl2br(preg_replace_callback('#\[url(=([^]]+))?](.*)\[/url]#siU', 'callback', escape($input)));
  27. ?>


Uiteraard heeft dit ook nadelen (je kunt altijd rotzooi in een URL stoppen) maar is bij mijn weten wel veilig ($input wordt geescaped in de preg_replace_callback aanroep, je zou er ook voor kunnen kiezen om op een andere plaats output te escapen en/of bij te houden of dit is gebeurd).

Ideeën / suggesties?

2 antwoorden

Gesponsorde links
Offline Wijnand - 05/08/2014 12:34
Avatar van Wijnand Moderator Je zou nog ipv (.*) de tekens doen die mogen en een + doen ipv * (zodat je niet linkt naar https:// maar pas bij https://{character}, dus bv:

([a-zA-Z0-9\-\_\@\+ ]+)

En dan nog wat andere tekens die je wil toestaan. Dan haal je de rare hacks er ook al uit.
Offline Thomas - 05/08/2014 15:50 (laatste wijziging 05/08/2014 16:48)
Avatar van Thomas Moderator De volgende URL is prima geldig (weet niet of deze correct getoond zal worden ivm latin1 opslag, het betreft iig de Starbucks site in Korea geloof ik - zie ook eerdergenoemd artikel): http://&#49...50500;.com. (edit: rofl, dit forum verkloot de URL haha)

Ik wil dus zo min mogelijk/geen aannames doen over hoe de URL er uitziet. Die aannames zitten er namelijk nogal eens naast...

Enne, voor case-sensitivity (or lack thereof) heb je de /i switch, a-zA-Z is niet nodig.

+ vervangen door * is al wat beter, de url moet niet leeg zijn uiteraard (derp).

Ik zou trouwens de /s switch ook kunnen verwijderen uit de onderste aanroep, je wilt waarschijnlijk geen lappen (regeloverschrijdende) tekst zetten in een hyperlink.

Rare hacks ontstaan doordat je (vervolgens) niet goed escaped, lijkt mij, zodat de invoer een speciale betekenis krijgt binnen de HTML-context. Dit kan komen doordat je of iets verkeerd doet in combinatie met je character encoding (de escaping sluit niet aan bij de ingestelde character encoding) of de character encoding zelf heeft "exploits" zoals UTF-7 (?) en GBK. Maar dit (in het laatste geval) zijn nogal obscure randgevallen, die zijn gedocumenteerd.

Dit is volgens mij niet iets wat je kunt oplossen met input filtering tenzij je een soort van blacklist opstelt. Het nadeel van een blacklist is dat de kans groot is dat je gevallen vergeet (gevolg: valse positieven). En het nadeel van een whitelist is dus dat je wellicht teveel uitsluit (gevolg: valse negatieven).

Daarnaast: stel ik wil een virtual host maken genaamd "a" (because I can). http://a zou dan een geldige URL moeten zijn. Heb dit getest met WAMP, er wordt niet (noch door Apache, noch door mijn browser) geklaagd dat dit een ongeldige URL zou zijn. Of dit een verstandige naamgeving is is een tweede, maar dit zou gewoon moeten kunnen.

Uitdaging: probeer eens iets te vernielen met bovenstaande implementatie .

BTW: de "$" in regexps accepteert een enkele trailing newline (\n), dus wellicht loont het de moeite om de URL te trimmen (leading en trailing "whitespace" karakters snijden toch geen hout in URLs).
Gesponsorde links
Je moet ingelogd zijn om een reactie te kunnen posten.
Actieve forumberichten
© 2002-2024 Sitemasters.be - Regels - Laadtijd: 0.172s