Zobrazují se příspěvky se štítkemspring. Zobrazit všechny příspěvky
Zobrazují se příspěvky se štítkemspring. Zobrazit všechny příspěvky

29. ledna 2018

Spring Security, SAML & ADFS: Implementace

Posledně jsme se vyřádili na konfiguraci, tak teď už jen zbývá to nabouchat v tom Springu, ne? Dobrá zpráva je, že pokud budete následovat Reference Documentation, bude vám Spring SAML a ADFS fungovat out-of-the-box.

Špatná zpráva je, že pokud budete chtít použít Java configuration, nemáte se moc kde inspirovat. Pokud vím, tak k dnešnímu dni jsou k dispozici jen dva příklady:
Dalším benefitem mého příspěvku a ukázkového projektu je, že používají aktuální verzi Spring Frameworku a Spring Security, tedy verzi 5 (v tomhle asi budu chviličku unikátní). Třešničkou na dortu je pak buildování pomocí Gradle (protože kdo by ještě chtěl v dnešní době používat Maven, že jo? ;-)

Závislosti

Pro zdar operace budeme potřebovat následující závislosti:

Drobná Gradle poznámka: Protože používám současnou verzi Gradlu, používám konfiguraci implementation. Pro starší verze Gradle (2.14.1-) použijte původní (nyní deprecated) konfiguraci compile.

Spring SAML Configuration

Ať už se použije XML, nebo Java konfigurace, bude to v každém případě velmi dlouhý soubor. Velmi. I když nebudu počítat téměř 40 řádek importů, i tak zabere ta nejzákladnější konfigurace zhruba 5 obrazovek. Víc se mi to ořezat nepodařilo.

Ale nebojte se, nebudu vás oblažovat každým detailem. Jen vypíchnu to zajímavé, vynechám co jsem zmiňoval v minulém díle o konfiguraci a pro zbytek konfigurace vás odkážu do svého repozitáře, kde si to můžete vychutnat celé: SecurityConfiguration.java.

Nastavení HttpSecurity

Nebudu příliš zabíhat do podrobností, jak funguje samotné Spring Security (prostě chrání pomocí filtrů určité URL/zdroje) a podívám se na jedno konkrétní nastavení:

Uvedené nastavení definuje:
  • Vypnuté CSRF. U SAMLu nedává CSRF moc smysl - SAML requesty jsou podepsané privátním klíčem daného SP, jehož veřejný klíč je zaregistrován na použitém IdP.
  • Přídání dvou filtrů: jeden pro SAML metadata (metadataGeneratorFilter), druhý řeší samotný SAML mechanismus (samlFilter).
  • Definice URL, které vyžadují autentikaci (/user). 
  • Podstrčení SAML entry pointu namísto přihlašovacího formuláře (loginPage("/saml/login")).
  • Přesměrování na root kontext aplikace po úspěšném odhlášení (logoutSuccessUrl("/")).

SAML filtry

Základem jak Spring Security, tak Spring Security SAMLu jsou filtry - odchytí HTTP(S) komunikaci a transparentně aplikují zabezpečení aplikace. V případě SAMLu je těch filtrů celá smečka, ale v zásadě řeší jen tři věci: přihlášení (SSO), odhlášení (SLO) a metadata. Čtvrtým mušketýrem může být ještě IdP discovery, ale tu v našem případě nemáme.


Key manager

Všechny SAML zprávy, jež si IdP a SP vyměňují jsou podepsané privátním klíčem dané strany. Doporučuji mít pro SAML podpisový klíč separátní key store (nemíchat ho třeba s key storem, který potřebuje aplikační server pro HTTPS).

V naší ukázkové aplikaci je SAML key store na classpath - v jakémkoli jiném, než lokálním vývojovém prostředí, key store samozřejmě externalizujeme (nepřibalujeme do WARu) a hesla kryptujeme.


Podepisování SHA-256

V minulém díle jsem zmiňoval, že Spring SAML defaultně používá při podepisování algoritmus SHA-1, kdežto ADFS očekává SHA-256. Jedna strana se musí přizpůsobit. Doporučuji upravit aplikaci - použít SHA-256 není nic těžkého.

