Azure API Management - Incoming certificate is ignored - azure

I have implemented an API Management logic in the inbound policies so that some of the received certificate information is passed to an external service that does some checks on that information and returns true or false depending on the case. If validated, the call will continue through the backend URL.
The API Management is implemented in Consumption tier, with its corresponding Request client certificate properly set to Yes.
The problem is, this logic is only executed when the incoming certificate includes the private key (e.g. .pfx format). When the incoming certificate is public, the API management doesn't even execute the inbound policy, but rejects the incoming call with a 403 Forbidden.
I know it doesn't get to the inbound policy because I added this piece of code at the very beginning for testing purposes:
<choose>
<when condition="#(context.Request.Certificate == null || context.Request.Certificate.Issuer != "My expected certificate issuer")" >
<return-response>
<set-status code="403" reason="Invalid client certificate" />
</return-response>
</when>
</choose>
And when the certificate is public, I don't get the 'Invalid client certificate' message but the standard 'Forbidden'.
As all available setups in the API Management refer to certificates with key (e.g. you cannot upload in the Certificates tab a public certificate, and when selecting one from the key vault, it will include of course the private key), I wonder whether the incoming certificate must also include the key as a requirement.
Do you know if there is any requirement on that matter that forces the incoming certificate to include the private key?
If this is not the case, do you know what might be missing in my setup?

Related

Azure APIM - OAuth 2.0 - Internal Service API Authentication Issue

I need to expose an internal service API which runs on a server of our domain. The service is written in Java and is a SOAP service supporting basic auth. I need to implement Client Credentials flow via OAuth 2.0 and expose it via APIM (Azure API Management). The service will be used by external client applications (daemons).
What I've done so far is to create 2 Azure App Registrations: 1 Client App and 1 Proxy App.
App Roles have been defined in the proxy app and the client app has permissions on the proxy app.
In the client app I have setup Client Secret.
In the APIM initially I created a static http 200 response (via inbound policy) in order to test the service. Works fine.
<return-response>
<set-status code="200" />
<set-header name="content-type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>{
"status": "200",
"message": "OK"
}</set-body></return-response>
Then added an inbound JWT validation policy. I request a token from MS via Postman, pass the Bearer token to the API endpoint call and the token is being validated. Works fine.
Then I need to "route" the request to our internal service. We have already setup Azure Application Proxy, so I edit the Internal Url of the proxy enterprise app and set it to the internal service Url. I also remove the inbound static 200 response policy in order to allow the request to reach my internal service.
In such case if the proxy enterprise application "pre authentication" is set to passthrough it works fine but if it is set to "Azure Active Directory" it doesn't - I get a 200 response but requests to sign-in (html).
In case I leave the 200 reply from APIM (not reaching my endpoint) it works no matter if the enterprise application is set to "Passthrough" or "Azure Active Directory".
Any ideas?
Thank you in advance,
Dimitris

Unable to send test requests to backend APIs using the Azure APIM interactive portal

