Kun je je eigen site hacken? Een blik op enkele essentiële veiligheidsoverwegingen

Twee keer per maand bekijken we enkele van onze favoriete lezers uit de geschiedenis van Nettuts +. Deze tutorial is voor het eerst gepubliceerd in juli 2008.

Versie één wordt goud! Bezoekers landen vanuit alle uithoeken van de wereld. Je weet dat er waarschijnlijk een paar kinderziektes zullen zijn; Ik bedoel, dit is 1.0.0.0? al die nullen zijn bedoeld om ons een beetje genade te geven, goed?

Misschien rolt die lafhartige stylesheet niet elegant in browser X. Een onvolledige opmerking gooit een gebroken mark-up weg. Misschien had je die databaseverbindingen toch moeten houden. He, we zien allemaal dingen over het hoofd in de opwinding van het laten draaien van onze eerste versie - maar hoeveel van deze onoplettendheid kunnen we graag in ons maag buigen, en hoevelen kunnen gewoon een bittere smaak achterlaten in de onze, en pijnlijker in de mond van onze cliënt?

Dit artikel loopt door de brainstormfase van het plannen van wat in dit geval een hypothetische, gebruikersgerichte webapplicatie is.

Hoewel je niet met een compleet project - noch een marktklare raamwerk zal worden achtergelaten, hoop ik dat een ieder van jullie, wanneer ze te maken krijgt met toekomstige werkbelastingen, kan nadenken over de beschreven betere praktijken. Dus, zonder verder oponthoud? Zit je comfortabel?


Het voorbeeld

We zijn door onze klant gevraagd om op te nemen in een bestaande site, een boekbeoordelingssysteem. De site heeft al gebruikersaccounts en biedt anoniem commentaar.

Na een korte chat met de klant hebben we de volgende specificaties om te implementeren, en slechts vierentwintig uur om het te doen:

Opmerking: op de server van de client worden PHP5 en MySQL uitgevoerd, maar deze details zijn niet essentieel voor het begrijpen van de bugbears die in dit artikel worden beschreven.


De processen:

Onze klant heeft ons een PHP omvat om toegang te krijgen tot de database:

We hebben de bron niet echt nodig om dit bestand te gebruiken. Als de klant ons alleen had verteld waar hij woonde, hadden we het kunnen gebruiken met een include-verklaring en de $ db veranderlijk.

Op naar autorisatie? binnen het datatabelschema houden we ons bezig met de volgende kolomnamen:

  • gebruikersnaam, varchar (128) - opgeslagen als platte tekst.
  • wachtwoord, varchar (128) - opgeslagen als platte tekst.

Gegeven dat we tegen de klok werken? laten we zo snel mogelijk een PHP-functie schrijven die we opnieuw kunnen gebruiken om onze gebruikers te verifiëren:


$ _REQUEST Variabelen

In de bovenstaande code ziet u dat ik een gebied-barnsteen heb gemarkeerd en een gebied rood.

Waarom heb ik het niet-zo gevaarlijke gemarkeerd? $ _REQUEST variabelen?

Hoewel dit geen echt gevaar oplevert, is het wel een lakse benadering als het gaat om de code aan de kant van de klant. PHP heeft drie arrays die de meesten van ons gebruiken om onze geposte gegevens van gebruikers te krijgen, en vaker wel dan niet kunnen we in de verleiding komen om te gebruiken $ _REQUEST. Deze array geeft onze PHP op eenvoudige wijze toegang tot de POST- en GET-variabelen, maar hierin ligt een potentiële onderbreking?

Overweeg het volgende scenario. Je schrijft je code-clientzijde om POST-verzoeken te gebruiken, maar je geeft het project over terwijl je pauze neemt - en wanneer je terugkomt, heeft je hulpkas een paar GET-verzoeken in het project geschreven. Alles loopt goed - maar dat zou niet moeten.

Enige tijd later typt een nietsvermoedende gebruiker een externe link in een commentaarvak en voordat je het weet, heeft die externe site een dozijn gebruikersnaam / wachtwoord-combinaties in zijn verwijzingslogboek.