Výběr podpisového algoritmu se provádí při inicializaci SAMLu pomocí třídy SAMLBootstrap, která bohužel není konfigurovatelná. Pomůžeme si tak, že od třídy podědíme a potřebný algoritmus podstrčíme:

V konfiguraci pak třídu instancujeme následujícím způsobem. Mimochodem, povšimněte si, že beana je instancovaná jako static. To proto, aby inicializace proběhal velmi záhy při vytváření kontextu.


That's All Folks!

Tím se náš 3-dílný mini seriál o Spring Security, SAMLu a ADFS uzavírá. Samozřejmě, že bych mohl napsat ještě mnoho odstavců a nasdílet spoustu dalších gistů. Ale už by to bylo jen nošení housek do krámu.

Lepší bude, pokud si teď stáhnete ukázkový projekt sw-samuraj/blog-spring-security, trochu se povrtáte ve zdrojácích a na závěr v něm vyměníte soubor FederationMetadata.xml a zkusíte ho rozchodit vůči vašemu ADFS. Při troše štěstí by to mělo fungovat na první dobrou :-)

Jako bonus pro odvážné - pokud se opravdu pustíte do těch zdrojových kódů - můžete v historii projektu najít další Spring Security ukázky (je to celkem rozumně otagovaný):
  • Výměna CSRF tokenu mezi Springem a Wicketem (tag local-ldap).
  • Multiple HttpSecurity - v jedné aplikaci: autentikace uživatele přes formulář a mutual-autentication REST služeb přes certifikát (tag form-login).
  • Autentikace vůči lokálnímu (embedovanému) LDAPu (tag local-ldap).
  • Autentikace vůči Active Directory (tag remote-ad).

Související články


17. ledna 2018

Spring Security, SAML & ADFS: Konfigurace

Minule jsme se podívali - z obecnějšího pohledu - jak SAML funguje pro autentikaci aplikace. Kromě toho, že byste měli znovu zkouknout ty pěkné barevné diagramy, zobrazující SSO (Single Sign-On) a SLO (Single Logout), by se vám mohl hodit SAML a ADFS slovníček - od teď už očekávám, že termíny máte našprtané ;-)

Tenhle článek se bude zaměřovat na konfiguraci potřebnou pro to, aby nám SAML autentikace fungovala. V realitě pak tato konfigurace půjde nejčastěji ruku v ruce s implementací, protože abyste získali SP (Service Provider) metadata, budete potřebovat ho mít už funkční (pokud nejste SAML-Superman, který to zvládne nabouchat ručně, nebo externím nástrojem).

Registrace metadat

Aby nám SAML fungoval, musíme nejdřív vzájemně zaregistrovat jednotlivé IdP (Identity Providery) a SP (Service Providery). Jak jsme si říkali v minlém díle, vztah IdP - SP může být M:N, nicméně pro zbytek článku (a i v tom následujícím) budeme uvažovat jenom vztah 1:1, tedy náš SP (aplikace) se autentikuje vůči jednomu IdP.

Registrace metadat se může provést dvojím způsobem - buď můžeme poskytnou URL, na kterém si IdP/SP metadata sám stáhne při svém startu, nebo (asi častější) metadata poskytneme jako statický soubor. Zde budeme pracovat s druhým případem.

Registrace (ADFS) Federation metadat

Registrace Federation Metadat je jednoduchá - stáhneme XML soubor z daného URL a protože pro implementaci SP používáme Spring Security SAML, poskytneme ho jako resource pro MetadataProvider.

Metadata na ADFS serveru najdeme na následujícím URL:

Federation Metadata je dlouhý, ošklivý XML soubor. Způsob, jak ho poskytnout naší aplikaci je trojí:
  • dát ho na classpath a načíst třídou ClasspathResource
  • dát ho na file systém a načíst třídou FilesystemResource
  • načíst ho přímo z ADFS třídou HttpResource

Pokud např. soubor FederationMetadata.xml umístíme do adresáře src/main/resource/metadata, můžeme ho načíst následujícím způsobem:


Pokud potřebujete trochu víc (Spring) kontextu, může se podívat do kompletní Spring SAML konfigurace:

Registrace (Spring) SAML metadat

Registraci SAML metadat na ADFS si rozdělíme do dvou kroků:
  • Získání metadat z aplikace.
  • Registraci metadat na ADFS.