Using the Azure portal, I’m unable to send test requests to the Echo API (and all other backend APIs).
When sending a request, I’m getting the following error:
HTTP/1.1 401 Access Denied
cache-control: private, s-maxage=0
content-length: 152
content-type: application/json
date: Tue, 12 Apr 2022 05:13:28 GMT
vary: Origin
www-authenticate: AzureApiManagementKey realm="https://AAAA.azure-api.net/echo",name="Ocp-Apim-Subscription-Key",type="header"
{
"statusCode": 401,
"message": "Access denied due to missing subscription key. Make sure to include subscription key when making requests to an API."
}
The request works fine when I tick the “Bypass CORS proxy” checkbox and through Postman.
I have the following global inbound CORS policy:
<policies>
<inbound>
<cors allow-credentials="true">
<allowed-origins>
<origin>https://AAAA.developer.azure-api.net</origin>
<origin>https://AAAA.azure-api.net</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="300">
<method>*</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
</inbound>
<backend>
<forward-request />
</backend>
<outbound />
<on-error />
</policies>
and the inbound base policy set on the Echo API.
I haven't expereinced this problem previously. Any ideas how I can bupass the CORS error while submitting test request in the APIM portal?
HTTP/1.1 401 Access Denied
www-authenticate: AzureApiManagementKey realm="https://AAAA.azure-api.net/echo",name="Ocp-Apim-Subscription-Key",type="header"
"message": "Access denied due to missing subscription key. Make sure to include subscription key when making requests to an API."
In this Troubleshooting Steps of Unauthorized errors (401) while invoking APIs in Azure, it is mentioned clearly:
Due to Wrong Ocp-Apim-Subscription-Key, this error occurs.
When you create the APIM, the Echo API is subscribed to built-in subscriptions by default.
Each subscription has two subscription keys that can be used.
Scenario 1:
By default, Echo API is registered to the Built-in all-access subscription so it will work perfectly until the subscription key is matched:
Scenario 2:
There are 2 more product subscriptions that come by default when an APIM instance is created which are Starter and Unlimited.
When the API is subscribed to that product subscriptions, then the subscription key passing in the header should match with the Original Product Subscription Keys available in the Subscriptions Menu.
Here, the Echo API is subscribed to both the products Starter and Unlimited as shown in 1st Image.
That Product Subscriptions has given with some permissions called Administrators, Developers and Guests. Any one among these should have on the user to access the APIs subscribed these products.
In the 3rd Image, you can see what APIs are subscribed to Starter Product like Echo API.
If any of the above workaround did not solve the issue, please refer the troubleshooting steps doc provided that shows all of the causes that produces this specific error 401 Unauthorized and Missing the Subscription Key along with the resolution.
ByPass CORS option allows the requests originating from any domain. Sometime, allowing the cross-domain access also fixes the issue. Try Azure Cross-domain policy as given below to allow access from 'any' domain (you can specify your domain too).
<cross-domain>
<cross-domain-policy>
<allow-http-request-headers-from domain='*' headers='*' />
</cross-domain-policy>
For details refer MS documentation on managing cross domain access : https://learn.microsoft.com/en-us/azure/api-management/api-management-cross-domain-policies
I have engaged with Microsoft on this and they are investigating the issue. As per MS initial investigation “Microsoft Defender for Cloud Apps" creates a Proxy that intercepts all requests going out of Azure portal and it seem like MCAS proxy is either removing or modifying headers from the outgoing request thus causing this behaviour. Microsoft has pointed to the following document for reference: Troubleshooting - What is cas.ms, mcas.ms, or mcas-gov.us?. MS has advised that they don’t have any ETA for the fix and that they are investigating further. Their recommendation is to check the Bypass CORS proxy option as workaround for the time being.

Azure API Management policy caching of JWKs for JWT validation

I'm trying to implement caching of public key provided by openid-configuration/jwks endpoint of our JWT provider. I want to use cached value for validation of signature of incoming request. We want to have cache in place in order to lower requesting jwks endpoint. So we decided to create custom APIM policy in order to do that. After few hours of struggles I found myself clueless. Therefore I would like to address some questions about APIM xml policies.
How can I read request JWTs kid or x5t in policy? Regarding to APIM policy expressions calling AsJwt() returns Jwt object which does not contains Header property which should contain required fields
I tryed Id, Claims with no successful reach of kid or x5t (<set-variable name="requestKid" value="#(context.Request.Headers.GetValueOrDefault("Authorization","").Split(' ')[1].AsJwt()?.____)"/>)
When retrieving JWKs from the provider endpoint I'm not able to store various fields of response into separate context variables, it always fails on second <set-variable. Seems like (IResponse)context.Variables["jwksResponse"]) is null when calling second time. Does context variables get destroyed after first read?
<send-request mode="new" response-variable-name="jwksResponse" timeout="10" ignore-error="true">
<set-url>https://some.identity.server/.well-known/openid-configuration/jwks</set-url>
<set-method>GET</set-method>
</send-request>
<set-variable name="jwksn" value="#(((IResponse)context.Variables["jwksResponse"]).Body.As<JObject>()["keys"][0]["n"])" />
<set-variable name="jwkse" value="#(((IResponse)context.Variables["jwksResponse"]).Body.As<JObject>()["keys"][0]["e"])" />
How can I set key exponent and modulus attributes? When I use variables from previous step I got xml validation Error: "ValidationError" - "Error in element 'validate-jwt' on line 8, column 4: value is not a valid base64url string." while It works when when hardcoded. (I also tried hacks with Convert.ToBase64String(), with no success, to me seems like xml validator issue - can it be overriden?)
<issuer-signing-keys>
<key e="#((string)context.Variables["jwkse"])" n="#((string)context.Variables["jwksn"])"/>
</issuer-signing-keys>
Can I use x5c value retrieved from our JWKs provider as key value? like so:
<issuer-signing-keys>
<key>#((string)context.Variables["jwksx5c"])</key>
</issuer-signing-keys>
After further investigation we found gitHub thread regarding caching of JWKs when using JWT validation with OpenId. It seems JKWs are cached for 1 hour and also recached when validation fails. However this behavior is not stated in documentation.