Door te verwijzen naar de $ _POST variabelen in plaats van $ _REQUEST, we elimineren per ongeluk publicatie van elke werkende code die een riskant GET-verzoek kan onthullen.

Hetzelfde principe is van toepassing op sessie-id's. Als u merkt dat u sessievariabelen schrijft in URL's, doet u iets verkeerd of heeft u een heel goed reden om dit te doen.


SQL injectie

Nogmaals verwijzend naar de PHP-code, kan de rode gemarkeerde regel bij sommigen van u zijn uitgekomen? Voor diegenen die het probleem niet hebben opgemerkt, zal ik u een voorbeeld geven en vanaf daar bekijken of iets u riskant lijkt.

De snelste bescherming is om de omsluiting karakters of ontsnappen.

Deze afbeelding maakt de fout duidelijk door variabelen rechtstreeks in SQL-instructies in te bedden. Hoewel het niet precies kan worden gezegd wat controle die een kwaadwillende gebruiker zou kunnen hebben - het is gegarandeerd, als u deze methode gebruikt om een ​​SQL-instructie aaneen te rijgen, dat uw server nauwelijks wordt beschermd. Het bovenstaande voorbeeld is gevaarlijk genoeg op een alleen-lezen account; de krachten die een lees / schrijf-verbinding heeft, worden alleen beperkt door uw verbeeldingskracht.

Bescherming tegen SQL-injectie is eigenlijk vrij eenvoudig. Laten we eerst kijken naar het geval van citaat ingesloten stringvariabelen:

De snelste oplossing is om de omsluiting karakters of ontsnappen. Sinds PHP 4.3.0, de functie mysql_real_escape_string is beschikbaar om inkomende reeksen te reinigen. De functie neemt de onbewerkte tekenreeks als een enkele parameter en retourneert de tekenreeks met de vluchtige tekens die zijn ontsnapt. Echter mysql_real_escape_string ontsnapt niet aan alle tekens die geldige controletekens zijn in SQL? de gemarkeerde elementen in de onderstaande afbeelding tonen de technieken die ik gebruik om te ontsmetten String, nummer en Boolean waarden.

Het eerste hoogtepunt, de regel die instelt $ string_b gebruikt een PHP-functie genaamd addcslashes. Deze functie maakt deel uit van PHP sinds versie 4 en is, zoals in het bovenstaande voorbeeld is geschreven, mijn voorkeursmethode voor de veiligheid en veiligheid van SQL-tekenreeksen.

Een schat aan informatie is beschikbaar in de PHP-documentatie, maar ik zal in het kort uitleggen wat addcslashes doet en hoe kan het anders zijn mysql_real_escape_string.

Uit het bovenstaande schema kun je dat zien mysql_real_escape_string voegt geen schuine strepen toe aan het teken (%) procent.

De % wordt gebruikt in SQL NET ZOALS clausules, evenals enkele andere. Het gedraagt ​​zich als een wildcard en niet een letterlijke karakter. Het moet dus worden geëscaped door een voorgaande backslash-teken in alle gevallen waarin letterlijke tekenreeksen een SQL-instructie vormen.

De tweede parameter, ga ik naar addcslashes, welke in de afbeelding is stoutmoedig; is de karaktergroep waarvoor PHP slashes zal toevoegen. In de meeste gevallen zal dat gebeuren spleet de string waar je in voorziet tekens, en dan opereren op elk. Het is vermeldenswaard dat deze karaktergroep ook een reeks tekens kan krijgen, hoewel dat buiten het bestek van dit artikel valt. In de scenario's die we bespreken, kunnen we alfanumerieke tekens letterlijk gebruiken. ? Abcd1234? en alle andere tekens als hun letterlijke C-stijl? \ r \ n \ t ?, of hun ASCII-index? \ x0A \ x0D \ x09?.

Het volgende hoogtepunt maakt onze getalswaarden veilig voor SQL-instructies.