Generování SAML metadat (z aplikace)

Tady přichází ke slovu, co jsem předesílal - abyste byli schopný si vygenerovat SAML metadata, budete potřebovat mít Spring SAML aspoň částečně naimplementovaný. O generování metadat se stará Springovský filtr MetadataGeneratorFilter.

Generování metadat out-of-the-box funguje dobře (a s ADFS ho rozchodíte). Pokud chcete, nebo musíte metadata upravit, tohle je to správné místo. Například specifické (SP) Entity ID se dá nastavit tímto způsobem:


Filter pak necháme naslouchat na určitém URL endpointu, kde nám bude metadata poslušně generovat:


Situace je samozřejmě trochu složitější, takže pokud jste netrpělivý, nebo vám chybí potřebné Spring beany, nahlídněte do zmiňované SecurityConfiguration.java.

Ještě než si metadata vygenerujete a stáhnete z daného URL, jedno důležité upozornění! Při nesplnění následujících podmínek vám SAML před ADFS nebude fungovat.
  • Na URL musíte přistoupit přes HTTPS (a mít tedy odpovídajícím způsobem nakonfigurovaný aplikační server/servlet kontejner).
  • Na URL musíte přistoupit přes hostname nebo IP adresu, které jsou z IdP (ADFS) viditelné. (Takže ne https://localhost.)

Registrace metadat na ADFS

Teď se magicky přenesema na ADFS server (typicky přes Remote Desktop), kam si zkopírujeme vygeneraovaný SAML metadata soubor. Spring ho defaultně nazývá spring_saml_metadata.xml, nicméně na jméně nezáleží.

V AD FS Management konzoli nás bude zajímat jediná věc - položka Relying Party Trust, kde metadata našeho SP zaregistrujeme, resp. naimportujeme.

AD FS Management konzole

Import metadat se provádí pomocí wizardu (jak jinak ve Windows). Je to jednoduché a přímočaré: zadáme import ze souboru a pak už se jen doklikáme nakonec:

Import SP metadat ze souboru

V závěrečném shrnutí je dobré si zkontrolovat, že v sekci Endpoints jsou splněny dvě výše uvedené podmínky (HTTPS a hostname/IP adresa):

Kontrolní shrnutí SP endpointů

ADFS defaultně očekává, že hash algoritmus použitý při podepisování SAML zpráv bude SHA-256. Bohužel, Spring SAML posílá out-of-the-box SHA-1. Máte tedy dvě možnosti:
  • Upravit Spring SAML, aby používal SHA-256 (jak to ohackovat vám prozradím příště),
  • nebo říct ADFS, aby očekávalo SHA-1.

Nastavení Secure hash algorithm najdete po rozkliknutí Properties na záložce Advanced (je potřeba to udělat dodatečně - při importu metadat je tato volba nefunkční):

Nastavení Secure hash algorithm

Pokud si to na obou stranách nesladíte, budete na straně Springu dostávat nic neříkající výjimku:
2018-01-17 12:15:01 DEBUG org.springframework.security.saml.SAMLProcessingFilter - Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
        at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:100) ~[spring-security-saml2-core-1.0.3.RELEASE.jar:1.0.3.RELEASE]
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-5.0.0.RELEASE.jar:5.0.0.RELEASE]
Caused by: org.opensaml.common.SAMLException: Response has invalid status code urn:oasis:names:tc:SAML:2.0:status:Responder, status message is null
        at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:113) ~[spring-security-saml2-core-1.0.3.RELEASE.jar:1.0.3.RELEASE]
        at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:87) ~[spring-security-saml2-core-1.0.3.RELEASE.jar:1.0.3.RELEASE]

A na straně ADFS nápomocnější:
Microsoft.IdentityServer.Protocols.Saml.SamlProtocolSignatureAlgorithmMismatchException:
    MSIS7093: The message is not signed with expected signature algorithm.
    Message is signed with signature algorithm http://www.w3.org/2000/09/xmldsig#rsa-sha1.
    Expected signature algorithm http://www.w3.org/2001/04/xmldsig-more#rsa-sha256.
Takže, prozatím SHA-1. "Dvě stě padesát šestka" bude příště ;-)

Claim Rules

