Barvna shema: Temna
Trenutno dejavna stran: start » sl » Dokumentacija » Razširitve DokuWikija (WvdS) » Identity » Koncepti

Koncepti

Kako deluje wkidentity: kaj paket upravlja, od česa je odvisna zaščita in zakaj so postopki zasnovani tako, kot so. Kaj želite narediti, piše na strani Identity; kaj želite poiskati, v Tehnični referenci.

Definicija

wkidentity skrbi za identiteto računa ves čas njegovega obstoja. Paket združuje dve področji, ker odgovarjata na isto vprašanje – kdo je to:

  • Drugi dejavnik. TOTP po RFC 6238 (združljiv z Microsoft Authenticator, Google Authenticator in drugimi standardnimi aplikacijami), ki si ga uporabnik sam nastavi na strani profila (do=profile), po izbiri tudi enkratna koda po e-pošti; poleg tega obveznost za posamezen imenski prostor in neobvezno preverjanje ob prijavi. Dopolnjuje ju notranja preizkusna stran, ki poljubno skrivnost Base32 preveri z lastnim izračunom TOTP, ne da bi skrivnost zapustila brskalnik.
  • Postopki z enkratno kodo. Ponastavitev gesla, samoregistracija in povabilo, ki ga pošlje skrbnik – zgrajeni po načelu, da gesla nikoli ne pozna nihče razen imetnika računa. Glejte Postopki z enkratno kodo.

Paket ni authtype (zaledje, v katerem DokuWiki hrani račune in preverja gesla): upravljanja uporabnikov ne zamenja, temveč deluje pred njim in ob njem.

Zagotovilo je lastnost seje, ne računa

isEnrolled() po nastavitvi dejavnika za vedno odgovarja pritrdilno – to je lastnost računa brez časovne meje. currentAssurance() odgovarja na drugo vprašanje: kaj je ta seja zdaj dokazala. Račun, ki ima dejavnik nastavljen že leta, ima lahko sejo, ki ni nikoli opravila dodatnega preverjanja (step-up).

Prav ti dve lastnosti je enforceNamespacePolicy() do 8. avgusta 2026 zamenjevala – to je bil dejanski povod za utrditev varnosti. Kljuka ACL je preverjala samo isEnrolled($user); pri izklopljenem enforce_login (privzeto) je zato enkrat nastavljen dejavnik zadoščal za vedno, ne glede na to, ali ga je trenutna seja kdaj dokazala. enforceNamespacePolicy() zdaj veljavno zahtevo razreši prek PolicyStore::effectiveRequirement() (glejte »Pravilnik« spodaj) in jo preveri proti dejanskemu zagotovilu seje (helper_plugin_wkidentity_contract::satisfies()); isEnrolled() odloča le še o tem, ali bi bila zahteva sploh izpolnljiva – račun brez vpisanega dejavnika je še naprej zavrnjen takoj in nikoli poslan v dodatno preverjanje, ki ga ne more opraviti. Zavrnitev zdaj navede enega od dveh razlogov: »ni vpisan« (povezava na vpis) ali »vpisan, a ta seja tega ni dokazala« (povezava na pravo dodatno preverjanje, ki se vrne na prvotno stran, do=wkidentity_stepup_start). Podrobnosti: docs/threat-model.md, ugotovitev F6.
Stopnja Pomen
NONE ni seje ali anonimni obisk
AUTHENTICATED samo prijava v jedru, v tej seji brez drugega dejavnika
TOTP drugi dejavnik dokazan z aplikacijo za preverjanje
MAIL_OTP drugi dejavnik dokazan s kodo, poslano po e-pošti

TOTP in MAIL_OTP sta alternativi, ne stopnici na lestvi – oboje sprejme samo zahteva MFA_ANY. Od utrditve varnosti ima lahko račun oba dejavnika hkrati (vsakega je mogoče nastaviti in odstraniti neodvisno od drugega); posamezna seja pa vedno dokaže samo enega od njiju.

