Kaip saugos nuskaitymas pakeitė mano požiūrį į saugaus kodo rašymą

Prasidėjo kaip bet kuri įprasta diena.
Paraiška buvo stabili. Funkcijos buvo pristatytos laiku. QA buvo pasirašyta. Iš išorės viskas atrodė baigta.
Tada atėjo saugumo nuskaitymo ataskaita.
Radinių puslapiai.
Didelis sunkumas. Vidutinio sunkumo. Mažas sunkumas.
Niekas nebuvo sulaužyta. Jokie įspėjimai nebuvo šaudomi. Nė vienas naudotojas nepranešė apie problemas.
Tačiau ataskaitoje buvo pasakyta kitokia istorija – ne apie tai, kas nepavyko, o apie tai kas gali nepavykti.
Tai buvo akimirka, kai supratau kai ką svarbaus:
Saugumas nėra susijęs su tuo, kas šiandien nutrūksta.
Kalbama apie tai, ką užpuolikai gali išnaudoti rytoj.


Kai „darbo kodas“ nėra saugus kodas
Kaip ir daugelis kūrėjų, aš kažkada tikėjau, kad jei programa veikia ir išlaikė testus, ji yra pakankamai gera.
Saugumo analizė paneigė šią prielaidą.
Jis nenuėjo laimingu keliu.
Buvo ieškoma piktnaudžiavimo, piktnaudžiavimo ir kraštų atvejų:
- Kas atsitiks, jei įvestis yra kenkėjiška?
- Ką daryti, jei žetonas nutekėjo?
- Ką daryti, jei priklausomybė yra pažeista?
Staiga „dirbti“ nebebuvo tas pats, kas „saugu“.
1 . Pirmasis pažadinimo skambutis: kelių svetainių scenarijus (XSS)
Viena iš pirmųjų problemų buvo DOM pagrįstas XSS pažeidžiamumas.
Aiškiai prisiminiau kodą. Tai tiesiog pateikė vartotojo įvestį į vartotojo sąsają. Logika buvo švari. Funkcija veikė tiksliai taip, kaip tikėtasi.
Bet nuskaitymas parodė kitą pusę:
- Sukurtas naudingasis krovinys
- Į DOM įvestas scenarijus
- Kodo vykdymas kito vartotojo naršyklėje
Tai, kas atrodė nekenksminga, tapo seanso vagystės ir duomenų atskleidimo įėjimo tašku.


