GraphQL injekcijos atakos

Kas yra GraphQL ir kodėl jis tapo tokia populiaria taikymo programavimo sąsaja?

GraphQL – tai užklausų kalba, kurią 2012 metais sukūrė Facebook inžinieriai, siekdami išspręsti REST API apribojimus. Skirtingai nei tradicinės REST sąsajos, kur reikia daryti kelis užklausimus į skirtingus endpoint’us, GraphQL leidžia gauti visus reikiamus duomenis vienu užklausimu. Tai tarsi galimybė užsisakyti tiksliai tokį patiekalą, kokio norite, vietoj to, kad gautumėte standartinį meniu rinkinį.

Pastaraisiais metais GraphQL tapo itin populiarus tarp kūrėjų, ypač dideliuose projektuose, kur duomenų struktūros yra sudėtingos. Jį naudoja tokios kompanijos kaip GitHub, Shopify, Twitter, Netflix ir daugelis kitų. Tačiau kaip ir bet kuri technologija, GraphQL turi savo saugumo iššūkius. Vienas iš pavojingiausių – GraphQL injection atakos, kurios gali sukelti rimtų pasekmių jūsų aplikacijai.

GraphQL injection esmė: kaip veikia šios atakos

GraphQL injection atakos yra panašios į klasikines SQL injection atakas, tik pritaikytos GraphQL kontekstui. Pagrindinis principas – piktavalis bando įterpti kenkėjišką kodą į GraphQL užklausas, tikėdamasis, kad serveris juos apdoros ir suteiks neteisėtą prieigą prie duomenų arba leis atlikti neleistinas operacijas.

Problema dažniausiai kyla tada, kai kūrėjai tiesiogiai įterpia vartotojo įvestus duomenis į GraphQL užklausas, tinkamai jų nevaliduodami. Pavyzdžiui, jei turite paieškos funkciją, kuri priima vartotojo įvestą tekstą ir jį tiesiogiai perduoda į GraphQL užklausą, atakuojantysis gali bandyti manipuliuoti šia užklausa.

Skirtingai nei SQL injection, kur dažniausiai tikslas yra pakeisti duomenų bazės užklausas, GraphQL injection atakos gali būti įvairesnės. Jos gali apimti duomenų nutekėjimą per introspekciją, Denial of Service (DoS) atakas per sudėtingas užklausas, autentifikacijos apėjimą arba net privilegijų eskalavimą.

Konkretūs GraphQL injection atakų pavyzdžiai

Pažiūrėkime į kelis realistiškus scenarijus, kaip šios atakos gali būti vykdomos. Tarkime, turite tokią GraphQL užklausą:


query {
user(id: "123") {
name
email
}
}

Jei šis ID yra gaunamas iš vartotojo įvesties ir nėra tinkamai validuojamas, atakuojantysis galėtų pabandyti įterpti papildomus laukus arba net visiškai pakeisti užklausos struktūrą. Pavyzdžiui:


query {
user(id: "123") {
name
email
password
creditCard
ssn
}
}

Kitas pavojingas scenarijus – batch užklausos. GraphQL leidžia siųsti kelias užklausas vienu metu, o tai gali būti išnaudota DoS atakoms. Atakuojantysis gali sukurti užklausą, kuri reikalauja milžiniškų skaičiavimo resursų:


query {
users {
posts {
comments {
author {
posts {
comments {
author {
posts {
# ir taip toliau...
}
}
}
}
}
}
}
}
}

Tokia užklausa gali užkrauti serverį ir padaryti jį nepasiekiamą kitiems vartotojams. Tai vadinama „circular query” arba „deep nesting” ataka.

Introspection kaip potenciali saugumo spraga

Viena iš GraphQL savybių, kuri gali tapti saugumo problema, yra introspection. Tai mechanizmas, leidžiantis užklausti pačią GraphQL schemą ir sužinoti, kokie tipai, laukai ir operacijos yra prieinami. Kūrimo aplinkoje tai neįtikėtinai naudinga funkcija, bet produkcinėje aplinkoje ji gali atskleisti per daug informacijos potencialiems atakuojantiesiems.

Atakuojantysis gali naudoti introspection užklausas, kad sužinotų visą jūsų API struktūrą:


query {
__schema {
types {
name
fields {
name
type {
name
}
}
}
}
}

Gavęs šią informaciją, jis gali tiksliai sužinoti, kokie duomenys yra prieinami, kokie laukai egzistuoja ir kaip jie susiję tarpusavyje. Tai tarsi gauti detalų pastato planą prieš bandant jį apiplėšti.