Dokazana stopnja zdaj poteče. Nastavitev assurance_ttl (privzeto 3600 sekund) določa, kako dolgo opravljeno dodatno preverjanje – ali dejavnik, dokazan ob prijavi – šteje kot zagotovilo te seje, preden se seja vrne na AUTHENTICATED (ne na NONE: prijava ostane, poteče samo dodatni dokaz dejavnika). Za območje, ki zahteva dejavnik, je treba po izteku znova opraviti dodatno preverjanje. To nadomešča prejšnjo opredelitev, da »nobena stopnja v seji ne poteče«.

Dodatno preverjanje s preverjenim ciljem vrnitve

Prej ga ni bilo: vsaka preusmeritev v LoginGate (razredu, ki prijavo zadrži do vnosa kode) in v action.php je brezpogojno kazala na $conf['start'], nikoli nazaj na prvotni cilj. Zdaj require() po potrebi preusmeri na do=wkidentity_stepup – isto lupino obrazca kot pri do=wkidentity_verify (ponovno uporabljena verifyCode()/verifyEmailChallenge(), brez nove kriptografije) – in se po pravilni kodi vrne na preverjeni cilj. Uporabnik tako po potrditvi kode pristane tam, kamor je bil namenjen, in ne na začetni strani.

Cilj vrnitve nikoli ni surov naslov URL. Sprejet je izključno strukturiran sklic – ID strani, očiščen s cleanID(), ali ID prispevka po pogodbi Contract v1, ki pri wkfluentui dejansko obstaja. V vseh drugih primerih velja $conf['start'], ista varna privzeta vrednost, ki jo v tem vtičniku že uporablja vsaka obstoječa preusmeritev. Povezave, ki bi uporabnika po vnosu kode preusmerila na tuje spletno mesto, zato ni mogoče sestaviti. Ta disciplina namenoma posnema, kako jedro DokuWikija razreši cilj vrnitve svojega prijavnega obrazca – nikoli z naslovom, ki ga izbere klicatelj.

Pravilnik: imenski prostor, skupina, uporabnik in dokumentirano razreševanje sporov

Od utrditve varnosti PolicyStore (razred, ki hrani in vrednoti pravilnik) upošteva štiri neodvisne osi namesto samo imenskega prostora: namespace (primerjava predpone kot v conf/acl.auth.php), group (vsaka skupina trenutne seje), user (natančno prijavno ime) ter plugin/module/view/command/adminAction (natančen ID, ki ga določi vtičnik sam). Za eno zahtevo lahko hkrati velja več osi – PolicyStore::effectiveRequirement($scope, $user, $groups) vse ustrezne vnose združi prek PolicyStore::combine().

combine() obravnava štiri vrednosti zahteve kot majhno mrežo, ne kot številsko lestvico – prav zato, ker sta TOTP in MAIL_OTP alternativi, ne stopnici. AUTHENTICATED je nevtralni element (druge strani ne spremeni); MFA_ANY je pod obema konkretnima dejavnikoma (vsak od njiju že izpolni »kateri koli MFA«); TOTP in MAIL_OTP sta sicer neprimerljiva. Vsaka definirana kombinacija da strožjo od obeh vrednosti – nikoli šibkejše od katere koli od njiju. Zato je trditev »pravilnik lahko vtičniku lastno najnižjo zahtevo samo zviša, nikoli je tiho ne oslabi« dokazljiva, ne zgolj obljubljena.

Edina nedefinirana celica je pravi spor: vnos za imenski prostor zahteva TOTP, vnos za skupino ali uporabnika pa MAIL_OTP – dva različna konkretna dejavnika, ki ju nobena posamezna stopnja currentAssurance() ne more izpolniti hkrati. effectiveRequirement() spor razreši po specifičnosti: vnose združuje od najsplošnejšega do najbolj specifičnega (imenski prostor oziroma neposredni obseg, nato vsaka ustrezna skupina, nato uporabnik – in eno raven višje, v satisfies()/require(), na koncu še izrecna zahteva vtičnika med izvajanjem, kot najbolj specifičen glas sploh). Sam spor se zabeleži prek Trace::note() – pravilnik, ki si nasprotuje, zasluži pozornost upravljavca, ne tihega metanja kovanca.