Poslední věc, kterou zbývá nastavit je mapování claims na assertions. Na naší nově vytvořené Relying Party Trust dáme Edit Claim Rules... a přidáme následující pravidlo, které nám z Active Directory vytáhne Name ID a doménové skupiny daného uživatele:

Editace Claim Rules

To be continued...

Tak a máme hotovo! Teda konfiguraci. Ovšem, jak už jsem naznačoval, v tento moment už stejně většinou máte hotovou i zbývající implementaci, takže pokud jsme nic neopomněli, mělo by nám fungovat jak SSO, tak SLO, přesně podlě těch krásných diagramů z minulého dílu.

V příštím, závěrečném díle, se podíváme, jak nabastlit zbytek Springovských věcí - můžete se těšit na Java konfiguraci (což je zatím vzácnost, protože Reference Documentation stále jede na XML) a samozřejmě pofrčíme na aktuálním Spring 5 (Cože?!? Vy jedete ještě na čtyřce?! No, nekecej 8-)

20. prosince 2017

Spring Security, SAML & ADFS: Úvod

Posledních pár týdnů jsem se teď mordoval se Spring Security, abych v grandiózním finále doiteroval k autentikaci pomocí SAML vůči ADFS.

Mimochodem, přijde mi, že poslední dobou, je to se Springem, jako s jakoukoliv jinou (velkou) dominantní značkou - pokud se pohybujete uvnitř dané domény, na vyhrazeném dvorečku, všechno frčí, jak na drátkách. Jakmile to zkusíte zintegrovat s něčím ne-nativním, začnou probémy. A rozlousknout ty problémy chvilku trvá - zvlášť, když StackOverflow mlčí a oficiální dokumentace se k (technologickým) cizincům moc nezná.

Ale to jsem odbočil, zpátky k tématu. Tentokrát se nebudu věnovat ani aktuálnímu use casu, ani těm problémům, ale zaměřím se jen na to, jak Springovský SAML s ADFS funguje a jak to v aplikaci naimplementovat a celkově nakonfigurovat.

Nicméně, pokud jste se ještě stále nechytli, o čem to mluvím a přemýšlíte, jestli číst dál, tak to bude o: lehce komplikované autentikaci webové aplikace oproti Active Directory (takový Microsoftí LDAP).

SAML slovníček

Neuškodí si trochu prosvištět názvosloví. I když SAML nepřináší nic moc převratného, přeci jenom říká určitým věcem svým vlastním způsobem.
  • SAML - Security Assertion Markup Language
  • User Agent (UA) - software, skrz který uživatel komunikuje se zabezpečeným zdrojem, typicky browser.
  • Service Provider (SP) - systém, který poskytuje zabezpečený zdroj a akceptuje authentication assertions a poskytuje Single Sign-On (viz následující body).
  • Identity Provider (IdP) - systém, který vydává authentication assertions a podporuje Single Sign-On.
  • SAML Assertions - assertions jsou gró SAML zpráv, které lítají mezi SP a IdP. Obsahují bezpečnostní informaci/e trojího typu: authentication statements, attribute statements a authorization decision statements. Nás budou zajímat první dva typy: jestli je uživatel autentikovaný a do jakých Active Directory skupin patří.
  • Single Sign-On (SSO) - přihlášení uživatele do více systémů jedinou akcí.
  • Single Logout (SLO) - odhlášení uživatele z více systémů jedinou akcí.
  •  SAML Metadata - XML data formát pro popis specifického SAML deploymentu, tedy SP nebo IdP.

ADFS slovníček

I ADFS má vlastní okruh termínů:
  • ADFS - Active Directory Federation Services
  • Claims - prohlášení týkající se uživatelů, které se primárně používá pro autorizaci přístupu k aplikacím. Claims jsou to, co potřebujeme dostat do SAML assertions jako attribute statements.
  • Claims Provider - systém, který poskytuje claims.
  • Relying Party Trust - partnerský systém, který zpracovává claims a kterému ADFS důvěřuje.
  • Federation Metadata - XML data formát pro výměnu konfigurace mezi Claims Providerem a Relying Party Trust.

SAML Binding