Deze keer willen we niets ontlopen, we willen alleen maar een geldige numerieke waarde hebben - of het nu een geheel getal of een zwevend punt is.

Je hebt het misschien gemerkt regel 10, en misschien vroeg hij zich af wat het doel was. Een paar jaar geleden werkte ik aan een logboekregistratiesysteem van een callcenter variabele + = 0; om numerieke waarden te garanderen. Waarom dit is gebeurd, kan ik niet eerlijk zeggen? tenzij voorafgaand aan PHP 4 dat was hoe we het deden ?! Misschien kan iemand die iets leest enig licht werpen op het onderwerp. Anders dan dat, als je, zoals ik, een dergelijke regel in het wild tegenkomt, je zult weten wat het probeert te doen.

Voorwaarts dan; lijnen 11 en 12 zijn alles wat we nodig hebben om onze numerieke invoerwaarden voor SQL voor te bereiden. Ik zou zeggen, had de input string $ number_i bevatte niet-numerieke karakters vooraan of naar links van de numerieke? onze waarden $ NUMBER_A, $ number_b en $ number_c zou alles is gelijk aan 0.

We zullen gebruiken floatval om onze inputnummers schoon te maken; PHP drukt alleen decimale plaatsen af ​​wanneer deze in de invoerwaarde voorkomen. Als u ze in een SQL-instructie afdrukt, worden er geen fouten veroorzaakt als er geen decimaal in de invoer staat. Zolang onze servercode veilig is, kunnen we de meer kieskeurige validatie achterlaten bij onze client-side code.

Voordat we doorgaan naar een definitieve aanbieding voor onze PHP, zullen we een blik werpen op de finale code highlight, het Booleaanse boksen.

Net als het C ++ -equivalent is een Boolean in PHP echt een geheel getal. Zoals in, True + True = Two. Er zijn talloze manieren om een ​​invoerreeks te vertalen naar een Booleaans type, mijn persoonlijke favoriet is: bevat de kleine letterreeks het woord waar?

Jullie mogen allemaal eigen voorkeursmethoden hebben; is de invoerreeks expliciet gelijk? waar? of is de invoerreeks? 1? etcetera? wat belangrijk is, is dat de waarde die binnenkomt, hoe deze er ook uit zou kunnen zien, wordt weergegeven door een Booleaanse waarde (of geheel getal) voordat we deze gebruiken.

Mijn persoonlijke filosofie is simpel: als X is waar of vals, dan X is een Boolean. Ik zal zalig alle code schrijven die ik later zou moeten bespreken met Booleans en niet kort, int, tinyint of iets dat niet Boolean is. Wat er op het metaal gebeurt, is niet mijn zorg, dus hoe het er voor een mens uitziet, is veel belangrijker.

Dus, net als met cijfers en snaren, zijn onze Booleans gegarandeerd veilig vanaf het moment dat we ze in ons script trekken. Bovendien heeft onze hygiënecode geen extra regels nodig.


HTML verwerken

Nu we onze SQL hebben beschermd tegen injecties en we hebben zeker gemaakt dat alleen een POST-login minzaam met ons script kan werken, zijn we klaar om onze functie voor het indienen van beoordelingen te implementeren..

Onze klant wil gebruikers met een beoordelingsfout in staat stellen hun bijdragen op te stellen als gewone HTML. Dit lijkt eenvoudig genoeg, maar we weten ook dat e-mailadressen tien zijn en dat boekwinkelaccounts programmatisch zijn gemaakt - dus in het beste belang van iedereen zorgen we ervoor alleen de tags die we zeggen passeren.

Beslissen hoe we de binnenkomende beoordeling controleren, lijkt misschien ontmoedigend. De HTML-specificatie heeft een tamelijk gezonde reeks tags, waarvan we er veel graag toestaan.

Hoe lang de taak ook mag lijken, ik adviseer iedereen graag - kies wat toe te staan, en nooit wat te ontkennen. Browser- en servermarkeringstalen allemaal vasthouden aan XML zoals structureren, zodat we onze code kunnen baseren op het fundamentele feit dat uitvoerbare code moet worden omgeven door of deel moet uitmaken van tags die tussen haakjes zijn geplaatst.