Imenski prostor, ki je naveden samo na starejšem seznamu required_namespaces (seznam je nastal pred required_scopes in nima lastne stopnje zahteve), se še naprej upošteva, in sicer z zahtevo MFA_ANY. To je najzvestejše branje tega, kar je seznam vedno pomenil (»zadošča kateri koli vpisan dejavnik«), le da se zdaj preverja proti dejanskemu zagotovilu seje, ne zgolj proti vpisu računa.

Potek dela (vpis)

Vpis je namenoma postopek v dveh korakih in ne takojšen vklop, da uporabnik dvostopenjskega preverjanja nikoli ne »vklopi« s skrivnostjo, ki je njegova aplikacija za preverjanje v resnici ni nikoli poskenirala:

  1. Začetek – gumb »Nastavi dvostopenjsko preverjanje« na do=profile (viden samo, dokler dejavnik ni vpisan). Ustvari novo naključno skrivnost (auth_randombytes(), 160 bitov) in jo shrani samo v sejo – na disk se še nič ne zapiše.
  2. Potrditev – stran prikaže kodo QR, skrivnost za ročni vnos in polje za šestmestno kodo. Šele pravilna koda (preverjena proti čakajoči skrivnosti, s toleranco ±1 časovni korak) skrivnost šifrirano trajno shrani (saveSecret()) in čakajoči vnos odstrani iz seje. Napačna koda čakajoče skrivnosti ne spremeni – uporabnik lahko isto kodo QR znova poskenira oziroma vnese.
  3. Stanje in izklop – ko je dejavnik vpisan, stran profila pokaže samo še stanje »aktivno« in potrditveno polje »Izklopi dvostopenjsko preverjanje«, ki skupaj z običajnim gumbom profila »Shrani« skrivnost odstrani (removeSecret()).
Znana past DokuWikija, tu rešena: doku.php pokliče session_write_close() pred act_dispatch(). Preprost zapis $_SESSION[…] = … v kljuki ACTION_ACT_PREPROCESS zato posodobi samo tabelo v pomnilniku trenutne zahteve in nikoli ne pride na disk – naslednja zahteva (oddaja potrditve) čakajoče skrivnosti ne vidi več in vsaka koda bi bila »napačna«. Rešitev: vsak zapis čakajoče skrivnosti v sejo se sam znova ovije v @session_start(); …; session_write_close(); (isti vzorec, ki ga jedro DokuWikija uporablja v updateprofile() iz auth.php in v send_redirect() iz common.php). Brez tega vzorca ponovnega odpiranja potrditev odpove ne glede na okolje – simptom se je enako pokazal na MicroApache in IIS.

Notranja preizkusna stran (do=wkidentity_debug; povezavo nanjo v profilu vklopi nastavitev show_debug_link) je od pravega vpisa popolnoma ločena: kode TOTP za poljubno prilepljeno skrivnost izračuna izključno v brskalniku (Web Crypto HMAC-SHA1) in strežniku nikoli ničesar ne pošlje. Namenjena je preverjanju združljivosti z aplikacijami za preverjanje, ne prikazu pravih uporabniških skrivnosti.

Potek dela (vpis po e-pošti)

Drugi zavihek v istem razdelku dvostopenjskega preverjanja, viden samo, če je vklopljena nastavitev allow_email_method (privzeto izklopljena, glejte »Konfiguracija« v Tehnični referenci). Velja isto načelo dveh korakov kot pri TOTP: »Nastavi po e-pošti« pošlje šestmestno kodo na naslov, ki je trenutno shranjen v profilu (sendEmailChallenge(); koda je na strežniku shranjena kot zgoščena vrednost s soljo – od utrditve varnosti password_hash(), prej SHA-256 brez soli – z veljavnostjo 10 minut in nikoli v golem besedilu); šele pravilno vnesena koda metodo vklopi (saveEmailMethod()). Od utrditve varnosti lahko uporabnik uporablja obe metodi hkrati, TOTP in e-pošto; nastavi in odstrani ju neodvisno drugo od druge (hasFactor()/enrolledFactors()/removeFactor()). Posamezna prijava ali dodatno preverjanje pri tem sprejme tisti nastavljeni dejavnik, s katerim se ujema vnesena koda.

Obveznost ob prijavi

