Enforce the global policy in Azure API Management

Azure API Management has a very powerful concept of policies: logic that you can inject in the request or response pipeline of your APIs.  These policies can be defined on four levels:

  • All APIs: a global policy that is applicable for all your API calls
  • Product: a policy that gets executed when the API is accessed via a subscription key linked to this product (optional)
  • API: a policy that gets executed for all API methods on a specific API
  • Operation: a policy that runs for a certain API operation only

The All APIs policy is very handy, because it gets executed for every API call.  It is often used to secure all your APIs in a single policy, to configure central monitoring or to hide stack traces.  However, did you know that it is not guaranteed that this policy will execute?

The problem

The effective policy that gets executed is typically a combination of those different levels.  Within the XML policy, the <base/> element determines the order in which they get executed.  The <base/> element refers to the content of the “parent” policy, which can contain on its turn another <base/> element.  If, for some reason, somebody removes the <base/> element from the policy, the global policy is not executed.  This might lead to a huge security issue.

The solution

I’ve found two possible solutions and they are preferably used together!

Validate XML policies within the build pipeline

In the build pipeline, you can execute the following PowerShell script against every policy XML file, to ensure that it contains the <base/> element in every section.

[CmdletBinding()]
param (
$PolicyString
)
Write-Host $PolicyString
$PolicyXml = new-object System.Xml.XmlDocument
$PolicyXml.LoadXml($PolicyString)
if($PolicyXml.SelectNodes("//inbound/base").Count -eq 0)
{
Write-Error "Missing base element in the inbound section"
}
if($PolicyXml.SelectNodes("//outbound/base").Count -eq 0)
{
Write-Error "Missing base element in the outbound section"
}
if($PolicyXml.SelectNodes("//on-error/base").Count -eq 0)
{
Write-Error "Missing base element in the on-error section"
}

Validate your API Management instance

As an alternative, you can connect to your API Management instance itself and validate all policies on each level.  The following script performs this task for you:

[CmdletBinding()]
param (
$ApimName,
$ApimResourceGroup
)
function Is-ValidPolicy {
[CmdletBinding()]
param([string] $PolicyString)
if(![string]::IsNullOrEmpty($PolicyString))
{
$PolicyXml = new-object System.Xml.XmlDocument
$PolicyXml.LoadXml($PolicyString)
if($PolicyXml.SelectNodes("//inbound/base").Count -eq 0 -or $PolicyXml.SelectNodes("//outbound/base").Count -eq 0 -or $PolicyXml.SelectNodes("//on-error/base").Count -eq 0)
{
return $false
}
}
return $true
}
$ApimContext = New-AzApiManagementContext -ResourceGroupName $ApimResourceGroup -ServiceName $ApimName
$Apis = Get-AzApiManagementApi -Context $ApimContext
foreach($Api in $Apis)
{
Write-Host "Validating API '$($Api.Name)'"
$ApiPolicy = Get-AzApiManagementPolicy -Context $ApimContext -ApiId $Api.ApiId
if(!(Is-ValidPolicy -PolicyString $ApiPolicy))
{
Write-Error "Missing base element in the API policy of $($Api.Name)"
Write-Host $ApiPolicy
}
$Operations = Get-AzApiManagementOperation -Context $ApimContext -ApiId $Api.ApiId
foreach($Operation in $Operations)
{
Write-Host "Validating Operation '$($Operation.Name)'"
$OperationPolicy = Get-AzApiManagementPolicy -Context $ApimContext -ApiId $Api.ApiId -OperationId $Operation.OperationId
if(!(Is-ValidPolicy -PolicyString $OperationPolicy))
{
Write-Error "Missing base element in the '$($Operation.Name)' operation policy of $($Api.Name)"
Write-Host $OperationPolicy
}
}
}
$Products = Get-AzApiManagementProduct -Context $ApimContext
foreach($Product in $Products)
{
Write-Host "Validating Product '$($Product.Title)'"
$ProductPolicy = Get-AzApiManagementPolicy -Context $ApimContext -ProductId $Product.ProductId
if(!(Is-ValidPolicy -PolicyString $ProductPolicy))
{
Write-Error "Missing base element in the product policy of $($Product.Title)"
Write-Host $ProductPolicy
}
}

Conclusion

Not many people are aware of the potentially dangerous impact of the <base/> element.  That’s why it is important to enforce its presence, certainly when you are relying on a global policy that arranges your security centrally.  I’ve raised a feature request to the product team, to introduce a setting that allows to enforce this behavior.  I hope it will make it to the product!

Sharing is caring!
Toon

ABOUT

MEET THE YOUR AZURE COACH TEAM

Your Azure Coach is specialized in organizing Azure trainings that are infused with real-life experience. All our coaches are active consultants, who are very passionate and who love to share their Azure expertise with you.