Ons uitsluiting na vyf verkeerde wagwoorde het niemand ooit uitgesluit nie
Rekening-uitsluiting is 'n basiese vereiste. Vyf verkeerde wagwoorde en die rekening word vir tien minute gesluit, wat aanlyn wagwoord-raaiery van 'n werklike bedreiging in 'n nikswerd oefening verander: 'n aanvaller kry vyf pogings per rekening per tien minute, wat niks is nie. Ons het dit gehad. Die drempel was vyf, die venster was tien minute, die kode wat dit nagegaan het was korrek, en dit het elke keer wat ons dit getoets het perfek gewerk, want elke keer wat ons dit getoets het, het ons die verkeerde wagwoord vyf keer agtereenvolgens getik, een na die ander, soos 'n mens.
'n Aanvaller is nie 'n mens wat een na die ander tik nie. 'n Aanvaller stuur vyfhonderd gelyktydig. En toe ons dit gedoen het, het die teller wat veronderstel was om by vyf te stop skaars beweeg, en die rekening het nooit gesluit nie. Jy kon 'n onbeperkte aantal raaiskote na 'n enkele rekening gooi, en die een beheer wat ontwerp is om presies dit te stop het daar sit en niks doen nie, nie omdat die logika verkeerd was nie, maar omdat dit aangeneem het dit sal net ooit een vraag op 'n slag gevra word.
'n Teller wat tel verloor
Die mislukte-aanmeld-kode het die vanselfsprekende ding gedoen. By 'n verkeerde wagwoord het dit die gebruiker gelaai, een by hul mislukte-poging-telling getel, nagegaan of die nuwe waarde vyf bereik het, en die gebruiker teruggestoor. Lees, wysig, skryf. Uitgeskryf soos 'n mens daaraan sou dink, is dit korrek.
Uitgeskryf as twee versoeke wat op dieselfde oomblik aankom, is dit nie. Albei lees die telling, en albei lees dieselfde waarde, sê drie. Albei tel een by en kry vier. Albei skryf vier terug. Twee mislukte pogings het gebeur, en die telling het van drie na vier gegaan. Een van die inkremente het eenvoudig verdwyn, oorgeskryf deur die ander, want albei het vanaf dieselfde verouderde beginpunt gewerk, en die tweede skryf het bo-op die eerste geland asof die eerste nooit gebeur het nie.
Vuur nou vyfhonderd verkeerde wagwoorde gelyktydig af. Hulle stapel op mekaar op presies hierdie manier, elkeen lees 'n lae telling en skryf 'n effens minder lae telling, met inkremente wat by die honderd verdamp. Die teller dryf 'n bietjie op en stop nêrens naby vyf nie. Die uitsluitingsdrempel word nooit oorgesteek nie, nie omdat die kontrole verkeerd is nie, dit word elke enkele keer getrou nagegaan, maar omdat die getal wat dit nagaan 'n leuen is wat deur 'n skare versoeke saamgestel is wat almal mekaar oorskryf. Brute-krag-beskerming is verslaan, nie deur 'n logika-fout nie maar deur gelyktydigheid, wat die soort fout is wat elke reekstoets wat jy ooit sal skryf slaag en misluk die oomblik wat dit 'n werklike aanvaller ontmoet, wat die enigste party is met 'n motief om versoeke in parallel te stuur.
Waar hierdie fout gewoon het, maak saak. Dit was nie in een of ander stoorlaag begrawe nie. Dit was reg daar in die aanmeld-hanteerder, in volle sig, 'n ++ en 'n stoor, die mees natuurlike kode ter wêreld. Niks aan die lees daarvan dui op gevaar nie. Jy moet aan twee daarvan dink wat gelyktydig loop vir die fout om te verskyn, en om kode een reël op 'n slag te lees is presies die gemoedstoestand waarin jy dit nie doen nie.
Om 'n teller atomies te maak op 'n stoor wat nie kan tel nie
Die oplossing moet die hele lees-wysig-skryf ondeelbaar maak: geen ander poging mag tussen ons lees en ons skryf inglip nie. Die interessante deel is dat ons op 'n sleutel-waarde-stoor is sonder 'n inkrement-bewerking. Jy kan dit nie sê "tel een by hierdie veld" nie, wat die primitief is wat 'n databasis met atomiese tellers vir jou sou aangee. Jy kan net 'n ry lees en 'n ry skryf.
Wat die stoor jou wel gee, is 'n voorwaardelike skryf. Elke ry dra 'n weergawe-merker wat by elke skryf verander, en jy kan sê: skryf hierdie ry terug, maar net as die weergawe daarvan steeds die een is wat ek gelees het. As iemand anders intussen geskryf het, het die weergawe geskuif, en jou skryf word verwerp eerder as om stil-stil hulle s'n oor te skryf.
Dit verander die verlore-inkrement-wedloop in 'n gevangde botsing. Lees die ry en sy weergawe. Tel een by. Probeer terugskryf op voorwaarde van daardie weergawe. As dit slaag, het jou inkrement beslis bo-op die waarde wat jy gelees het geland, met niks verlore nie. As dit verwerp word, beteken dit 'n ander poging het jou voorgespring, so jy herlees die nou-hoër telling en probeer weer. Herhaal by verwerping, 'n begrensde handvol kere, en elke inkrement word gedwing om agter die vorige een te reeks in plaas daarvan om dit oor te skryf. Die telling word weer waar, en die vyfhonderd-gelyktydig-aanval stap nou die teller reguit na vyf en laat die slot afgaan, wat die hele punt is.
Ons het dieselfde vorm na ons ander stoor-agterkant gedra, wat ook nie 'n inkrement het nie maar ook 'n voorwaardelike skryf bied, hierdie keer gehek op die teller-waarde self eerder as 'n weergawe-merker: skryf die nuwe telling net as die gestoorde telling steeds is wat ek gelees het. Dieselfde waarborg, uitgedruk in daardie stoor se woordeskat. Die les veralgemeen verby enige enkele databasis: wanneer jy 'n atomiese inkrement kortkom, herbou 'n voorwaardelike skryf plus 'n herprobeer-lus een, want die voorwaarde is wat die verouderde oorskryf weier wat jou telling verloor het.
Een doelbewuste asimmetrie is die moeite werd om op te let. Ons het die mislukking-pad atomies gemaak en die sukses-pad, wat die teller na nul terugstel by 'n korrekte aanmelding, as 'n gewone laaste-skrywer-wen skryf gelaat. Gelyktydigheid bedreig nie 'n terugstel nie: verskeie suksesvolle aanmeldings wat wedywer wil almal nul skryf, en dit maak nie saak watter een wen nie. Net die inkrement is vyandig, want net die inkrement is die ding wat 'n aanvaller probeer onderdruk. Jy spandeer die koste van voorwaardelike skrywe en herprobeer waar 'n teenstander druk, en nie waar hulle nie druk nie.
Aanvaar iemand tel
Die werklike waarde van hierdie fout was die gemoedstoestand wat dit afgedwing het, want dieselfde dag wat ons dit reggemaak het, het ons gaan soek na alles anders wat stil-stil een-versoek-op-'n-slag aangeneem het of aangeneem het niemand meet nie, en 'n klein groepie broers en susters gevind.
Ons aanmeld-response was besig om aanvallers te vertel watter rekeninge bestaan. 'n Ontbrekende rekening, 'n gedeaktiveerde een, 'n gesluite een, en 'n verkeerde wagwoord het elkeen 'n onderskeibare antwoord opgelewer, en die geen-sulke-gebruiker-pad het selfs vinniger teruggekeer omdat dit die wagwoord-hutswaarde heeltemal oorgeslaan het, so 'n aanvaller kon leer wie 'n rekening het deur óf die status óf die horlosie dop te hou. Ons het die antwoorde saamgevou: 'n onbekende gebruiker en 'n verkeerde wagwoord lewer nou die identiese response op, en 'n onbekende gebruiker word steeds teen 'n vaste skyn-hutswaarde geverifieer sodat die tydsberekening dieselfde is of die rekening bestaan al dan nie. Die rekening bestaan of nie, en jy kan van buite af nie sien nie.
Registrasie het dieselfde lek in 'n moeiliker vorm gehad, want jy kan nie eenvoudig altyd sukses sê wanneer iemand 'n e-pos probeer registreer wat reeds geneem is nie, aangesien dit sou lyk of jy die rekening aan hulle oorhandig. So 'n registrasie vir 'n reeds-geregistreerde adres lewer nou presies dieselfde neutrale sukses op wat 'n vars registrasie oplewer, spandeer dieselfde wagwoord-hutstyd, skep niks, en stuur in plaas daarvan vir die werklike eienaar 'n e-pos wat sê iemand het hul adres probeer registreer. Die aanvaller leer niks. Die werklike eienaar word stil-stil ingelig.
En die wagwoord-herstel-vorm was 'n e-poskanon: geen per-adres-limiet nie, so jy kon ons herstel-e-posse na enige adres op herhaling laat afvuur, 'n aanval gemik op 'n derde party en op ons eie stuurreputasie. Dit beperk nou die tempo per teiken-e-pos, en oor die limiet slaan dit die stuur oor terwyl dit dieselfde sukses-response as altyd teruggee, sodat die demping self nie 'n nuwe manier word om te ondersoek watter adresse bestaan nie.
Elkeen daarvan is die uitsluitingsfout se les wat ander klere dra. Ontwerp asof 'n aanvaller versoeke in parallel stuur, jou response se tyd meet, en tel. Want die een ding waarvan jy seker kan wees, is dat die persoon wat jou aanmelding aanval dit nie een beleefde versoek op 'n slag doen nie.
Waarom 'n sekuriteitsteller gelyktydig getoets moet word
'n Gelyktydigheidsfout in 'n sekuriteitsbeheer is erger as 'n gelyktydigheidsfout enige ander plek, om 'n spesifieke rede: die party met die sterkste motief om dit te aktiveer is die aanvaller, en die toestande wat dit aktiveer, baie gelyktydige versoeke, is presies die toestande wat 'n aanval skep en 'n toets amper nooit skep nie. So 'n uitsluiting, 'n tempo-limiet, 'n kwota, enige beheer wat op 'n teller gebou is, kan nie gevalideer word deur dit in 'n lus te inkrementeer en te kyk hoe dit afgaan nie. Dit moet gelyktydig geslaan word, want gelyktydigheid is die aanvaller se eie omgewing, en die reeks-weergawe van die toets bewys net dat jou beheer werk teen 'n teenstander wat ingestem het om beleefd te wees.
En wanneer jy die teller wel verhard, is die gereedskap op 'n stoor sonder atomiese inkremente die voorwaardelike skryf: lees die weergawe, skryf net as dit nie beweeg het nie, herprobeer as dit wel het. Dit herbou die atomiese inkrement wat die databasis jou nie gegee het nie, uit die een waarborg wat dit wel gegee het.
As jy eerder op 'n identiteitsverskaffer wil loop wat reeds aanvaar die aanvaller tel in parallel, het Authagonal hierdie beheermeganismes gebou om almal gelyktydig geslaan te word, want dit is die enigste manier waarop hulle ooit werklik geslaan sou word.