Izvedena je z LoginGate.php in jedrno kljuko AUTH_LOGIN_CHECK (inc/auth.php, pred dokončno vzpostavitvijo seje). To je edina točka v celotnem ciklu zahteve, na kateri je mogoče dodatni dejavnik vriniti pred dokončano prijavo, ker jedrna auth_login() sicer preverjanje gesla in vzpostavitev seje opravi v enem koraku.

Potek: pravilno geslo + vpisan dejavnik + vklopljena nastavitev enforce_login → prijava se ustavi. Čakajoče stanje (uporabnik, geslo, nastavljene metode, rok, števec neuspelih poskusov) se za največ login_attempt_window sekund shrani v sejo (vzorec ponovnega odpiranja, glejte zgoraj), stran do=wkidentity_verify pa zahteva ustrezno kodo (če sta nastavljeni dve metodi, se koda preveri proti obema, glejte »Zagotovilo je lastnost seje, ne računa« zgoraj). Ob uspehu se sproži prava jedrna prijava (auth_login()), brez lastne logike za sejo in piškotke, hkrati pa se zabeleži zagotovilo seje (StepUp::recordAssurance()): dejavnik, dokazan ob prijavi, od tedaj šteje enako kot dejavnik, dokazan z dodatnim preverjanjem, zato takojšnje, odvečno ponovno vprašanje ni potrebno. Po login_max_attempts neuspelih poskusih se čakajoče stanje zavrže; uporabnik mora znova začeti z geslom.

Uporabnikov brez vpisanega dejavnika to sploh ne zadeva – gateLogin() poseže samo pri uporabnikih, ki dejavnik že imajo; račun, ki dvostopenjskega preverjanja nikoli ni nastavil, se še naprej prijavlja v enem koraku, tudi pri vklopljenem enforce_login. Nastavitev torej nikogar ne pripravi do vpisa, temveč zaostri samo prijavo računov, ki dejavnik že imajo.

enforce_login preglasi jedrno vedenje funkcije auth_login() – to je najbolj tvegan del kode v tem vtičniku. Zato je privzeto izklopljen in dosegljiv kot preprosto stikalo onoff v običajnem zaslonu »Splošne nastavitve« (ne samo v lastni skrbniški plošči): napake na tem mestu bi sicer v najslabšem primeru ne bilo mogoče dovolj hitro izklopiti.

Postopki z enkratno kodo

Trije postopki, en mehanizem: ponastavitev gesla, samoregistracija in povabilo, ki ga pošlje skrbnik. Vsi trije se končajo tako, da imetnik računa geslo vnese sam – geslo se nikoli ne ustvari, nikoli ne pošlje in nikoli ne prikaže.

Vklopite in izklopite jih z otp_flows (privzeto vklopljeno).

Dve ključni spoznanji

Brez njiju je seznam nastavitev videti kot pet poljubnih časovnih vrednosti:

  1. Koda ni postopek. Koda velja otp_ttl sekund (privzeto 1800), postopek sam pa reset_ttl / register_ttl / invite_ttl (privzeto 1 dan / 2 dneva / 7 dni). Zato je mogoče zahtevati novo kodo, ne da bi začeli znova – poslano sporočilo se lahko izgubi, postopek ga preživi. Koda v poštnem predalu je ključ v vratih, zato je kratkotrajna; postopek ni.
  2. Ugibanje omejuje proračun poskusov, ne dolžina kode. otp_attempts (privzeto 5) šteje na postopek, nova koda ga ne napolni znova. Pritisk na »Pošlji novo kodo« ne prinese nove serije poskusov. otp_resend_gap (privzeto 60 s) dodatno omejuje, kako pogosto je kodo sploh mogoče poslati znova.

Kaj obrazec razkrije o računu

Nič. requestReset() odgovori enako, ne glede na to, ali račun obstaja – sicer bi bil obrazec za ponastavitev preverjalnik članstva, v katerega lahko vsakdo vtipka poljubna imena.

Iz istega razloga je besedilo na zaslonu za vse enako: izpelje se samo iz stanja celotnega wikija, nikoli iz vnesenega imena. Besedilo, ki bi se prilagajalo posameznemu računu, bi izdalo, katera imena imajo aplikacijo za preverjanje.

