This document discusses vulnerabilities in single sign-on (SSO) protocols like SAML and OpenID Connect. It describes how XML parsing issues and weak cryptographic signing algorithms can be exploited to bypass SSO and assume another user's identity. It provides recommendations to prevent exploitation, such as using stronger cryptographic algorithms, validating protocol fields, and implementing additional checks on protocol attributes.
2. What is Single Sign On
App1
App3
App2
Identity Provider
(IdP)
login
login
login
Responsibility
- Presents login page
- Verifies credentials
- Creates and maintain user sso session
- Returns identity to app securely
- Skip login page if user already has session
User
credentials
Database
Most popular SSO protocols
- SAML
- OpenID-Connect
send identity to app securely
Internal DB call
3. SAML SSO
Service Provider
(SP)
Identity Provider
(IdP)login
Responsibility
- Presents login page
- Verifies credentials
- Creates and maintain user session
- Returns identity to app using
signed/encrypted SAML response
- Skip login page if user already has session
on subsequent login requests
User
credentials
Database
Examples
www.example.com(SP) → google.com( IdP)
SAML2
SP_acs?SAMLResponse=signed_or_encrypted(identity)
5. SAML SSO bypass vulnerability
● Service Provider relies on Subject/NameID and Assertions to
know the who the user is to create SP session.
● Service Provider uses various SAML libraries to parse SAML
response ( XML Formatted )
● Attacker exploits two issues to manipulate parsed data
1) XML parsing issue
2) Cryptographic signing issue
6. Issue #1 - XML Parsing Issue
XML
<saml:NameID>not-a-cto@example.com</saml:NameID>
Parsed
NameID
|_ Text: not-a-cto@example.com
XML
<saml:NameID>not-a-<!-- this is a comment -->cto@example.com</saml:NameID>
Parsed
NameID
|_ Text: not-a-
|_ Comment: this is a comment
|_ Text: cto@example.com → here is the vulnerability
7. Issue #2 - Cryptographic signing
Both XML will have same cryptographic signature
<p> hello </p>
And
<p>hello</p>
Reason: because XML doesn’t care about whitespace.
When the XML document is analyzed before a signature is created, space is removed.
Its called canonicalization.
Most canonicalization algorithms also don’t care about comments → vulnerability
That means both will have same signature.
<saml:NameID>not-a-<!-- this is a comment -->cto@example.com</saml:NameID>
And
<saml:NameID>not-a-cto@example.com</saml:NameID>
8. SAML Response with Canonicalization algo
<SAMLResponse>
<Issuer>https://google.com/</Issuer>
<Assertion ID="_id1234">
<Subject>
<NameID>non-a-cto@example.com</NameID>
</Subject>
</Assertion>
<Signature>
<SignedInfo>
<CanonicalizationMethod Algorithm="xml-c14n11"/>
<Reference URI="#_id1234"/>
</SignedInfo>
<SignatureValue>
some base64 data that represents the signature of the assertion
</SignatureValue>
</Signature>
</SAMLResponse>
9. How attacker exploits
Precondition
a) Attacker owns his own account at Identity Provider AND
b) Knows what’s canonicalization
Steps
1) Attacker creates/has an account with emailAddress - not-a-cto@example.com
2) Attacker logs in the IdP i.e. Example.com and updates his own emailAddress to
not-a-<!-- this is a comment -->cto@example.com
3) Attacker logs out and logs back in which generates SAML response with comments to exploit XML parsing
vulnerability
OR
1) Attacker creates/has an account with emailAddress - not-a-cto@example.com
2) Attacker logs in the IdP and grabs SAML response and updates assertion to
not-a-<!-- this is a comment -->cto@example.com
4) Replays SAML response to exploit weak XML canonicalization vulnerability
10. How to prevent exploitation
● Avoid SAML and use more modern OpenID-Connect which doesn’t use XML
● Check your SAML library and dependent XML parsing and signature verification
libraries and check for vulnerabilities.
● As an IdP, purge comments while creating XML documents
● As an SP, reject any SAML attribute if XML attribute node contains more than one
child.
● Use canonicalization algorithm which doesn’t remove comments while generating
signature - http://www.w3.org/2001/10/xml-exc-c14n#WithComments
Affected ones
● OneLogin’s python-saml,
● OneLogin’s ruby-saml,
● Clever’s saml2,
● omniauth-saml, and
● Shibboleth’s openSAML C++
11. OpenID-Connect
App
(RP aka Relying Party)
OpenID-Connect
Provider
(OP)
login
Responsibility
- Presents login page
- Verifies credentials
- Creates and maintain user session
- Returns identity to app using signed id_token
response(JWT formatted)
- Skip login page if user already has session
on subsequent login requests
User
credentials
Database
Examples
www.example.com(RP) → accounts.google.com( OP)
OpenID
RP_callback?id_token=signed(identity)
12. id_token (JWT formatted)
● id_token represents identity of user in JWT format ie. who this user is
● JWT stands for Json Web Token
● JWT format is designed to reduce message size
Format :
base64UrlEncode(header_in_json_format) . base64UrlEncode(claim_in_json_format) . base64UrlEncode(signature)
Example:eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik4wSXdNa1UyUWtSQk5rWkNNRFF3UWtOQ05qQkJNVFZFT0RZM1JqSkdOalUxTWpNMk1qQXhRdyJ9
.eyJpZHBfaWQiOiJhdXRoMCIsImlzcyI6Imh0dHBzOi8vc3NvLmludC5hY2NvdW50cy5kb3dqb25lcy5jb20vIiwic3ViIjoiYXV0aDB8NDE2MmI0NjgtNjYzOC0zMDc4LWI5NW
EtMTY5MDI1MmVlM2NkIiwiYXVkIjoiMkhuYWNkY0U1U3g2a0k0TEhiTWtROWlVNzROVkRYVUwiLCJleHAiOjE0OTEwMjg4NjksImlhdCI6MTQ5MDk5Mjg2OX0.i05sBSb
M6LjW0q7tHuStkeKcMRRUgqu7UAlgJCWuhmb9-NAWmdmgb1Y7IKoRcNS-BtQnOWiFEQkooQGYRtyLHkHP1DFXqb066gd0hJnr5u7i1py-QrRNOgjYLaN5JZkw8Kmc
Y4CFpNqoJScQ2B7y3bCVYCVOC8pfwGeuY2THotbGBGAUXlWN3dEIVZg-m0jsBMrHcHd0w34xYULvPfrMurHk52cdJ-zkRf1Uyl1W1QrHjuJm4w4v5qc2LpYx28YWAoJ
4Bp1EHt0zlwtD-FJs8y_kjLTVm-LHrLylq87CPjccjOkxp-oLK4P9u77b9OFRspKCZF8X96XDcQVj1WaKRA
Base64 url decode Header
{"typ": "JWT",
"alg": "RS256",
"kid": "N0IwMkU2QkRBNkZCMDQwQkNCNjBBMTVEODY3RjJGNjU1MjM2MjAxQw"
}
Base64 url decode Claim
{
"iss": "https://accounts.google.com/",
"sub": "4162b468-6638-3078-b95a-1690252ee3cd", → this is UID of user
"aud": "2HnacdcE5Sx6kI4LHbMkQ9iU74NVDXUL",
"exp": 1491028869,
"iat": 1490992869
}
13. SSO bypass vulnerability
● Relying Party relies on subject aka “sub” and claims to know
the who the user is to create RP session.
● Service Provider uses various JWT libraries to validation JWT
and extract claims
● Attacker exploits below issues to bypass JWT validation
1) “none” algorithm in JWT header
2) weak secret key for HMAC signing i.e. HS256
16. How to prevent exploitation
● Avoid JWT validation libraries which supports “none” algorithm
● Use Asymmetric key signing/validation ie. RS256 and always use “kid” field to select
corresponding public key for validation
● If RP is expecting RS256 then reject any request which doesn’t have RS256 algorithm
● If RP is expecting HS256 ( OP enforcing) then client_secret must be at least 32
character long.
Affected ones
● NodeJS JWT - https://github.com/auth0/node-jsonwebtoken and https://github.com/kjur/jsjws
● Python - https://github.com/jpadilla/pyjwt/
● PHP - https://github.com/namshi/jose and https://github.com/firebase/php-jwt
17. Additional tips
● Apply additional validation on JWT claims
○ validate issuer represented by claim “iss” with expected IDP domain
○ validate audience represented by claim “aud” with your own client_id
○ ensure expiry represented by claim “exp” is greater than current time
○ validate nonce represented by “nonce” claim → send me email to know more about it
■ It can prevent both id_token replay and CSRF
○ Log as much as you can but strip any PII information from logs to detect id_token abuse
○ Never store id_token, treat it as mini SAML, create your own RP session (encrypted) to retain userInfo.
● Apply additional validations on SAML attributes after retrieval
○ validate issuer represented by “Issuer” with expected IDP entityID
○ Validate audience represented by “Audience” with your own SP entityID
○ Verify “InResponseTo” in SAML response which must be same as “ID” sent in SAML Request
○ Always implement SP Initiated flow to prevent CSRF using “ID” and “InResponseTo” pair, this will
prevent SAML response replay.