De auteur beschrijft hoe een in 2014 geschreven 'shim' van 174 regels PHP, bedoeld als tijdelijke oplossing voor het CMS van AOL, onverwacht wijdverspreid raakte. Met bijna 20 miljoen installaties via Packagist en integraties in software zoals WPML, Debian en Ubuntu, is de code onderdeel geworden van een groot deel van het web.
Onlangs ontdekte de auteur een bug in de code waarbij bepaalde letters uit URL's verdwijnen bij trailing slashes. In plaats van deze te repareren of het project over te dragen, heeft de auteur besloten het pakket als deprecated te markeren. De redenen hiervoor zijn de beschikbaarheid van moderne alternatieven (zoals de PHP League URI-bibliotheek en de URI-API in PHP 8.5) en het vermijden van veiligheidsrisico's die gepaard gaan met het overdragen van wijdverspreide open-source pakketten aan nieuwe beheerders.
Mijn tijdelijke PHP-fix uit 2014 heeft bijna 20 miljoen installaties. Vandaag markeer ik deze als verouderd.
Een tijdelijke shim
In 2014 waren we bezig met het upgraden van het CMS van AOL van PHP 5.2 naar 5.3. Een onderdeel van die upgrade was het verwijderen van versie 1 van de peclhttp-extensie, die ons een functie gaf genaamd httpbuildurl(). Een CMS verwerkt veel URL's, en het onze gebruikte die functie op tientallen plekken. Ik wilde die code niet aanpassen. De functie leek eenvoudig genoeg om na te bouwen, dus ik schreef mijn eigen httpbuild_url(), die alleen werd gedefinieerd als de echte versie nog niet bestond. De oude code merkte er niets van.
Composer was op dat moment net opgekomen, wat het delen ervan makkelijk maakte. Ik dacht dat het een jaar of twee nuttig zou zijn, totdat de PHP-gemeenschap overstapte op iets beters.
Dat zijn veel installaties
Nou, het bleek niet tijdelijk te zijn. Het is via Packagist bijna 20 miljoen keer geïnstalleerd en het haalt nog steeds meer dan 400.000 installaties per maand.
En het blijkt dat Composer slechts een deel van het verhaal is. WPML, de marktleidende meertalige plugin voor WordPress, bundelt de polyfill direct in de eigen codebase, en WPML geeft aan dat het op meer dan 1,5 miljoen sites is geïnstalleerd. De domeinnaam-bibliotheek idna-convert is er ook van afhankelijk, waardoor het is terechtgekomen in de broncode van SPIP (een Frans CMS) en uiteindelijk is opgenomen in Debian en Ubuntu. Grote kans dat je een website hebt bezocht die nog steeds mijn code gebruikt.
Ik had nooit kunnen vermoeden dat het zo ver zou komen.
Er opnieuw naar kijken
Ik besefde pas een paar maanden geleden hoe wijdverspreid het was, toen ik voor het eerst in jaren weer naar het pakket keek. Ik wist dat er gebruikers waren. Tegen 2021 was ik al een tijdje gestopt met PHP, en de downloads waren zo verrassend dat ik vroeg om een nieuwe beheerder. Drie mensen boden zich aan. Kort daarna verloor ik onverwacht een familielid, wat onze wereld een tijdje op zijn kop zette. Ik heb hier nooit op gereageerd, en dat is mijn fout. Tegen de tijd dat de rust was teruggekeerd, hadden andere doelen prioriteit gekregen en was ik het pakket jarenlang vergeten.
Naast de cijfers waren er een handvol GitHub-issues, waaronder één waarbij het toevoegen van een pad aan een URL met een trailing slash (sluitende schuine streep) elke letter "a" uit het pad verwijdert. Zo eenvoudig was het dus ook niet. Onder een commentaar met de tekst // Workaround for trailing slashes plakt mijn code een "a" aan het pad, zodat er altijd een laatste segment is om weg te snijden, waarna dit met een 'find-and-replace' wordt verwijderd. Wanneer het pad eindigt op een slash, is dat laatste segment simpelweg de "a", en verwijdert de find-and-replace alle andere "a's" in het pad mee. Ik kan niet geloven dat deze bug zo lang onopgemerkt is gebleven.
Ik moest een beslissing nemen. Ik kon na bijna tien jaar weer in PHP duiken, het pakket overdragen aan een van de mensen die zich hadden aangeboden, of het gewoon laten staan.
Geen van bovenstaande
Het was altijd bedoeld als tijdelijke oplossing, dus ik stop ermee. De URI-bibliotheek van de PHP League is al jaren het antwoord van de community, en PHP 8.5 wordt nu geleverd met een standaardconforme URI-API in de taal zelf (bedankt aan jawira voor de tip). Beiden zijn beter dan een shim van 174 regels uit 2014.
Het pakket blijven onderhouden zou de overstap die iedereen zou moeten maken alleen maar vertragen, en het overdragen zou daar een extra risico aan toevoegen. Ik twijfel niet aan de mensen die zich aanboden, en ozh heeft een fork in stand gehouden voor YOURLS. Maar een wijdverspreid pakket met een nieuwe beheerder die niet is gecontroleerd door downstream-gebruikers is precies waar aanvallers op letten. De video van Veritasium over de xz Utils backdoor is het beste voorbeeld van hoe dat in zijn werk gaat.
Het pakket blijft beschikbaar voor installatie, maar er komen geen nieuwe fixes meer, ook niet voor de bug met de ontbrekende "a". Na zo'n lange tijd zonder wijzigingen zou zelfs een fix van één regel onvoorziene gevolgen kunnen hebben voor iemand, zonder dat er iemand is om ondersteuning te bieden. In de README staat hoe je kunt overstappen.
Ik schreef deze code om een pijnlijke migratie te vergemakkelijken, voor mezelf en iedereen die in hetzelfde schuitje zat. Bedankt aan iedereen die een pull-request stuurde of aanbood het over te nemen, en aan de mensen die issues bleven melden lang nadat ik gestopt was met lezen. Het was een mooie rit voor een tijdelijke oplossing.
P.S. We zijn het CMS van AOL nooit echt gemigreerd weg van de "tijdelijke" polyfill. Het is daar blijven draaien totdat het hele platform rond 2020 werd uitgeschakeld.
Mijn tijdelijke PHP-fix uit 2014 heeft bijna 20 miljoen installaties. Vandaag markeer ik deze als verouderd.
Een tijdelijke shim
In 2014 waren we bezig met het upgraden van het CMS van AOL van PHP 5.2 naar 5.3. Een onderdeel van die upgrade was het verwijderen van versie 1 van de peclhttp-extensie, die ons een functie gaf genaamd httpbuildurl(). Een CMS verwerkt veel URL's, en het onze gebruikte die functie op tientallen plekken. Ik wilde die code niet aanpassen. De functie leek eenvoudig genoeg om na te bouwen, dus ik schreef mijn eigen httpbuild_url(), die alleen werd gedefinieerd als de echte versie nog niet bestond. De oude code merkte er niets van.
Composer was op dat moment net opgekomen, wat het delen ervan makkelijk maakte. Ik dacht dat het een jaar of twee nuttig zou zijn, totdat de PHP-gemeenschap overstapte op iets beters.
Dat zijn veel installaties
Nou, het bleek niet tijdelijk te zijn. Het is via Packagist bijna 20 miljoen keer geïnstalleerd en het haalt nog steeds meer dan 400.000 installaties per maand.
En het blijkt dat Composer slechts een deel van het verhaal is. WPML, de marktleidende meertalige plugin voor WordPress, bundelt de polyfill direct in de eigen codebase, en WPML geeft aan dat het op meer dan 1,5 miljoen sites is geïnstalleerd. De domeinnaam-bibliotheek idna-convert is er ook van afhankelijk, waardoor het is terechtgekomen in de broncode van SPIP (een Frans CMS) en uiteindelijk is opgenomen in Debian en Ubuntu. Grote kans dat je een website hebt bezocht die nog steeds mijn code gebruikt.
Ik had nooit kunnen vermoeden dat het zo ver zou komen.
Er opnieuw naar kijken
Ik besefde pas een paar maanden geleden hoe wijdverspreid het was, toen ik voor het eerst in jaren weer naar het pakket keek. Ik wist dat er gebruikers waren. Tegen 2021 was ik al een tijdje gestopt met PHP, en de downloads waren zo verrassend dat ik vroeg om een nieuwe beheerder. Drie mensen boden zich aan. Kort daarna verloor ik onverwacht een familielid, wat onze wereld een tijdje op zijn kop zette. Ik heb hier nooit op gereageerd, en dat is mijn fout. Tegen de tijd dat de rust was teruggekeerd, hadden andere doelen prioriteit gekregen en was ik het pakket jarenlang vergeten.
Naast de cijfers waren er een handvol GitHub-issues, waaronder één waarbij het toevoegen van een pad aan een URL met een trailing slash (sluitende schuine streep) elke letter "a" uit het pad verwijdert. Zo eenvoudig was het dus ook niet. Onder een commentaar met de tekst // Workaround for trailing slashes plakt mijn code een "a" aan het pad, zodat er altijd een laatste segment is om weg te snijden, waarna dit met een 'find-and-replace' wordt verwijderd. Wanneer het pad eindigt op een slash, is dat laatste segment simpelweg de "a", en verwijdert de find-and-replace alle andere "a's" in het pad mee. Ik kan niet geloven dat deze bug zo lang onopgemerkt is gebleven.
Ik moest een beslissing nemen. Ik kon na bijna tien jaar weer in PHP duiken, het pakket overdragen aan een van de mensen die zich hadden aangeboden, of het gewoon laten staan.
Geen van bovenstaande
Het was altijd bedoeld als tijdelijke oplossing, dus ik stop ermee. De URI-bibliotheek van de PHP League is al jaren het antwoord van de community, en PHP 8.5 wordt nu geleverd met een standaardconforme URI-API in de taal zelf (bedankt aan jawira voor de tip). Beiden zijn beter dan een shim van 174 regels uit 2014.
Het pakket blijven onderhouden zou de overstap die iedereen zou moeten maken alleen maar vertragen, en het overdragen zou daar een extra risico aan toevoegen. Ik twijfel niet aan de mensen die zich aanboden, en ozh heeft een fork in stand gehouden voor YOURLS. Maar een wijdverspreid pakket met een nieuwe beheerder die niet is gecontroleerd door downstream-gebruikers is precies waar aanvallers op letten. De video van Veritasium over de xz Utils backdoor is het beste voorbeeld van hoe dat in zijn werk gaat.
Het pakket blijft beschikbaar voor installatie, maar er komen geen nieuwe fixes meer, ook niet voor de bug met de ontbrekende "a". Na zo'n lange tijd zonder wijzigingen zou zelfs een fix van één regel onvoorziene gevolgen kunnen hebben voor iemand, zonder dat er iemand is om ondersteuning te bieden. In de README staat hoe je kunt overstappen.
Ik schreef deze code om een pijnlijke migratie te vergemakkelijken, voor mezelf en iedereen die in hetzelfde schuitje zat. Bedankt aan iedereen die een pull-request stuurde of aanbood het over te nemen, en aan de mensen die issues bleven melden lang nadat ik gestopt was met lezen. Het was een mooie rit voor een tijdelijke oplossing.
P.S. We zijn het CMS van AOL nooit echt gemigreerd weg van de "tijdelijke" polyfill. Het is daar blijven draaien totdat het hele platform rond 2020 werd uitgeschakeld.