Pot prek skrbnika

Za primer, ki ga noben obrazec ne more rešiti – recovery_factor ima vrednost totp, račun pa nima aplikacije za preverjanje, ali pa e-pošta dokazano ne prispe:

  • Skrbnik izda kodo v upravljanju povabil (Tehnična referenca) ali v ukazni vrstici. Ta pot namenoma odpre postopek s kodo po e-pošti, ne glede na recovery_factor – je izhod v sili, in izhod, za katerega bi veljal isti pravilnik kot za pot v račun, ne bi bil izhod.
  • Koda se skrbniku prikaže natanko enkrat, v odgovoru na zahtevo, in ni nikjer shranjena tako, da bi se lahko prikazala še enkrat.
  • Najkrajši razmik za ponovno pošiljanje tu ne velja: če bi morala pomoč čakati minuto samo zato, ker se je uporabnik zatipkal, bi to izničilo njen namen.
  • Koda upravičuje do nastavitve gesla in nobenega ne razkrije. Skrbnik zato nima v rokah nobenih prijavnih podatkov. Ker pa koda dovoljuje nastaviti geslo, je njena predaja skrbnikova odgovornost: oseba, ki jo prejme, lahko računu nastavi novo geslo.

Varnost

  • Šifriranje shranjenih podatkov – skrivnosti nikoli niso shranjene na disku v golem besedilu (Tehnična referenca: shranjevanje); kode za e-pošto so shranjene samo kot zgoščene vrednosti s soljo (od utrditve varnosti password_hash()).
  • Ponovna uporaba kode TOTP je izključena (od utrditve varnosti) – časovni korak, ki je bil enkrat sprejet, se za isti račun ne sprejme še enkrat, na vseh mestih, ki uporabljajo verifyCode() (prijava, dodatno preverjanje, potrditev vpisa, obnovitev gesla).
  • CSRF – vsaka oddaja na do=profile, v skrbniški plošči in na do=wkidentity_verify preveri checkSecurityToken() (CWE-352).
  • XSS – vse izpisane vrednosti (skrivnost, URI, prijavno ime) gredo skozi hsc() (CWE-79).
  • Vpis v dveh korakih – preprečuje »vklop« brez dokazanega skeniranja oziroma prejema (glejte »Potek dela (vpis)«), za obe metodi.
  • Obveznost ob prijavi kot zasilno stikalo – enforce_login je preprosto stikalo onoff, neodvisno od lastne skrbniške plošče, ki ga je mogoče takoj izklopiti.
  • Omejitev poskusov – števec neuspelih poskusov na do=wkidentity_verify, vezan na sejo (brez sledenja naslovom IP, namenoma preprosto).
  • Orodje za razhroščevanje – dosegljivo samo prijavljenim uporabnikom; kode se izračunajo izključno v brskalniku, strežniku se nikoli nič ne pošlje.
  • Prvi vtičnik AUTH_ACL_CHECK v tem repozitoriju – pravice samo dodatno zaostri, datoteke conf/acl.auth.php nikoli ne spreminja.
  • Nobeno geslo nikoli ne zapusti strežnika – postopki z enkratno kodo gesla ne ustvarijo in ne pošljejo; imetnik računa ga vtipka sam, najmanjšo dolžino pa preveri strežnik.
  • Kode samo kot zgoščene vrednosti – tudi v datotekah postopkov; skrbnik lahko kodo izda, ne more pa je poiskati.
  • Brez razkrivanja računov – obrazec za ponastavitev odgovori enako, ne glede na to, ali račun obstaja, in njegovo besedilo se nikoli ne izpelje iz vnesenega imena.
  • Brez zapisovanja pri GET – vsaka veja postopkov, ki spreminja stanje, zahteva POST in varnostni žeton; zahteva GET samo izriše obrazec. Ponastavitev, ki bi jo lahko sprožil iskalni robot s sledenjem povezavi, bi lahko vsakdo sprožil za vsakogar.

Sorodne teme

sl/wiki/dwe/wkidentity/concepts.txt · Zadnja sprememba: uporabnika Wolfgang van der Stille