Daugelis saugumo ekspertų rekomenduoja išjungti introspection produkcinėje aplinkoje arba bent jau apriboti prieigą prie jos tik autentifikuotiems ir autorizuotiems vartotojams. Tačiau statistika rodo, kad dauguma GraphQL API vis dar palieka introspection įjungtą, nes kūrėjai nežino apie šią riziką arba tiesiog pamiršta ją išjungti.

Autentifikacijos ir autorizacijos spragos GraphQL kontekste

Viena dažniausiai pasitaikančių klaidų – netinkamas autentifikacijos ir autorizacijos įgyvendinimas GraphQL API. Skirtingai nei REST, kur kiekvienas endpoint’as gali turėti savo autorizacijos logiką, GraphQL dažnai turi vieną įėjimo tašką. Tai reiškia, kad autorizacijos logika turi būti įgyvendinta kiekviename resolver’yje atskirai.

Problema kyla tada, kai kūrėjai įgyvendina autentifikaciją tik GraphQL sluoksnyje, bet pamiršta patikrinti leidimus kiekviename atskirame lauke. Pavyzdžiui, vartotojas gali būti autentifikuotas, bet tai nereiškia, kad jis turėtų matyti visų kitų vartotojų slaptažodžius ar finansinę informaciją.

Klasikinis pavyzdys – kai kūrėjas apsaugo pagrindinę užklausą, bet pamiršta apsaugoti įdėtus (nested) laukus:


query {
publicPosts {
title
content
author {
email # Šis laukas turėtų būti apsaugotas!
privateNotes # Ir šis taip pat!
}
}
}

Net jei `publicPosts` yra prieinami visiems, `author.email` ir `author.privateNotes` turėtų būti prieinami tik tam pačiam vartotojui arba administratoriams. Jei autorizacija nepatikrinama kiekviename lygyje, tai tampa rimta saugumo spraga.

Praktiniai apsisaugojimo būdai ir rekomendacijos

Dabar pereikime prie konkretesnių rekomendacijų, kaip apsisaugoti nuo GraphQL injection atakų. Pirmas ir svarbiausias žingsnis – niekada neformuokite GraphQL užklausų tiesiogiai iš vartotojo įvesties. Visada naudokite parametrizuotas užklausas su kintamaisiais (variables).

Vietoj to, kad darytumėte taip:


const query = `query { user(id: "${userInput}") { name } }`;

Darykite taip:


const query = `query($id: ID!) { user(id: $id) { name } }`;
const variables = { id: userInput };

Antra, įdiekite užklausų sudėtingumo ribojimus. Daugelis GraphQL bibliotekų palaiko mechanizmus, leidžiančius apriboti užklausų gylį, plotį ir sudėtingumą. Pavyzdžiui, galite nustatyti maksimalų įdėjimo gylį (depth limit) į 5-7 lygius, o tai užkirs kelią circular query atakoms.

Trečia, įgyvendinkite rate limiting. Net jei atakuojantysis negali sukurti vienos labai sudėtingos užklausos, jis gali bandyti siųsti daug paprastų užklausų. Rate limiting pagal IP adresą arba vartotojo ID gali padėti apsisaugoti nuo tokių atakų.

Ketvirta, išjunkite introspection produkcinėje aplinkoje arba apribokite prieigą prie jos. Daugelyje GraphQL bibliotekų tai galima padaryti viena konfigūracijos eilute. Pavyzdžiui, Apollo Server:


const server = new ApolloServer({
schema,
introspection: process.env.NODE_ENV !== 'production'
});

Penkta, įgyvendinkite tinkamą autorizacijos logiką kiekviename resolver’yje. Naudokite direktyvas arba middleware funkcijas, kurios automatiškai patikrins leidimus prieš grąžinant duomenis. Pavyzdžiui, galite sukurti `@auth` direktyvą, kurią pridėsite prie jautrių laukų.

Testavimas ir saugumo auditas

Apsauga nuo GraphQL injection atakų nėra vienkartinis veiksmas – tai nuolatinis procesas. Reguliariai turėtumėte testuoti savo API ieškodami potencialių spragų. Yra keletas įrankių, kurie gali padėti automatizuoti šį procesą.

InQL ir GraphQL Cop yra populiarūs įrankiai, skirti GraphQL API saugumo testavimui. Jie gali automatiškai atlikti introspection, ieškoti įprastų saugumo spragų ir net bandyti atlikti injection atakas kontroliuojamoje aplinkoje.

