Whether you are in charge of deciding what Cloud solution to choose for your organization or you are a Security Professional trying to decide what Cloud technology to learn, when it comes to choosing the right Cloud solution there are multiple factors that need to be reviewed such as Services/Features, Costs and obviously Security which we will focus on.
AWS has been indeed first to market and has been a market leader ever since. However, as we will see, being second has its advantages and allowed Azure to design better and avoid some mistakes.
Even though other articles comparing these two cloud providers give an edge to AWS, based on our experience conducting both AWS and Azure assessments, we see misconfigurations affecting AWS more often than Azure due to a lack of “Secure by default” settings.
When deciding based on Services and Costs the choice between AWS and Azure would typically depend on your specific requirements and the needs of your organization. Both AWS and Azure offer a broad and deep set of capabilities with global coverage and both offer a pay-as-you-go pricing model with discounts applicable depending on the amount of resources consumed.
However, let’s take a closer look at the Security differences between the two cloud providers by reviewing the design choices, default settings and vulnerabilities that have been exploited over the years.
There are situations where a discovered Server-Side Request Forgery (SSRF) vulnerability (with read access) within a web application does not have a high impact because it cannot be leveraged to extract sensitive information.
However, SSRF has become a very high impact vulnerability if discovered on web applications hosted on AWS, mainly because the Instance Metadata Service API Version 1 (IMDSv1) can be interrogated through a simple GET request from the EC2 instance. Through the SSRF vulnerability it is possible to initiate the GET request and receive from the Metadata Service API a session token with the permissions of the EC2 instance. This could lead to privilege escalation within the cloud environment.
AWS has introduced Instance Metadata Service Version 2 (IMDSv2) which prevents Server-Side requests from interrogating the Metadata Service API and obtaining a session token by requiring a PUT request instead of a GET request, therefore it is not possible to initiate a session with IMDSv2 using a SSRF vulnerability.
However, when creating a new EC2 instance, Version 1 of IMDS is selected by default and during our assessments we find many cases of new EC2 instances still using IMDSv1. Therefore, even though AWS offers a secure option through IMDSv2, it also allows room for misconfiguration by having IMDSv1 selected by default when creating a new EC2 instance.
On the other side, Azure has mitigated this issue from the start by requiring the presence of the “Metadata: true” header when initiating a session with the Metadata Service, which cannot be added through a Server-Side request thus preventing SSRF.
More details about this issue: https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf
Similar to the previous attack vector but not as popular, Lambda functions affected by some kind of file read vulnerability (LFI, SSRF, XXE) can also lead to exposing IAM credentials stored in the Environment Variables (file:///proc/self/environ) of the container running the code. More details about this vulnerability: https://hackingthe.cloud/aws/exploitation/lambda-steal-iam-credentials/
Unlike AWS which stores these access credentials directly in Environment Variables, Azure Managed Identities that are used in multiple resource types (App Services, Azure Functions, Automation Accounts, etc.) use two environment variables called IDENTITY_ENDPOINT and IDENTITY_HEADER that contain information required to perform a specific HTTP GET request to a localhost endpoint that returns the Managed Identity access credentials (similar to how the Azure Metadata Service works). Therefore, you cannot retrieve access credentials through a file read vulnerability, instead you need full code execution on the container. Details about Azure Managed Identity headers: https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity#rest-endpoint-reference
Even though the Azure implementation is better designed to protect against credentials exposure which could lead to privilege escalation, mistakes have been made in the past that allowed critical Cross-Account unauthorized access to any other Azure customer accounts using services such as Azure Automation Account. This vulnerability was however mitigated promptly: https://orca.security/resources/blog/autowarp-microsoft-azure-automation-service-vulnerability/
One issue that we often encounter is regarding certain resource types, such as S3 Buckets/Storage Accounts and Container Registry images, that are exposed unintentionally to the Internet. Often times these resources do not contain anything sensitive of value, but it can be time consuming to review all the exposed resources to identify if they contain sensitive information, that is why in order to avoid misconfiguration when creating these resources the default configuration should have public access disabled and authentication should be required.
Public S3 buckets have been a serious security issue that has affected many organizations since 2006 when they were initially introduced. This is all due to the very simple fact that when they are created, the default configuration allows accessing them from the Internet without any type of authentication. It is up to the Cloud Admins to change these settings and restrict access.
Over the past decade this misconfiguration has resulted in many data leaks and bug bounties. However, after over a decade of many organizations making configuration mistakes, AWS has decided to change this default behavior in 2023 and block Public Access for new S3 Buckets.
Azure Blobs on the other hand, even if public access is enabled you need to configure authentication because “anonymous access” is disabled. Due to this “Secure by default” configuration that helps prevent mistakes and accidental exposure, we have rarely encountered issues with exposed Azure Storage Accounts.
The same goes for other resource types such as Container Registries where public access is sometimes preferred usually because 1) Admins are lead to believe that the Container Images have been “cleaned” and do not contain anything sensitive (tools such as Gitleaks, Trufflehog and Keyhacks can be useful in finding and verifying hidden secrets) 2) It makes network access configuration easier.
Fewer leaks have been encountered throughout the years on Azure compared to AWS, but even though disabling anonymous access and requiring authentication can help Azure Admins avoid accidental leaks, human mistakes still happen. (38TB Data Leak by Microsoft AI researchers – Wiz Article)
When not using the AWS Management Console, the main method of connecting programmatically or through CLI to the AWS Cloud environment has been Access Keys, and for good reason, they are indeed a convenient way to allow granular user access to AWS resources. However, this method places the responsibility on each individual user to securely handle these keys and ensure that they are stored properly, not shared or exposed to unauthorized individuals, and considering that Access Keys are long-term credentials that do not expire, they need to be deactivated when no longer used.
As you might have guessed, this has lead to many situations where keys were mishandled in various ways, hardcoded into application code or forgotten in configuration files (such as ~/.aws/credentials).
We recently conducted an investigation where we scanned over 30.000 public Amazon Machine Images and public Azure Shared Images (these images are shared by the community and used to create AWS EC2 Instances and Azure VMs) in order to find hidden secrets that most likely have been forgotten by those who exposed these images to the Internet. Results show that AWS Access Keys are one of the most commonly mishandled or forgotten secrets.
AWS is recommending to avoid using long-term Access Keys and instead use the IAM Identity Center which allows Interactive Login including for AWS CLI V2, but most users and organization continue to use Access Keys because it is the default option, it is more familiar and easier to deploy.
On the other hand, Azure users wanting to authenticate and access Azure services (either through Web Portal, Azure Cloud Shell or CLI) must do it through Interactive Login which generates short-term access tokens (Refresh tokens expire after 90 days) and discourages using user account sessions for long-term purposes in order to avoid mishandling. If you require long-term credentials for a Service/Application then Azure provides the option to generate Service Principals (which are long-term credentials) through App Registrations.
Moreover, Azure short-term Refresh tokens are no longer stored plain-text in the “accessTokens.json” file on the local system, but instead they are encrypted: https://learn.microsoft.com/en-us/cli/azure/msal-based-azure-cli
We can all agree that AWS IAM offers a great deal of flexibility and control through the use of multiple Policies (such as Trust Relationship Policies, Service Control Policies, Resource-Based Policies, Identity Policies, Permission Boundaries, Session Policies) and through permissions such as “sts:AssumeRole” and “sts:PassRole“.
However, allowing great control does not guarantee great control. When highly complex environments with specific requirements are managed by multiple people, this can lead to an ambiguous Policy Evaluation Logic where even experienced administrators can sometimes fail to properly restrict access and avoid privilege escalation.
A very important aspect of IAM Policies is that the same JSON document contains both the permissions that are allowed and the target resources where access is granted. So everything you need to specify to enable access is grouped together in one place. As a result, it is enough for a wildcard “*” to be placed in the wrong place, which actually happens relatively often, and just like that you accidentally either allow too many permissions or allow access over all resources.
Azure uses the classic Role-Based Access Control (RBAC) architecture that I believe most IT Admins are much more familiar with.
Unlike AWS, in Azure the permissions and target resources are “decoupled”. When creating a Role in Azure, the JSON document contains only the defined permissions. A Role Assignment needs to be performed in order to apply those permissions to a specific Scope: resource, resource group, subscription. (more details: AWS, Azure and GCP: The Ultimate IAM Comparison)
When you apply a very permissive policy to an AWS user you might accidentally give access to more resources than required, however, when you assign a role to an Azure user you have to mention the exact resource where that role applies. I know this difference might seem basic but it could prevent admins from assigning very permissive rights without being aware of it.
Take for instance the following image where the default Azure “Reader” role has read access over any resource and unlimited “assignableScopes” (assignableScopes means where it could potentially be applied, not where it does apply). Only after performing the secondary Role Assignment step and mentioning the specific target resource, the permissions will take effect only on that defined resource.
I believe this decoupling is essential in helping prevent misconfiguration as it provides transparency and makes it harder to accidentally allow access over all resources. Moreover, the “evaluation logic” is simplified in Azure as you only need to take into account Inheritance from upper levels (Resource <- Resource Group <- Subscription).
Based on our assessments, over-privileged permissions and privilege escalation scenarios are encountered more often on AWS than on Azure.
Security misconfigurations and bad practices are the main reasons breaches occur in the Cloud. Therefore, when reviewing Cloud providers from a security perspective it is crucial to compare how well they protect company users from making configuration mistakes by implementing “secure by default” configuration settings.
Even though both Cloud providers enable companies to achieve a high security environment, AWS places more responsibility on the company users to implement secure settings. As a result, Azure’s default security settings and design have resulted in less issues than on AWS over the years, and it is all due to differences like the ones we mentioned. Not being first to market has allowed Microsoft to make different design choices that sometimes might help avoid incidents.
Therefore, if you value security when choosing between AWS and Azure, either for your company or even personal use, consider the importance of these key differences and how could they impact your situation, especially if you have a complex environment with many resources and permissions.
On the other hand, if you are a Security Professional interested in learning new Cloud technologies and are undecided between the two, I believe AWS can be more fun as it can lead to more interesting security findings.