The last part in the series about Identity, Access Management, processes and flows is about Azure-related settings and deployment to Azure.
Content of the series:
- Needs and means, analysing the requirements for enterprise apps and available off-the-shelf solutions.
- Auth Flows, explaining relevant authentication/authorisation flows (OAuth2, OIDC) with sequence diagrams.
- Writing the code, nuts & bolts of coding a Transparent Auth Gateway in .NET.
- Deploying to Azure, explaining App Registrations and Firewall settings (Azure WAF / Front Door).
In the previous post, we implemented a bespoke authentication/authorisation service that
- transparently (without additional user interaction) confirms the identity with the linked Identity Provider (e.g. Azure AD tenant), supporting SSO;
- issues an access token with app-specific attributes;
- is self-hosted without reliance on third-party services.
Here, we make the implementation suitable for Azure hosting and configure an Azure AD tenant as the linked Identity Provider.
1. Azure App Registrations
Before your first test run, one needs to get the ducks in a row and align OAuth2 requirements with Azure AD implementation:
- Have an Azure AD Tenant (see how to set up one).
- Register your application with the Microsoft Identity Platform (also often referred to as ”App Registrations”, see the docs).
- Expose scopes and grant scope permissions (docs).
While the first step of acquiring an Azure AD Tenant is straightforward, all the fun begins with step #2 – App Registrations, an MS requirement to perform identity and access management in Azure AD (the Identity Provider). See the official answers on why it’s needed.
The key OAuth2 artefacts we receive from App Registrations are
- Client ID, a public identifier for the app (often referred to as Application ID, and to make things more confusing, it’s called Service Principle by MS);
- At least one scope that normally is used to limit an application’s access, but here it’s used to outline who can consent to use the app (hence, MS refers to them as ”published” or ”exposed” APIs). Note that you can further restrict the list of users allowed to use the app to an AD Group or explicitly selected AD users.
Our implementation uses OAuth2 Authorization Code Flow that relies on HTTP redirects that can’t be completely concealed from the user. Hence, we don’t need to add a Client Secret or certificates in App Registrations.
Meanwhile, the key security settings that we have to provide are as shown in the screenshot below:
- allowed URIs for redirecting back to our Auth Gateway (again, as the used flow relies on HTTP redirects);
- authentication of accounts in the selected tenant only.
As you see, we picked the ”Web” platform over a more lucrative ”Single-page application” option. It’s due to our Auth Gateway being NOT a SPA (actually, it has no front-end at all) and resolving the token via PKCE is not applicable.
All done here. If you spare a minute, it might be handy to also configure
- Optional claims to return more information about the user stored in Azure AD (note that the user has to consent to provide it on the first login to your app).
- Branding the login page.
You may want to have a look at the official examples for automating app registrations in PowerShell scripts.
And the last bit – check out the code on GitHub to test the configured App Registration.
2. Load Balancers
For corporate Azure customers using a Load Balancer for HTTP/HTTPS traffic (e.g. Azure Front Door or Application Gateway) to shield web services is a reasonable practise.
And Load Balancers would absolutely work with our Transparent Auth Gateway… if the it forwards a host header field that the origin uses to retrieve the targeted resource.
Here’s a screenshot of setting the ”Origin host header” for the Front Door (docs), as an example:
Otherwise, the Transparent Auth Gateway might experience one of these 2 issues:
- listing a private FQDN (behind the load balancer) in the OpenID configuration at
/.well-known/openid-configuration
; - returning HTTP 401 on all requests to
/.well-known/jwks
or/connect/authorize
end-points due to an unexpected host.
Keep the ”Origin host header” in mind when you get things off the ground.
3. Firewall settings
Firewall is an essential part of modern cloud environments, but it might be overly protective. Here I show the most common obstacles using the Azure Web Application Firewall (WAF) as an example.
In the premium pricing tier, the Web Application Firewall applies a vast set of rules to defend against common exploits and vulnerabilities. That gets many solution architects overly excited to the point of establishing a blanket rule of running the WAF in Prevention mode (see modes) by default in front of all resources.
As I already mentioned, OAuth 2 Authorisation Code Flow (RFC 6749) heavily relies on HTTP redirects and random markers to track the request source. As the requests fly through the Firewall, it can block some suspicious requests of the Auth Gateway…
Starting from a simple one, the redirect_uri
parameter of the authentication requests (as per the OIDC spec) gets passed around and contains a white-listed return URL on successful authentication. If the URI contains /
symbol (and it’s almost certain that it does), it may upset the WAF and triggers rule #931130 ”Possible Remote File Inclusion (RFI) Attack: Off-Domain Reference/Link”. Every OAuth2 server stumbles upon this WAF rule, here’s an example.
Now, to more interesting ones. OAuth 2 Authorisation Code Flow (RFC 6749) with PKCE (RFC 7636) heavily relies on cryptographically-random strings used in authorisation requests/responses:
state
(OIDC docs) to maintain the state between the request and callback, as a Cross-Site Request Forgery (CSRF, XSRF) mitigation by cryptographically binding the value of this parameter with a browser cookie.nonce
(OIDC docs), similar to thestate
, used to associate a client session with responses and to mitigate replay attacks.code_challenge
(OAuth2 docs) to issue thecode
that is verified by the server on issuing tokens.
Being random means they can contain any ASCII sequence (usually, with symbols like '
, =
, etc.) that may cause intermittent WAF blockage. Commonly triggered rules are from SQLI - SQL Injection group:
- #942450. SQL Hex Encoding Identified.
- #942440. SQL Comment Sequence Detected.
- #942430. Restricted SQL Character Anomaly Detection (args): # of special characters exceeded (12).
- #942120. SQL Injection Attack: SQL Operator Detected.
Even the generated tokens may trigger those SQLI - SQL Injection blockages. You can catch it on any of them: code
, id_token
, access_token
, refresh_token
.
And there are cookies… They are optional but the implementation in the previous article uses them on the Auth Gateway to avoid excessive redirects to the Azure AD if the user just confirmed his/her identity (see article #2 in the series with a detailed diagram). The user’s profile is stored in typical .AspNetCore.Cookies that essentially has an encrypted id_token
that make it fall out of WAF’s favour for the same ”SQLI - SQL Injection” reasons as the other tokens.
And not to mention additional parameters that could be sent by JavaScript client-side libraries that you may use. E.g. MSAL.js has this issue.
3.1. WAF mitigation strategy.
In the ideal world, developers work together with solution architects and DevOpsSec experts to produce a tailored solution for the app. If you are one of the lucky ones the best strategy would be
- Run the WAF in the Detection mode for a while in the DEV or TEST environment.
- Review the WAF logs.
- Adjust the rules accordingly. And try again (go to #1) till there’re no unreasonable blockages.
- Apply these rules’ exclusions to other environments.
If running WAF in the Detection mode is not an option, then you can add a set of specific exclusions for the rules mentioned above: #931130 and the four from SQLI - SQL Injection group (#942450, #942440, #942430, #942120). Like in this example:
Just keep in mind that some of those parameters can be passed in more than one way:
- the query string of the URL and the body of a POST request (like
code
,state
), - the query string and a cookie (like
nonce
), - and some, just in a cookie (
.AspNetCore.Cookies
).
Hence, for various ”Selector” values (the parameters listed above) we need to set ”Match variable” to ”Query string args name” and/or ”Request Body Post Args Name”. And for the used cookies (e.g. nonce
, .AspNetCore.Cookies
) – ”Request Cookie Name“.
3.2. Is that it with WAF?
That’s it in the short run and you’re safe pushing the build to PROD even on Friday night 😃.
But excitement aside, Web Application Firewall evolves along with the OWASP’s Core Rule Set (CRS) and the Internet threats. That means new rules will be added in the future and some of them may cause a false-positive request block of an OAuth2 step.
The best way not to miss out is to receive notifications on WAF blocks. For example, set up Azure Monitor alert rules.
Now you’re safe to run Auth Gateway in PROD.
That’s the end of the series. Thoughts? Leave a comment below, or on Twitter, LinkedIn, or Reddit.