Taip pat verta naudoti SAST (Static Application Security Testing) įrankius, kurie gali analizuoti jūsų kodą ir identifikuoti potencialius saugumo trūkumus dar prieš jį išleidžiant į produkciją. Kai kurie populiarūs įrankiai, palaikantys GraphQL: Snyk, SonarQube ir Checkmarx.

Nepamiršite ir penetracijos testavimo. Samdyti profesionalius saugumo specialistus, kurie bandytų įsilaužti į jūsų sistemą, gali atrodyti brangiai, bet tai daug pigiau nei realios duomenų nutekėjimo pasekmės. Jei biudžetas ribotas, galite pradėti nuo bug bounty programų, kur saugumo tyrinėtojai ieško spragų už atlygį.

Monitoringas ir reagavimas į incidentus

Net ir su visomis apsaugos priemonėmis, visada yra galimybė, kad atakuojantysis ras būdą įsilaužti. Todėl svarbu turėti tinkamą monitoringo sistemą, kuri padėtų greitai aptikti įtartinas veiklas.

Stebėkite neįprastus užklausų modelius: staigius užklausų skaičiaus padidėjimus, neįprastingai sudėtingas užklausas, bandymus pasiekti neegzistuojančius laukus ar tipus. Šie signalai gali rodyti vykstančią ataką.

Naudokite logging sistemą, kuri fiksuotų visas GraphQL užklausas kartu su metaduomenimis: kas darė užklausą, kada, iš kokio IP adreso, kokia buvo užklausos sudėtingumas ir kiek laiko užtruko jos vykdymas. Ši informacija bus neįkainojama, jei reikės tirti saugumo incidentą.

Turėkite parengtą incidentų reagavimo planą. Kas bus atsakingas už reagavimą? Kaip greitai galite atjungti API, jei aptinkate ataką? Kaip informuosite vartotojus, jei įvyko duomenų nutekėjimas? Šie klausimai turėtų būti išspręsti iš anksto, o ne krizės metu.

Ateities perspektyvos ir besivystančios grėsmės

GraphQL technologija nuolat vystosi, o kartu su ja atsiranda ir naujų saugumo iššūkių. Vienas iš naujausių rūpesčių – GraphQL federation, kai kelios GraphQL schemos yra sujungiamos į vieną. Tai sukuria papildomų sudėtingumo sluoksnių ir potencialių saugumo spragų.

Kitas augantis rūpestis – automatizuotos atakos. Atakuojantieji pradeda naudoti dirbtinį intelektą ir mašininį mokymąsi, kad automatiškai rastų ir išnaudotų GraphQL saugumo spragas. Tai reiškia, kad apsaugos mechanizmai taip pat turi būti vis sudėtingesni ir protingesni.

Geros naujienos yra tai, kad GraphQL bendruomenė aktyviai dirba ties saugumo klausimais. Atsiranda naujų bibliotekų ir įrankių, kurie padeda lengviau įgyvendinti saugumo geriausias praktikas. Pavyzdžiui, GraphQL Shield leidžia deklaratyviai apibrėžti autorizacijos taisykles, o GraphQL Armor suteikia įvairių apsaugos mechanizmų rinkinį.

Svarbu sekti GraphQL saugumo naujienas ir reguliariai atnaujinti savo žinias. Dalyvaukite konferencijose, skaitykite saugumo tyrinėjimus, bendraujate su bendruomene. Saugumas nėra statiškas dalykas – tai nuolatinė kova tarp atakuojančiųjų ir ginančiųjų.

Taip pat verta paminėti, kad reguliavimo institucijos pradeda skirti daugiau dėmesio API saugumui. GDPR, CCPA ir kiti duomenų apsaugos įstatymai reiškia, kad netinkamas duomenų apsaugojimas gali baigtis ne tik techninėmis problemomis, bet ir didelėmis baudomis. Investicija į saugumą nėra tik techninė būtinybė – tai ir verslo būtinybė.

Baigiant, GraphQL injection atakos yra reali grėsmė, bet su tinkamomis žiniomis ir įrankiais jų galima išvengti. Svarbiausias dalykas – niekada neprisiimti, kad jūsų API yra saugus. Visada testuokite, stebėkite ir tobulinkite savo saugumo praktikas. Kaip sakoma, geriau būti paranojišku ir saugiam, nei atsipalaidavusiam ir nulaužtam.

Daugiau

Moon.js: monorepo kūrimo sistema