Následující popis SSO a SLO je poměrně detailní (musím se pochválit, že podrobnější jsem nevygoogloval) a je zaměřený na jednu konkrétní technologickou integraci - Spring Security SAML a ADFS. To znamená, že pro jiné konstelace to může fungovat "trošku" jinak.

Rovněž stojí za zmínku, že SAML specifikace definuje tzv. binding, tj. jak jsou SAML requesty a responsy mapovány na standardní message a komunikační protokoly. Pro SSO v browseru se většinou společně používají HTTP Redirect Binding a HTTP POST Binding. V následujících diagramech byste je měli bez problémů identifikovat. Co je podstatné - u tohoto druhu bindingu teče veškerá komunikace přes User Agent (browser), takže tam např. není žádná komunikace na pozadí mezi SP a IdP.

SAML Single Sign-On

Na následujícím obrázku jsem si dal hodně záležet a věřím, že jeho vysvětlující a informační hodnota je enormní, podaná krystalicky čistým způsobem. Přesto si neodpustím kratičké shrnutí:
  1. Browser (UA) přistoupí na chráněný zdroj na Service Providerovi (SP), který přesměruje UA na svoje login URL.
  2. SP vygeneruje SAML request a přesměruje UA na Identity Providera (IdP), kterému je předán SAML request.
  3. IdP vrátí UA přihlašovací formulář (jméno, heslo).
  4. UA pošle IdP credentials uživatele, spolu se SAML requestem.
  5. IdP předá credentials Claims Providerovi (CP), který ověří uživatele a v případě úspěchu vrátí před-konfigurované claims.
  6. IdP přebalí claims do assertions, které obalí do SAML response a tu pak vloží do HTML formuláře, který pošle UA. Součástí formuláře je i JavaScript, který může formulář automaticky odeslat.
  7. UA automaticky odešle formulář se SAML response na SP. V případě, že je v UA vypnutý JavaScript, musí uživatel odeslat formulář klepnutím na tlačítko.
  8. SP ověří přijaté SAML assertions a pokud je všechno v pořádku, přesměruje UA na původně vyžádaný zdroj.

SAML Single Sign-On

Uvedené schéma zobrazuje komunikaci pro ne-autentikovaného uživatele. V případě, že je uživatel již přihlášen - ať už lokálně na daném SP, nebo díky SSO na jakémkoliv jiném SP (který je registrován u daného IdP) - tak se přeskočí posílání a odesílání ADFS login formuláře (a samozřejmě komunikace s CP) a UA rovnou obdrží SAML response.

IdP Discovery

Uvedený scénář se dá zpestřit ještě o jednu věc. Vztah mezi SP a IdP je m:n
  • jeden IdP může obsluhovat několik SP a stejně tak
  • jeden SP si může vybrat z několika IdP.
V prvním případě se nic zvláštního neděje, právě od toho tu je SSO. V druhém případě je to trochu komplikovanější - jak si UA/SP vybere, vůči kterému IdP se autentikovat?

Tenhle případ řeší IdP Discovery. Místo úvodního přesměrování na login URL aktuálního SP dojde k přesměrování na stránku se seznamem všech zaregistrovaných IdP, z nichž si uživatel explicitně vybere.

Nastavit IdP Discovery pomocí Spring Security SAML není nijak složité, nicméně pro tento a následující články s touto možností nepracuji.

SAML Single Logout

Když jsem se pustil do kreslení předešlého diagramu pro SSO, tak se mi výsledek tak zalíbil, že jsem si střihnul ještě jeden obrázek - pro Single Logout (SLO).

A aby to nebylo triviální, tak jsem si rozchodil dva SP, abych mohl zdokumentovat, jak probíhá odhlášení ze všech zaregistrovaných SP. Protože o tom SLO je: když se odhlásím na jednom SP, tak mě to automaticky odhlásí i ze všech ostatních SP.

SAML Single Logout

To be continued...

Abych udržel článek v rozumné čitelnosti, rozhodl jsem se téma rozdělit do krátkého mini-seriálku. Příště bych se podíval, jak vyměnit metadata mezi SP a IdP, plus jak nakonfigurovat ADFS.

V závěrečném díle bych probral konfiguraci Spring Security SAML. Můžete se těšit na popis Java configuration (ofiko dokumentace jede furt na XML) a samozřejmě to pofrčí na aktuálním Spring 5.

Související články