Ką Išmokau
- Vartotojo įvestis niekada nėra patikima – net tada, kai ji atrodo nekenksminga.
- pavojinglySetInnerHTML turėtų būti naudojamas tik su išvalytu turiniu
- Kai būtinas HTML atvaizdavimas, visada naudokite valymo biblioteką, pvz., DOMPurify
Kas pasikeitė
- Neapdoroto HTML pateikimo buvo išvengta arba jis buvo išvalytas
- Išvestis buvo pašalinta pagal numatytuosius nustatymus
- Įvesties patvirtinimas buvo ir priekinėje, ir užpakalinėje sistemoje
Esmė:
„React“ apsaugo jus pagal numatytuosius nustatymus normaliam teksto atvaizdavimui. Naudokite DOMpurify, kai reikia pateikti HTML arba kaip papildomą saugos sluoksnį, kai siunčiate duomenis į užpakalinę programą. Atminkite: patvirtinimas tikrina formatą, dezinfekavimas pašalina grėsmes.
2 . Įpurškimo atakos: užpakalinės realybės patikrinimas
Toliau atsirado injekcijos pažeidžiamumas.
Priekinė dalis turėjo patvirtinimus. Įvesties duomenys buvo apriboti. Klaidos buvo tvarkomos.
Tačiau užpuolikas niekada nenaudoja vartotojo sąsajos.
Nuskaitymas buvo sutelktas į užpakalinę programą – ten, kur vartotojo įvestis atitinka duomenų bazę.
Pažeidžiamas kodas (Python):
# Vartotojas pateikia vartotojo vardą ir slaptažodį
vartotojo vardas = request.form.get(„naudotojo vardas”)
slaptažodis = request.form.get („slaptažodis”)
# Kurti SQL užklausą su eilučių formatavimu – PAVOJINGA!
query = f”SELECT * FROM users WHERE vartotojo vardas=”{naudotojo vardas}” IR slaptažodis=”{slaptažodis}”
cursor.execute(query)
Kas gali suklysti:
Užpuolikas įveda kaip vartotojo vardą:
‘admin’ AR ‘1’=’1
Gauta užklausa tampa tokia:
SELECT * FROM user
Kadangi ‘1’=’1′ visada teisinga, užpuolikas visiškai apeina autentifikavimą ir gauna prieigą prie administratoriaus paskyros.
Saugus pataisymas:
# Parametrų užklausų naudojimas – SAUGU!
vartotojo vardas = request.form.get(„naudotojo vardas”)
slaptažodis = request.form.get („slaptažodis”)
# Vietos žymos (%s) atskiria kodą nuo duomenų
query = „Pasirinkti * IŠ vartotojų WHERE vartotojo vardas =%s IR slaptažodis =%s”
cursor.execute(query, (naudotojo vardas, slaptažodis))
Kodėl tai veikia:
Parametrizuotos užklausos vartotojo įvestį traktuoja kaip duomenis, o ne vykdomąjį kodą. Net jei užpuolikas bando admin’ ARBA ‘1’=’1, duomenų bazė ieško vartotojo, pažodžiui pavadinto „admin” ARBA „1’=’1”, o ne vykdydama kenkėjišką logiką.
Pavyzdys su FastAPI:


Tai buvo pamaina:
Frontend patvirtinimas pagerina patirtį.
Backend patvirtinimas apsaugo sistemas.
Pataisymas
- Parametrizuotos užklausos pakeitė dinamines
- ORM buvo naudojami tinkamai, o ne apeiti
- Prieiga prie duomenų bazės buvo apribota iki to, kas buvo griežtai reikalinga
Injekcijos atakos nustojo būti teorinės rizikos ir tapo konkrečiomis grėsmėmis.
3 . Rizika, kurios nerašiau: priklausomybės
Kai kurių pažeidžiamumų mano kode visai nebuvo.
Jie gyveno trečiųjų šalių bibliotekose – giliai priklausomybės medyje.
Pasenusios pakuotės. Neprižiūrimi moduliai. Žinomos saugumo problemos.
Tai buvo sunkiausia pamoka:
Šiuolaikinės programos paveldi riziką dėl savo priklausomybių.
Nauji įpročiai
- Reguliarus priklausomybės auditas
- Nenaudojamų pakuočių pašalinimas
- Atnaujinimų traktavimas kaip saugos užduotis, o ne pasirenkama priežiūra
4 . Neteisinga saugumo konfigūracija: nematoma silpnybė
Derinimo vėliavėlės gamyboje.
Pernelyg leistinos CORS taisyklės.
Išsamūs klaidų pranešimai.
Nė vienas iš jų nesukėlė greitų gedimų.
Visi jie praplėtė puolimo paviršių.
Konfigūraciją peržiūrėjau taip pat rimtai kaip kodą.
Ko iš tikrųjų moko saugos nuskaitymas
Iš pradžių nuskaitymas atrodė kaip kritika.
Laikui bėgant tai atrodė labiau kaip nurodymas.
Kiekvienas įvesties laukas.
Kiekvienas API galutinis taškas.
Kiekviena priklausomybė.
Kiekviena konfigūracija.
Kiekvienas iš jų gali sumažinti riziką arba tyliai ją pristatyti.
Tai nekaltino.
Tai atskleidė prielaidas.
Jis išryškino akląsias vietas.
Saugumo nuskaitymas verčia kūrėjus mąstyti kaip užpuolikams.
Šis saugos nuskaitymas pakeitė kodo rašymo būdą.
Ne todėl, kad rado problemų –
bet todėl, kad pasikeitė mano mąstymas.


