The security of session tokens and session cookies is always an issue you must take into consideration – regardless of the fact if an SSO is used or not. Cookies and session tokens are stored in a browser and protected using various mechanisms.
- While they are stored in a browser, they can only be accessed by a
specific website due to the Same-Origin Policy. So, if a cookie was
set for the test.com, it would not be possible for the attacker.com
to gain access to your cookie. However, you should always remember to
use a trusted browser (Chrome, Firefox, Edge, Safari) and keep it up
to date.
- While the cookies are sent to a server and back, they are protected
in transit using an encrypted connection (HTTPS). Even if an attacker
is on the same Wi-Fi, they may capture the network traffic, but due
to encryption, they will not be able to extract the cookies from it.
- If the application has any vulnerabilities (e.g., Cross-Site
Scripting), it might be possible to impersonate the user, access
their data, or perform actions from their account, however, this
doesn't necessarily mean that the attacker will steal the cookies
(and this is not always possible due to the HttpOnly flag) – the
attacker might just perform actions directly from JavaScript.
So, stealing tokens is one thing. Another is being able to create a valid token for any user on your own – and this is a whole different story.
In web applications, we mostly have cookies and JSON Web Tokens (JWT).
- A cookie is a random string saved in a database and tied to a
specific user. If the entropy of a cookie is not sufficient (or is
generated predictably), an attacker might be able to generate a valid
cookie and impersonate a user with a specific session. You can find
out more about cookies and session management in OWASP Cheat Sheets.
If you are interested in the concept of randomness, check out this
article about the entropy of UUIDs.
- A JWT is a stateless token signed cryptographically. It doesn’t need
to be saved in a database – it contains all required information,
like when it is valid, for whom, and what user it belongs to. You
only need to verify the signature. The JWT can be signed using
symmetric cryptography with a secret (HMAC) or asymmetric
cryptography (RSA). The most obvious attack that would make it
possible to impersonate a user is made by gaining access to a secret
or private key and being able to create a valid token for any user.
You can read more about JWT attacks on PortSwigger Academy.
Considering all the above information, let's now move on to the SSO.
First, Identity Providers not only provide tokens – they provide confirmation of the user's identity, and providing a token is just a way of doing it. The Identity Provider and the Service Provider have a trusting relationship – which is established during the configuration process by providing proper certificates, secrets, and URLs.
In SAML, the flow may look as follows:
A user initiates the login flow at the Service Provider's website.
The Service Provider redirects the user to the Identity Provider
with an AuthnRequest ("Hi Identity Provider, could you please
authenticate this user for me?").
The Identity Provider authenticates the user, e.g., by asking the
user for their login and password. If the provided credentials are
correct, the Identity Provider does two things:
a. Creates a session in the Identity Provider's application, so the next time the user tries to log in using this Identity Provider, they do not have to provide the credentials again (the session might expire or require reauthentication after a specific period, e.g., a week, but if a user tries to log in the next day, it will be transparent for them).
b. Creates a signed XML document called SAML Response, which contains the requested information about the user's identity as well as some technical details (when the SAML Response was created, who signed it, how long it is valid, for whom it is valid). Then they send the SAML Response back to the Service Provider.
The Service Provider verifies the SAML Response by checking the
signature against Identity Provider's certificate that the Service
Provider has in their configuration. If the SAML Response is valid,
the Service Provider can:
a. Create a new session in their application (using a cookie or a token – however they want). This is the most common way – hence, the SAML Responses are usually valid for 3-5 minutes and are only possible to use once.
b. Use the received SAML Response as a session token. However, it would require the SAML Response to be valid longer and allow multiple usage of the response. It is also not very practical, as SAML Responses are quite large compared to a cookie or JWT.
You can read more about SAML and possible attacks on our blog.
In OpenID Connect, the most popular flow looks as follows:
The user initiates the login flow at the Service Provider.
The Service Provider redirects the user to the Identity Provider.
The Identity Provider authenticates the user and creates a session
in the Identity Provider's application. Then the Identity Provider
redirects the user back to the Service Provider with a code.
The Service Provider receives the code and sends it to the Identity
Provider via back channel (not via user’s browser). This connection
is done by the application backend, it is encrypted and
authenticated (the Service Provider must provide the client_id and
client_secret). The Identity Provider checks the code and its
validity, they create an id_token and send it back to the Service
Provider. The id_token is a JWT – it is a JSON containing user data
signed by the Identity Provider, which can be verified
cryptographically by the Service Provider.
After receiving the id_token, the Service Provider can now do one of
the following:
a. Use the id_token as a session token.
b. Create their own session based on the id_token. This solution is chosen much more often since the applications usually allow other authentication methods (login form, different OIDC providers, magic links etc.), and the applications would like to produce the same session type regardless of the login method.
It should be noted that if the attacker can steal the code in the OpenID Connect (e.g., due to an open redirect or improper web message configuration), they can use it to authenticate and impersonate the user, even though they never touched any of the session tokens. The PKCE extension helps mitigate some kinds of attacks but is also not bulletproof.
The last question is how you can detect if a token is used in an unauthorized manner. Here are some tips:
- Remember about logging & monitoring.
- Look out for extensive login attempts, especially failed ones.
- Verify if the token (SAML Response, OIDC code) has not been used
before. Fail and log if it is expired or already been used.
- Always check if your application is the designated recipient of the
token. If not – the token is probably misused.
- Allow the user to list and end all their sessions.