Pisutades eksemplaride String
sisemist sisu, on teil oht oma rakendust tõsiselt rikkuda.
Esimene põhjus on see, et String
eksemplarid peaksid olema muutumatud , mis tähendab, et eksemplare võidakse uuesti kasutada; "oma" stringi modifitseerimisel võite tegelikult muuta ka muid stressi, mis on kontseptuaalselt erinevad, kuid millel on juhtumisi sama sisu. Sellist taaskasutust võib juhtuda ka sisemiselt, kui String
-i eksemplarid viitavad selle massiivi tüki piiritlemiseks aluseks olevale char []
-le koos paari indeksiga. Lisateavet leiate sellelt lehelt. Üldiselt tugineb kood, mis kasutab String
-i eksemplare, nende muutumatusele ja selle invariandi purustamine võib viia kaugeleulatuvate ebameeldivate tagajärgedeni.
Teine põhjus on see, et String eksemplare pole dokumenteeritud ja võib muutuda . Tegelikult tegid nad seda juba mitu korda. Kui arvestada ainult Sun / Oracle JVM-i (juba julge samm, kuna seal on ka teisi JVM-e, nt IBMi), siis saavad Java 6 versioonid (alates värskendusest 21) kasutada tihendatud stringe, mis tähendab, et char []
teisendatakse automaatselt byte []
-iks, kui tähemärgid juhtuvad kõigi vahemikku 0..255 (st kõik märgid on tõesti osa ladina-1 -st). "Kokkusurutud stringid" olid mõeldud tippmarkide saamiseks mõnes võrdlusaluses, kuid lükati need hiljem maha (Java 7-l neid pole). Kuid see on piisav, et näidata, et sisemälu formaat võib ilma eelneva teatamiseta muutuda. Ja nad tegid seda uuesti Java 7 värskenduses 6.
Seega võib alternatiivse JVM-i kasutamine või JVM-i lihtsalt uuemale versioonile värskendamine (nagu on väga soovitatav, kui on turvaauke, mida parandada) võib koodi täielikult rikkuda, võib-olla vaikselt , st et teie andmed hävitavad puhta erandi asemel, mis lihtsalt tapab teie rakenduse. See on ebasoovitav, nii et ärge tehke seda. Te ei saa usaldusväärselt muheleda, kuidas String
-i eksemplarid on sisemiselt korraldatud. Vahemärkusena võib öelda, et privaatsetele väljadele juurdepääs ei ole Java-aplettide jaoks ka tegelikult otstarbekas variant (te ei saa seda teha näiteks allkirjastamata apletiga).
Kolmas põhjus ja võib-olla kõige veenvam kolm, on see, et tundlike väärtuste ülekirjutamine mälus ei tööta Java-s (usaldusväärselt) . Selle teadmiseks peate mõistma, kuidas prügivedu algoritmid töötavad ( see artikkel on väga kena sissejuhatus põhitõdedesse). Programmeerija seisukohast on asjad lihtsad: objekt on eraldatud, istub seal RAM-is ja kui rakenduskood ei viita sellele, nõuab GC mälu tagasi. Sisemiselt võivad asjad siiski erineda. Eelkõige kipuvad kõige tõhusamad GC algoritmid mälus objekte liigutama, st kopeerivad neid tõepoolest ühest kohast teise. See on teie koodile nähtamatu, kuna GC kohandab viiteid: kuna Java on tipitud, ei saa te tähele panna, et osuti sisemine esitus on muutunud (te ei saa näiteks viidet täisarvule heita). Selline kopeerimine võimaldab kiiremat GC tööd ja paremat asukohta (vahemälude osas). See aga tähendab, et teie väärtuslike andmete mitu eksemplari võivad mujal RAM-is püsida, täiesti teie käeulatusest väljas. Isegi kui saaksite oma String
sisu usaldusväärselt üle kirjutada, mõjutaks see ainult selle eksemplari praegust salvestusala, jättes selle kummituseksemplarid puutumata.
(Sun / Oracle JVM-is ilmus objekte sisemiselt kopeeriv GC Java 1.3 ümber. Seda võib näha nende raamatukogu koodi kujunduses; vana kood kasutas paroolide jaoks char []
vältida automaatset taaskasutamist, mis võib juhtuda String
-ga, ja edendada käsitsi ülekirjutamist; uuem kood kasutab String
-i, kuna raamatukogu kujundajad said aru, et see ülekirjutamine pole nagunii usaldusväärne.)
Kas see tähendab, et Java on oma olemuselt ebakindel? Ei, sest tundlike andmete mällu ülekirjutamise tähtsus on tugevalt liialdatud . Idee, et peate paroolid ja võtmed üle kirjutama, on üks neist päritud dogmadest: midagi, mis oli konkreetse juhtumi jaoks juba ammu asjakohane, kuid mida rakendavad ja rakendavad nüüd paljud inimesed, kes saavad seda jumaliku tarkusena ja ei saa aru, mis see on tegelikult umbes. Mälu ülekirjutamine on ohustatud süsteemides töötava rakenduskoodi jaoks tore asi, kui ründajad pole eriti pädevad: stsenaarium on keskmine koduomanik, kellel on pahavara täis arvuti. Pahavara kontrollib masinat täielikult, kuid olles lihtne automatiseeritud koodijupp, ei kasuta see seda juhtimist tegelikult ära; pahavara otsib RAM-i lihtsalt tähemärkide järjestuste järgi, mis näevad välja nagu krediitkaardi andmed. Nii et me räägime hukule määratud kliendisüsteemidest, mis suudavad ellu jääda ainult seetõttu, et ründajad eelistavad seda niimoodi, ning andmete püüdmist võib (potentsiaalselt) leevendada tundlike andmete kiire ülekirjutamine ainult seetõttu, et pahavarat kontrollivad inimründajad lihtsalt ei tee seda on aega huvitavate bittide väljavõtmiseks korralikult tööd teha ja peate selle asemel lootma kõige jõhkramatele täismälu skannimistele.
Ükski see ei kehti serverirakenduse ega kliendi koodi kohta, mis tegeleb saladustega, mille väärtus ei ole tühine. Kui pahatahtlik ründaja on võimeline otsima RAM-i tundlike andmete jaoks ja need andmed on väärt inimründaja selget tähelepanu 1 või 2 minutit, siis ükski ülekirjutamine ei päästa teid. Seega on paljudes kontekstides, kus turvalisus on oluline, paroolide ja võtmete ülekirjutamine lihtsalt raisatud pingutus, mis annab turvatunde , kuid ei paranda tegelikult asju (ehkki audiitorite austamine võib olla mugav).
Probleemi raskendab asjaolu, et kui teie tundlikud andmed ilmuvad teie Java-koodis, on need juba läbinud mitmesugused kihid, mis pole teie käeulatuses. Näiteks kui loete parooli failist, siis jäävad selle koopiad kerneli vahemäluks kasutatavasse RAM-i ja võib-olla üks või kaks Java-i poolt hooldatavat põrgapuhvrit emakeelse maailma ja Java pakutava abstraktsiooni vahel. Kui parool saadi võrgult SSL-i kaudu, läbis parool uuesti SSL-i teegi sisemise puhverdamise, mida te ei saa kontrollida. Kui me räägime kliendirakendusest ja parooli kirjutas lihtsalt kasutaja, siis käivitab iga pahavara, mis suudab mälu skannida, ka klahvilogerit ja sai parooli juba enne, kui see teie koodini jõudis.
Seetõttu, nagu kokkuvõte: ei, peegelduse kasutamine parooli mällu ülekirjutamiseks EI paranda turvalisust. See muudab teie koodi rikkumise tõenäolisemaks (isegi JVM-i lihtsa väiksema värskenduse korral), kuid ei paku reaalset käegakatsutavat turvalisuse kasvu. Nii et ärge tehke seda.
Märkus: me rääkisime siin Java-st, kuid kõik ülaltoodu kehtib võrdselt enamiku teiste programmeerimiskeelte ja -raamistike kohta, sealhulgas .NET (C #), PHP, Ruby, Node.js, Python, Mine ... Kui soovite tõesti delikaatsete andmete üle silma peal hoida, peate kasutama keelt, mis on piisavalt lähedal paljale metallile (assamblee, C, Forth) ja järgige seda kogu süsteemis, kaasa arvatud baaside teegid, kernel ja seadme draiverid. Kui keskendute lihtsalt rakenduse koodile, on teil garanteeritud see, et te ei pea seda punkti.