Vaak lijken websites voornamelijk te bestaan om iets in een database te plaatsen om het later weer tevoorschijn te halen. Terwijl andere databasemethoden, zoals NoSQL, de afgelopen jaren aan populariteit hebben gewonnen, bevinden gegevens voor veel websites zich nog steeds in de traditionele SQL-database. Deze gegevens bestaan vaak uit waardevolle persoonlijke informatie zoals creditcardnummers en andere persoonlijke informatie die van belang is voor identiteitsdieven en criminelen. Hackers zoeken daarom altijd naar deze gegevens. Een van de meest voorkomende doelen van deze aanvallen zijn de SQL-databases die achter veel webtoepassingen liggen via een proces van SQL Injection.
Een injectie-aanval werkt door de toepassing onbetrouwbare invoer door te geven aan de tolk. Onlangs zijn 40.000 klantrecords van Bell Canada het resultaat van een SQL Injection-aanval. Eind 2013 stalen hackers meer dan $ 100.000 van een in Californië gevestigde internetprovider met behulp van SQL-injectie.
Het Open Web Application Security Project (OWASP) koos de injectie-aanval als het nummer één beveiligingsrisico van de applicatie in hun top tien van 2013, op basis van de prevalentie en het risico voor de aangevallen websystemen. Helaas had het ook de nummer één positie in het vorige rapport uit 2010. Dus wat is een SQL Injection-aanval? In deze tutorial zal ik bespreken hoe ze werken en wat u kunt doen om uw applicatie tegen deze aanvallen te beschermen.
Elk geïnterpreteerd systeem achter een webserver kan het doelwit zijn van een injectie-aanval. De meest voorkomende doelen zijn de SQL-databaseservers achter veel websites. SQL-injectie is niet rechtstreeks het gevolg van zwakke punten in de database, maar maakt gebruik van openingen in de toepassing om de aanvaller in staat te stellen uitspraken van de aanvaller op de server uit te voeren. Een SQL-injectie-aanval zorgt ervoor dat de database meer informatie deelt dan de applicatie is bedoeld. Laten we eens kijken naar een SQL-databaseaanroep die u in ASP.NET zou kunnen schrijven.
SqlCommand-opdracht = new SqlCommand ("SELECT * FROM userdata WHERE UserId =" + id); SqlDataReader-reader = command.ExecuteReader ();
Wat is er mis met deze code? Misschien niets. Het probleem is dat ID kaart
string die we gebruiken. Waar komt die waarde vandaan? Als we dit intern of uit een vertrouwde bron genereren, werkt deze code mogelijk zonder problemen. Als we echter de waarde van een gebruiker krijgen en deze zonder wijziging gebruiken, hebben we ons net geopend voor SQL-injectie.
Laten we een veelvoorkomend geval nemen waarbij we de parameter laten opzoeken als onderdeel van de URL. Neem de URL http://www.example.com/user/details?id=123
. In ASP.NET met C # kunnen we die doorgegeven waarde verkrijgen met behulp van deze code:
string id = Request.QueryString ["id"];
Deze code in combinatie met de bovenstaande oproep laat ons openstaan voor een aanval. Als de gebruiker een gebruikers-ID zoals 123 doorgeeft zoals verwacht, werkt alles goed. Niets wat we hier hebben gedaan, zorgt ervoor dat dit het geval is. Laten we zeggen dat de aanvaller probeert toegang te krijgen tot de URL http://www.example.com/user/details?id=0; SELECTEER * UIT userdata
.
In plaats van alleen een waarde door te geven zoals verwacht, hebben we een waarde gegeven en vervolgens een puntkomma toegevoegd, waarmee een SQL-instructie wordt beëindigd. Vervolgens wordt een tweede SQL-instructie toegevoegd die de aanvaller zou willen uitvoeren. In dit geval worden alle records in de userdatatabel geretourneerd. Afhankelijk van de rest van de code op onze pagina, kan er een fout optreden of kan elke record in de database worden weergegeven voor de aanvaller. Zelfs een fout kan worden gebruikt met zorgvuldig samengestelde query's om een weergave van de database te maken. Erger nog, stel je voor dat de aanvaller naar een URL van http://www.example.com/user/details?id=0; DROP TABLE userdata
. Nu zijn al uw gebruikers verloren.
In dit voorbeeld kan de aanvaller elke gewenste code uitvoeren op de databaseserver. Als het account waarop de databaseaanvragen worden uitgevoerd, volledige controle over de database heeft, is een veelvoorkomend scenario, dan tabellen laten vallen en records verwijderen een eenvoudige manier om een site neer te halen. Zelfs als de aanvaller alleen gegevens in de database kan lezen en schrijven, is het pollen van gegevens alleen een kwestie van geduld en zorg.
Neem een eenvoudige database met de naam "Producten", bestaande uit drie kolommen. De eerste kolom bevat de product-ID, de tweede bevat de productnaam en de derde bevat de prijs van het product. Voor onze normale zoekopdracht proberen we elk product met widget in de naam te vinden. De SQL om dit te doen in hetzelfde patroon dat we laten zien, ziet er als volgt uit:
string sql = "SELECT * FROM Products WHERE productnaam LIKE '%' + searchterm +"% '";
Een typische website om toegang te krijgen tot deze zou eruit zien http://www.example.com/product?search=widget
. Hier zal de webpagina eenvoudig door elke geretourneerde record bladeren en deze op het scherm weergeven. In dit geval zien we ons widget-product.
| productid | productnaam | prijs | | ----------- | 1 | Widget | 100.00 |
Laten we onze vraag veranderen naar http://www.example.com/product?search=widget 'OR 1 = 1;--
en voer dezelfde query uit. zie iets meer. In feite zullen we elk record in de tabel zien.
| productid | productnaam | prijs | | --- | 1 | Widget | 100.00 | | 2 | Dingy | 50.00 | | 3 | Boxy | 125.00 |
We hebben de interpreter misleid tot het uitvoeren van SQL-code van onze keuze. De resulterende SQL die wordt uitgevoerd, is:
SELECT * FROM Producten WAAR productnaam LIKE '% widget' OF 1 = 1; -% '
Het resultaat zal twee uitspraken zijn:
SELECT * FROM Producten WAAR productnaam LIKE '% widget' OF 1 = 1; -%'
Door de 1 = 1
, wat altijd waar is, de where -clausule is waar voor elke rij in de tabel en de resulterende query retourneert elke rij. De --
aan het begin van de tweede instructie wordt de rest van de SQL-instructie omgezet in een opmerking die het foutbericht voorkomt dat we anders zouden zien.
Wijzigingen in het display kunnen deze verklaring niet voorkomen. Een aanvaller van een patiënt kan zelfs niets meer dan het feit dat een waarde wordt geretourneerd en zorgvuldig geconstrueerde vragen gebruiken om uw database langzaam in kaart te brengen en eventueel gegevens op te halen, zelfs als deze alleen een foutmelding zien. Er zijn tools zoals sqlmap om het proces te automatiseren.
U verhindert SQL-injectie door te voorkomen dat niet-vertrouwde invoer toegang krijgt tot de SQL-database of andere interpreter. Elke invoer van buiten het systeem moet als niet-vertrouwd worden beschouwd. Zelfs gegevens van andere partnersystemen moeten als onbetrouwbaar worden beschouwd, omdat u niet kunt garanderen dat het andere systeem geen beveiligingsproblemen ondervindt waardoor willekeurige gegevens kunnen worden ingevoerd en vervolgens aan uw toepassing kunnen worden doorgegeven.
Als we teruggaan naar ons eerdere voorbeeld, als we weten dat de id-parameter altijd een geheel getal moet zijn, kunnen we proberen deze te converteren naar een geheel getal en een fout weergeven als dit mislukt. Een ASP.NET MVC-toepassing zou dit doen met behulp van dezelfde code:
int hoeveelheid; if (! int.TryParse (Request.QueryString ["qty"], out quantity)) return RedirectToAction ("Invalid");
Hiermee wordt geprobeerd de tekenreeks naar een geheel getal te converteren. Als de conversie mislukt, wordt de code omgeleid naar een actie die een ongeldig bericht weergeeft.
Dit kan worden voorkomen als we op zoek zijn naar een integer-parameter. Het zou niet helpen als we tekst verwachten zoals in de eerdere productzoeker. De voorkeurstechniek in dit geval is om reguliere expressies of tekenreeksvervangingen te gebruiken om alleen benodigde tekens in de gepasste waarde toe te staan.
Dit kan worden gedaan door op een witte lijst het proces van verwijderen van andere tekens dan een opgegeven set of blacklisting, het proces waarbij leden van een opgegeven set uit de tekenreeks worden verwijderd. Whitelisting is betrouwbaarder omdat u alleen toegestane tekens opgeeft. Om iets anders dan letters en cijfers in een string te verwijderen, kunnen we code gebruiken zoals:
Regex regEx = nieuwe Regex ("[^ a-zA-Z0-9 -]"); string filteredString = regEx (originalString, "");
U moet elke invoer evalueren. Sommige databasequery's hebben mogelijk meer gespecialiseerde aandacht nodig. Neem tekens die zinvol kunnen zijn in een SQL-opdracht, maar die ook een geldig teken kunnen zijn in een databaseaanroep. Het enkele aanhalingsteken 'wordt bijvoorbeeld gebruikt om een tekenreeks in SQL te starten en te voltooien, maar kan ook deel uitmaken van de naam van een persoon, zoals O'Conner. In dit geval het vervangen van het enkele citaat '
met opeenvolgende enkele aanhalingstekens "
kan het probleem oplossen.
Opgeslagen procedures worden vaak gezien als de oplossing voor dit probleem en ze kunnen deel uitmaken van de oplossing. Een slecht geschreven opgeslagen procedure zal u echter niet redden. Neem deze opgeslagen procedure met code die lijkt op wat we al hebben gezien om een query te maken:
ALTER PROCEDURE [dbo]. [SearchProducts] @searchterm VARCHAR (50) = "AS BEGIN DECLARE @query VARCHAR (100) SET @query = 'SELECT * FROM Producten WHERE productnaam LIKE"%' + @searchterm + '% "; EXEC (@query) END
De string-aaneenschakeling hier is het probleem. Laten we een eenvoudige poging proberen, waarbij we proberen een altijd ware voorwaarde in te stellen om alle rijen in de tabel weer te geven en in dezelfde 'widget' OF 1 = 1; - "door te geven als de query die we eerder hebben gezien. :
| productid | productnaam | prijs | | - | 1 | Widget | 100.00 | | 2 | Thingy | 50.00 | | 3 | Boxy | 125.00 |
Als niet-vertrouwde gegevens worden doorgegeven, hebben we hetzelfde resultaat als wanneer het gesprek in onze code was gemaakt. Dat de string-aaneenschakeling plaatsvindt in een opgeslagen procedure in plaats van in onze code biedt geen bescherming.
Het volgende stukje van de puzzel om te beschermen tegen injectieaanvallen komt in het gebruik van parametrisering. SQL-queries bouwen door strings aaneen te schakelen en vervolgens de voltooide code door te geven geeft de database geen idee van welk deel van de tekenreeks een parameter is en wat deel uitmaakt van de opdracht. We kunnen u helpen beschermen tegen aanvallen door SQL-aanroepen te maken op een manier die uitspraken en waarden gescheiden houdt.
We kunnen de eerder opgeslagen opgeslagen procedure herschrijven om parameters te gebruiken en een veiligere oproep te produceren. In plaats van het samenvoegen van de %
tekens die jokertekens vertegenwoordigen, maken we een nieuwe tekenreeks die deze tekens toevoegt en vervolgens geeft deze nieuwe tekenreeks door aan de SQL-instructie. De nieuwe opgeslagen procedure ziet er als volgt uit:
ALTER PROCEDURE [dbo]. [SearchProductsFixed] @searchterm NVARCHAR (50) = "AS BEGIN DECLARE @query NVARCHAR (100) DECLARE @msearch NVARCHAR (55) SET @ msearch = '%' + @searchterm + '%' SET @query = 'SELECT * FROM Producten WHERE productnaam LIKE @search' EXEC sp_executesql @query, N '@ search VARCHAR (55)', @msearch END
Het uitvoeren van deze opgeslagen procedure met alleen de woordwidget werkt zoals verwacht.
| productid | productnaam | prijs | ---- | 1 | Widget | 100.00
En als we met onze parameterwidget "OR 1 = 1; passeren", resulteert dit in het feit dat niets wordt teruggezonden, wat aangeeft dat we niet langer kwetsbaar zijn voor deze aanval.
Parametrering vereist geen opgeslagen procedures. U kunt er ook gebruik van maken met queries die binnen de code zijn gebouwd. Hier is een kort segment in C # om verbinding te maken en een query uit te voeren tegen Microsoft SQL-server met behulp van een parameter.
const string sql = "SELECT * FROM Producten WHERE productnaam LIKE @CategoryID"; var connString = WebConfigurationManager.ConnectionStrings ["ProductDatabase"]. ConnectionString; using (var conn = new SqlConnection (connString)) var command = new SqlCommand (sql, conn); command.Parameters.Add ("@ searchterm", SqlDbType.NVarChar) .Value = string.Format ("% 0%", searchTerm); command.Connection.Open (); SqlDataReader-reader = command.ExecuteReader (); while (reader.NextResult ()) // Uit te voeren code op elke regel gaat hier
Tot nu toe heb ik laten zien hoe je database-aanvallen kunt verminderen. Een andere belangrijke verdedigingslaag minimaliseert de schadeoorzaken in het geval de aanvaller voorbij de andere verdedigingen komt. Het concept 'least privilege' geeft aan dat een module met code die in dit geval onze databaseaanroepen is, alleen toegang zou moeten hebben tot de informatie en bronnen die nodig zijn voor zijn doeleinden.
Wanneer een databaseopdracht wordt uitgevoerd, gebeurt dit onder de rechten van een gebruikersaccount. We krijgen beveiliging door het account te geven dat de databaseaanroepen alleen worden uitgevoerd onder de rechten om dingen te doen die normaal gesproken moeten worden gedaan. Als de oproepen uit een database alleen gegevens uit een tabel moeten lezen, geef dan alleen de accountkeuzerechten aan de tabel en voeg ze niet toe of verwijder ze. Als er specifieke tabellen zijn, moet deze worden bijgewerkt, bijvoorbeeld een tabel met orders, en moet deze worden toegevoegd en bijgewerkt naar die tabel, maar niet naar andere tabellen, zoals een tabel met gebruikersinformatie..
Het annuleren van bestellingen kan worden afgehandeld via een afzonderlijk account dat alleen wordt gebruikt op de pagina die deze taak uitvoert. Dit zou voorkomen dat het verwijderen van een bestelling uit de tabel elders moeilijker wordt. Het gebruik van een afzonderlijk database-account voor de administratieve functies van de site, met de noodzakelijke grotere rechten dan die het publieke gebruik kan doen om schade te voorkomen van een gebruiker die een open injectie-aanval ontdekt.
Dit voorkomt niet alle aanvallen. Het zou niets doen om te voorkomen dat extra resultaten worden geretourneerd, zoals het eerdere voorbeeld dat de volledige inhoud van een tabel weergeeft. Hiermee wordt voorkomen dat aanvallen gegevens bijwerken of verwijderen.
SQL-injectie is de gevaarlijkste aanval, vooral wanneer rekening wordt gehouden met de kwetsbaarheid van websites en het potentieel van dit soort aanvallen om veel schade aan te richten. Hier heb ik SQL-injectieaanvallen beschreven en aangetoond welke schade iemand kan aanrichten. Gelukkig is het niet zo moeilijk om uw webprojecten te beschermen tegen dit beveiligingslek door een paar eenvoudige regels te volgen.
Vertrouw nooit op externe gegevens die in uw toepassing worden binnengebracht. Het moet worden gevalideerd tegen een witte lijst met geldige invoer voordat het verder wordt verwerkt. Dit kan betekenen dat ervoor wordt gezorgd dat een integer-parameter eigenlijk een geheel getal is of dat een datum een geldige datumwaarde is. Bevestig ook de tekst om alleen de tekens op te nemen die de parameter nodig heeft. Voor tekst zoeken kunt u vaak alleen letters en cijfers toestaan en interpunctie uitsluiten die problematisch kan zijn, zoals het gelijkteken of een puntkomma.
Gebruik parameterisatie en vermijd reeksaaneenschakeling bij het maken van SQL-aanroepen. Opgeslagen procedures zijn geen wondermiddel, omdat ze ook kwetsbaar kunnen zijn als eenvoudige reeksaaneenschakeling wordt gebruikt. Parametrering vermijdt veel van de problemen van string concatenatie.
De toegang tot de database moet worden uitgevoerd met de minste rechten die nodig zijn om de benodigde taken uit te voeren. In enkele omstandigheden moeten de databaseaanroepen die door de webtoepassing worden gebruikt wijzigingen in de databasestructuur aanbrengen, zoals het verwijderen of wijzigen van tabellen. U kunt een extra beveiligingslaag toevoegen door afzonderlijke delen van de website onder verschillende accounts uit te voeren. Het databaseaccount dat wordt gebruikt voor normale gebruikersacties heeft waarschijnlijk geen reden om de tabel met de rollen of rechten van gebruikers aan te passen. Het uitvoeren van de administratieve delen van de site onder een meer geprivilegieerde account en het gedeelte met de eindgebruiker onder een minder bevoorrechte account kan veel doen om de kans te verkleinen dat code doorbreekt en andere problemen veroorzaakt.