Toegegeven, er zijn verschillende manieren waarop we hetzelfde resultaat kunnen bereiken. Voor dit artikel zal ik een mogelijke reguliere expressie pipeline beschrijven:

Deze reguliere expressies zullen geen perfecte resultaten opleveren, maar in de meeste gevallen moeten ze een bijna elegante taak uitvoeren.

Laten we eens kijken naar de reguliere expressie die we zullen gebruiken in onze PHP. U zult zien dat er twee arrays zijn gedeclareerd. $ safelist_review en $ safelist_comment - dit is zodat we dezelfde functies kunnen gebruiken om beoordelingen en later, opmerkingen te valideren:

? en hier is de belangrijkste functie die we zullen oproepen om de evaluatie- en reactiegegevens te ontsmetten:

De invoerparameters, ik heb rood en blauw gemarkeerd. $ ingang zijn de onbewerkte gegevens zoals ingediend door de gebruiker en $ lijst is een verwijzing naar de expressie-array; $ safelist_review of $ safelist_comment uiteraard afhankelijk van welk type inzending we willen valideren.

De functie retourneert de opnieuw ingedeelde versie van de ingediende gegevens - alle tags die niet doen slagen voor een van de reguliere expressies in onze gekozen lijst worden geconverteerd naar HTML-gecodeerde equivalenten. Wat in de eenvoudigste bewoordingen maakt < en > in < en > andere tekens worden ook gewijzigd, maar geen van deze vormen echt een beveiligingsrisico voor onze klant of de gebruikers.

Opmerking: de functies: cleanWhitespace en getTags zijn opgenomen in de bronbestanden van het artikel.

U zou gelijk hebben om aan te nemen dat alles wat we echt hebben gedaan de esthetische aspecten van de pagina's van onze site heeft overleefd en niet alles heeft gedaan om de veiligheid van de gebruiker te beschermen. Er is echter nog steeds een vrij groot beveiligingsgat: JavaScript-injectie.

Deze specifieke fout kan worden verholpen door een paar meer reguliere expressies en / of wijzigingen aan degene die we al gebruiken. Onze reguliere expressie voor het anker staat alleen toe? /? ?,? h? ? en? #? ? waarden als de href attribuut - wat eigenlijk slechts een voorbeeld van een oplossing is. Browsers begrijpen, over de hele linie, een enorme verscheidenheid aan script zichtbaar attributen, zoals bij klikken, onLoad enzovoorts.

We hebben in wezen een lastig probleem voor onszelf gecreëerd. We wilden HTML toestaan, maar nu hebben we een bijna eindeloze lijst met zoekwoorden om te strippen. Er is natuurlijk een minder dan perfecte - maar vrij snel geschreven manier om dit te doen:

Bij nader inzien zou je absoluut gerechtvaardigd zijn om te vragen: "Waarom hebben we niet gewoon BBCode of Textile of gebruikt?? ??

Ikzelf zou, als ik te maken had met mark-upverwerking, zelfs gaan voor XML-wandelen. Na alle inkomende gegevens moeten een geldige XML zijn.

Dit artikel is echter niet bedoeld om ons te leren hoe je kunt regexen, hoe je moet PHP of hoe je iets in een bepaalde taal moet schrijven. De redenering achter het simpelweg zijn, laat geen deuren op een kier.

Dus laten we het dan afmaken; met een snelle beoordeling van wat we hebben bekeken:

Toegegeven, dit artikel heeft u nog niet uitgerust met een standaardproject. Een belangrijk doel van mijn schrijven was niet om de ontwerpers die code coderen of het programmeerwerk van programmeurs doen weg te jagen, maar om iedereen aan te moedigen om vanaf het begin krachtige code te schrijven. Dat gezegd hebbende, ben ik van plan om bepaalde elementen van dit artikel later in meer detail te bekijken.

Tot die tijd veilig coderen!