Why a PFX file is required for Client Authentication in Azure API Management for external consumer?

This article explains that a client certification authentication is possible with azure api management. Here is an excerpt from this article:
API Management provides the capability to secure access to APIs (i.e., client to API Management) using client certificates. Currently, you can check the thumbprint of a client certificate against a desired value. You can also check the thumbprint against existing certificates uploaded to API Management.
It contains a link to another article which explain how to add a client certificate to azure api management for the purpose of using client certificate in the context of using client certificate authentication when communicating with back end service only. This obviously requires loading a PFX file (which contains the private key).
However I am interested in using the api management for validating a client certificate from the consumer of my API hosted in the api management. The first article seemed to indicate the api management can be receiver of the client certificate for validating consumers. One of the policies from the first article is shown below:
<choose>
<when condition="#(context.Request.Certificate == null || !context.Deployment.Certificates.Any(c => c.Value.Thumbprint == context.Request.Certificate.Thumbprint))" >
<return-response>
<set-status code="403" reason="Invalid client certificate" />
</return-response>
</when>
</choose>
This means I should be able to upload only a public portion of client certificate from my API consumer. Yet, it does not allow non-pfx files. What am I missing here?
Sorry for the delay. The certificates that you can upload are primarily for authenticating with a backend. The recommended solution for doing client certificate validation is just to check the thumbprint value in the policy. If you feel that there is value in being able to upload client certs with just the public key, please add it to our uservoice. http://aka.ms/apimwish

use X509Certificate field in SAML assertion or an external cert file.

As Identity Provider we send a SAML assertion request to Service Provider and then they validate our signature in assertion using our certificate. SAML assertion contains an optional field called X509Certificate which is certificate of assertion issuer. (our certificate). My question is that from a security perspective is it better Service Provider use this field in each assertion for validating signature or use an external certificate file.
It's a very bad idea for them to only trust the public key value that is included in a signed message. Who's to say that someone couldn't just forge your EntityID and send them random SAMLResponses with some user data? The signature of the message would be valid, and they are using the key included in the message to validate the signature, right?
The benefits of offline key exchange are well known: Your SP has securely stored your public key and will always use to it validate the signature of your messages unless you instruct them otherwise (key rollover/update). If you include your certificate in the message, the SP will compare the two certificates first to ensure they match and then they should use the copy they have stored previously (assuming they match). Otherwise the message is rejected.
However, if you want an SP to trust the public key you include in your messages, the SP must be able to ensure that it is YOUR certificate that is being used. There are some smart folks at Ping Identity who have thought about this SP DSig Validation Use Case -- you can find a description of how they do this in their "Anchored Certificate" model for DSig Validation.
https://documentation.pingidentity.com/pingfederate/pf80/index.shtml#concept_digitalSigningPolicyCoordination.html
I don't really get the 11/19 answer. If you send your BinarySecurityToken (BST) in the Assertion for signature validation, and the receiver has an entry in his trust store with this public certificate, you should be good. In order for this to work:
1) The receiver must require that the assertion is signed
2) The receiver must check the signature verifying certificate in the assertion against a trust store.
3) DO NOT just trust the dn/issuer of the signer instead of using a trust store; that can be faked in a signing certificate.
If these things are followed, you have verified that the message is signed by the holder of the private key and that the assertion has not changed in flight. You then trust that holder, therefore you can proceed.
If the receiver doesn't require that an assertion is signed, anyone can send anything to the receiver.
If the receiver doesn't check a trust store for the verifying certificate, anyone can send a signed anything to the receiver.
The advantage of sending the BST in the message is that when your IdP certificate expires and you have to get a new one, the client only has to add your new certificate to his trust store instead of changing the configuration of his application.

Resources