'n Identiteitsverskaffer het ons vertel wie jy is, en ons het dit geglo
Eenmalige aanmelding het 'n stille uitgangspunt: wanneer 'n gebruiker van 'n identiteitsverskaffer af opdaag, het daardie verskaffer reeds vir hom ingestaan, so jy kan die wagwoord oorslaan en hom net inlaat. Die hele punt is om die IdP te vertrou. Die fout wat ons nou gaan beskryf, het in die gaping gelê tussen "vertrou die IdP om sy eie gebruikers te verifieer" en "vertrou alles wat die IdP jou vertel oor wie sy gebruikers is". Dit is nie dieselfde sin nie, en die verskil was 'n rekeningoorname.
Hier is die opstelling. 'n Huurder op ons platform kan gefedereerde verbindings opstel: SAML na hul korporatiewe IdP, OIDC na 'n ander een. 'n Gebruiker meld aan deur een van daardie verbindings, die IdP stuur 'n bewering terug, en ons bediener moet een vraag beantwoord: watter plaaslike rekening is dit? Kry daardie afbeelding verkeerd en jy het óf 'n vreemdeling wat uit sy eie rekening uitgesluit is, óf, baie erger, 'n vreemdeling wat in iemand anders s'n instap.
Die aansluitsleutel was 'n e-pos, en 'n e-pos is net 'n bewering
Ons kode het "watter rekening is dit" op die voor die hand liggende manier beantwoord. Die bewering het 'n e-pos gedra, so ons het die gebruiker daarvolgens opgesoek: FindByEmailAsync(assertedEmail). As 'n passende rekening bestaan het, is die terugkerende gefedereerde gebruiker daarop opgelos en aangemeld. Skoon, eenvoudig, en presies hoe baie SSO-integrasies geskryf word.
Die probleem is wat daardie e-pos eintlik is. Dit is nie 'n feit wat die IdP bewys het nie. Dit is 'n string in 'n bewering, en die bewering is net so betroubaar soos die verbinding waardeur dit gekom het. In 'n multi-huurder wêreld beheer jy nie elke verbinding nie. Enigeen wat een kan opstel (hul eie SAML IdP, hul eie OIDC-verskaffer) kan enige string wat hulle wil in die e-posveld sit. So 'n verbinding wat jy nie vertrou nie beweer email = [email protected], ons opsoek vind die werklike HUB se rekening, en die aanvaller is as hom aangemeld. Geen wagwoord, geen phishing, geen fout in die kriptografie nie. Die handtekening op die bewering was volkome geldig. Dit het bloot ingestaan vir 'n bewering wat ons nooit as 'n identiteit moes behandel het nie.
Die ontstellende deel is dat elke individuele stuk gewerk het. Die IdP het sy eie gebruiker korrek geverifieer. Die handtekening het geverifieer. Die rekening het bestaan. Die oorname was nie 'n mislukking van enige toets nie; dit was om die verkeerde veld as die sleutel te gebruik.
Waarom meer validering nie die regstelling is nie
Die aanloklike reaksie is om die e-pos strenger te valideer. Vereis email_verified. Kontroleer die domein. Voeg 'n reël by. Maar let op die vorm van die strik: die aanvaller beheer die bewering, so enige eienskap wat jy van die bewering aflees, is 'n eienskap wat die aanvaller kan stel. Om email_verified = true te eis van 'n verbinding wat die aanvaller besit, is om die jakkals te vra om die hoenderhok te sertifiseer. Jy kan nie jou pad oop valideer uit die vertroue in die verkeerde bron nie.
Die e-pos is reg as gerief. Dit is 'n verskriklike primêre sleutel, want dit is globaal (dieselfde adres kan oor baie verskaffers opduik) en vervalsbaar (dit is wat ook al die berend verbinding sê). 'n Aansluitsleutel moet nie een van die twee wees nie.
Die regstelling was om die sleutel te verander, nie om kontroles by te voeg nie
Die werklike regstelling was om heeltemal op te hou aansluit op e-pos en aan te sluit op die een ding wat 'n verbinding nie kan vervals vir 'n verskaffer wat dit nie besit nie: die paar (provider, sub). Die sub is die subjekidentifiseerder wat die verskaffer vir daardie gebruiker uitreik, beperk tot daardie verskaffer. 'n Terugkerende gebruiker word opgelos deur die plaaslike identiteit op te soek wat voorheen aan hierdie verbinding se (provider, sub) gekoppel is. 'n Aanvaller se verbinding het sy eie verskaffer-id; dit kan enige sub wat dit wil binne sy eie naamruimte munt, maar dit kan nie iemand anders se verbinding se (provider, sub) voortbring nie. Die naamruimte is die slotgrag.
Die e-pos val dan terug na die rol wat dit altyd moes gehad het: 'n wenk vir koppeling, nooit vir oplossing nie, en slegs wanneer daar vir dit ingestaan word. Ons pas 'n beweerde e-pos slegs by 'n bestaande rekening wanneer die e-pos geverifieer is deur 'n bron wat ons werklik vir daardie domein vertrou: email_verified van 'n verbinding waarvan die domein in die huurder se AllowedDomains is. Buite dit kry 'n nuwe gefedereerde identiteit sy eie rekening, nie iemand anders s'n nie.
Die les, uitgehaal
Wanneer jy identiteit federeer, skei twee vrae wat jy in die versoeking is om saam te smelt. "Het hierdie verskaffer hierdie gebruiker geverifieer?" word deur die handtekening beantwoord. "Wie is hierdie gebruiker in my stelsel?" moet beantwoord word deur 'n sleutel wat die berend party nie oor grense heen kan vervals nie, wat 'n per-verskaffer subject beteken, nie 'n globale eienskap soos e-pos nie. Behandel elke veld in 'n bewering as aanvaller-beheer, tensy die spesifieke bron van daardie veld een is wat jy vir daardie spesifieke bewering vertrou. Die e-pos is die veld waarna mense eerste gryp omdat dit menslik is en uniek lyk. Dit is presies die verkeerde een.
Ons het dit uitgestuur as deel van 'n voor-bekendstelling sekuriteitsdeurloop oor ons eie verifikasiebediener, voordat enigiemand ons met hul aanmeldings vertrou het. Dit is die soort fout wat elke toets slaag, elke handtekening verifieer, en die sleutels oorhandig aan wie ook al in die regte formaat vra. Die regstelling was nie 'n beter slot nie. Dit was om te besef dat ons die verkeerde reël van die ID gelees het.
Om identiteit veilig te federeer, kom neer op aansluit op 'n sleutel wat die berend party nie kan vervals nie, en om dit verkeerd te kry is 'n rekeningoorname wat elke toets slaag. Authagonal los gefedereerde gebruikers op volgens hul per-verskaffer subject, nooit volgens die e-pos in die bewering nie.