DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • 12 Expert Tips for Secure Cloud Deployments
  • Keep Your Application Secrets Secret
  • Dynatrace Perform: Day Two
  • How To Reduce the Impact of a Cloud Outage

Trending

  • Stop Writing If-Else Spaghetti: Architecting Cleaner Java with the Strategy Pattern
  • Slopsquatting: Building a Scanner That Catches AI-Hallucinated Packages Before They Reach Production
  • Beyond Conversation: Mastering Context with Claude Code Skills and Agents
  • What Is SQL Injection and How Can It Be Avoided?
  1. DZone
  2. Software Design and Architecture
  3. Security
  4. Why AWS and Azure Handle Data Perimeter Differently

Why AWS and Azure Handle Data Perimeter Differently

AWS and Azure handle identities and audit logging in fundamentally different ways, changing what you see in your security logs when someone tries to access your data.

By 
Suresh Gururajan user avatar
Suresh Gururajan
·
Aug. 13, 26 · Analysis
Likes (0)
Comment
Save
Tweet
Share
94 Views

Join the DZone community and get the full member experience.

Join For Free

AWS can send audit logs to an attacker’s account unless denials are enforced at the network layer, while Azure doesn’t log network-block requests at all. 

The concept of a data perimeter was popularized by AWS [1] to establish organizational boundaries around identities, resources, and networks. In simple terms, AWS provides access controls to ensure that trusted identities access trusted resources from expected networks while blocking all outside access. 

This article explores how different cloud providers handle resource access logs and how it relates to data protection. It sets up an experiment where an outside identity with valid credentials accesses a trusted resource and is blocked by a policy in one of the scenarios. The experiment explains two scenarios that differ in where the deny decision is enforced. 

We find that the same request for resource access produces different log artifacts in AWS and Azure. AWS sends access logs containing caller-controlled metadata in both the identity and resource-owner accounts unless a network layer explicitly denies access. However, in Azure, resource access logs are only logged at the resource-owner’s subscription, and when access is blocked at the network layer, nothing is logged there either. Both behaviors have consequences for security teams collecting and analyzing audit logs. This article walks through both scenarios with lab experiments and reproducible code. 

Background 

AWS and Azure treat identities differently. In AWS, identities are not centralized into one single place — instead, they live at the account level. For example, if an organization contains 10 accounts, identities can be created in each of the 10 accounts. In comparison, in Azure, identities are centralized into one Entra ID tenant. Since a tenant is linked to multiple subscriptions containing the company’s resources, identities from the same tenant are configured to access resources inside subscriptions. In summary, the resource-owning entity in AWS (the account) also holds identities, whereas in Azure the resource-owning entity (the subscription) does not hold identities – those live in the Entra ID tenant. 

Secondly, AWS and Azure treat access logging differently. In AWS, CloudTrail logs API calls at the account level. For cross-account access, AWS lets customers configure CloudTrail such that when data events are enabled, the caller account and the resource-owning account get access events. For example, if an identity in Account-A accesses a resource in Account-B and gets denied, then the deny audit entry is logged in both Account-A and Account-B. This mirroring is what makes caller-controlled metadata visible to a malicious actor’s account [2].  

In contrast, in Azure, resource access logs (for example, StorageBlobLogs) live in the storage account in the subscription, whereas identity logs (Entra ID) live with the tenant. These are separate systems with no automatic mirroring. This difference sets up why a correlation problem exists and why a network-layer block does not produce logs at the resource layer. 

Threat Model 

The threat model is as follows: an attacker brings their credentials inside a corporate network and accesses the company’s resource (like an S3 bucket). By doing this, the attacker tries to exfiltrate company data by encoding sensitive information in the HTTP user agent header, a caller-controlled field that appears in access logs. This allows data to leave the corporate environment in small chunks across multiple requests. 

The second threat is more nuanced. A security team that relies on resource-layer logs to detect unauthorized access attempts will miss requests that are blocked before reaching the resource. If the network drops the request silently, the resource (service) never logs it. An attacker who knows this can probe a corporate environment repeatedly without appearing in the audit trail that the security team is monitoring. 

Experiments 

AWS Experiment 

To set up this experiment, we have three accounts: a credential-owning account (identity), a VPC-owning account, and a resource-owning account. The identity is a Lambda function that tries to access an S3 bucket (resource). The Lambda function runs from a private subnet in a VPC and accesses the S3 bucket through an S3 VPC endpoint (AWS PrivateLink). All audit logs are sent to a third account – this is a typical Control Tower setup [3]. 

We test two scenarios: 

  1. The bucket policy denies all untrusted identities — assume that the bucket policy denies access to our identity. However, the VPC endpoint policy allows all cross-account access. 
  2. The bucket policy allows this untrusted identity. However, the VPC endpoint policy disallows cross-organization access. 

Scenario 1 

When the request gets denied at S3, AWS CloudTrail generates a standard API event: 

JSON
 
{
  "eventType": "AwsApiCall", 
  "errorCode": "AccessDenied", 
  "userAgent": "...", 
  "requestParameters": {...}, 
  "tlsDetails": {...}
}


The full log is in https://github.com/sureshgururajan/aws-data-exfiltration-demo/blob/main/testing-results/scenario1-log.md.

In this case, the full request context is preserved. This includes: 

  • userAgent 
  • requestParameters 
  • TLS metadata 
  • Additional request context 

The main observation is that this event includes caller-controlled metadata in the userAgent field. Since customers can configure CloudTrail to log data events on both the caller account and the resource account, a malicious actor gets the same denial event in their account. Therefore, an attacker in an untrusted account can exfiltrate company data into their accounts by triggering these denied access requests on the company resource. 

Scenario 2 

In the second scenario, if the VPC endpoint policy denies cross-account access (example), CloudTrail generates a different event: 

JSON
 
{
  "eventType": "AwsVpceEvent",
  "eventCategory": "NetworkActivity",
  "errorCode": "VpceAccessDenied",
  ...
}


See the full log here. Instead of logging an AwsApiCall event, CloudTrail logs NetworkActivity with the errorCode: VpceAccessDenied and does not log the HTTP user agent header. 

More importantly, this event is not sent to the malicious actor or the resource owner’s account. Rather, the event is sent to the VPC endpoint owner’s account. In other words, the cause of the denial was a VPC endpoint policy, and therefore CloudTrail generates a NetworkActivity event rather than the API event and routes it to the VPC-owning account. This prevents the bad actor from stealing company data via CloudTrail. 

Azure Experiment 

To set up this experiment, we created two Azure subscriptions – one for identity and the other for the resource. An Azure function in subscription-A writes to a blob storage in subscription-B. The Azure function is registered as a system-assigned managed identity in the Entra ID tenant while turning off the shared access key for the blob storage to ensure only managed identities can access it [5]. The function uses DefaultAzureCredential to request a token from Entra ID and attempts to write to a file in the storage account. Since both subscriptions trust the same Entra ID tenant, the identity moves across subscriptions natively without needing an AssumeRole step. 

Like before, we run through two scenarios: 

  1. Azure function has the Storage Blob Data Contributor role and the network path is open 
  2. The Azure function attempts to write to the storage account but is blocked by the firewall. 

Scenario 1 

When the request is allowed at the blob storage, the following logs are written: 

  1. The Entra ID tenant gets a token request log when the Azure function uses default Azure credentials. This event does NOT contain any information about the actual API action being taken. 
  2. The resource account StorageBlobLogs records a PutBlob event with the file name and IP address but doesn’t show the name of the managed identity. 

Sample log entry from StorageBlobLogs 

Plain Text
 
TimeGenerated [UTC] - 2026-05-02T19:30:32.7306109Z
OperationName - PutBlob 
CallerIpAddress - 172.24.1.71:9156 
Uri - https://sgrstorageaccountinsubb.blob.core.windows.net:443/storage-container/test.json 
AuthenticationType - OAuth 
RequesterObjectId - 00daa177-96c6-4b29-9a5c-53ca603565e9 
StatusCode – 201
UserAgentHeader - azsdk-js-azure-storage-blob/12.31.0 core-rest-pipeline/1.22.3 Node/22.22.2 (Linux 6.6.130.1-3.azl3; x64) 


The requester object ID field indicates which identity made the request but doesn’t reveal more details as to the identity itself. That part is left to the Entra ID logs as shown below. However, we can see that the userAgentHeader is logged. The difference with AWS is that in Azure, the StorageBlob log entry is not mirrored to Entra ID, i.e., the caller’s subscription. In Azure, it stays only in the resource owner’s subscription. Entra ID contains just the token issuance log: 

Sample log entry from Entra ID 

Plain Text
 
Date (UTC),2026-05-02T19:30:32Z
Request ID,25c5f7f7-4206-448d-817b-730744991701
Correlation ID,73cf7b90-c49b-40f0-800d-74e77e40717c
Service principal ID,00daa177-96c6-4b29-9a5c-53ca603565e9
Service principal name,SureshTestingMultiCloud-Function
Credential key ID,
Credential thumbprint,
Application,SureshTestingMultiCloud-Function
Application ID ,57650788-dae5-416f-9da8-792b4ebbbb29
App owner tenant ID,
Resource,Azure Storage
Resource ID ,e406a681-f3d4-42a8-90b6-c2b029497af1
Resource tenant ID,
Resource owner tenant ID,f8cdef31-a31e-4b4a-93e4-5f571e91255a
Home tenant ID,
Home tenant name,
IP address,
Location,", , "
Status,Success
Sign-in error code,
Failure reason,Other.
Conditional Access,Not Applied 


Scenario 2 

In this scenario, we introduced a network-level block using the Storage Account Firewall while keeping the permissions intact. Entra ID logs still show a successful token issuance because the identity is valid and the scope is broad. However, the storage resource logs don’t log the request. Since the connection was dropped at the network layer before reaching the storage service plane, there is no “Access denied” event in the resource’s audit log. 

Sample log entry from Entra ID 

Plain Text
 
Date (UTC): 2026-05-02T19:35:10Z
Service principal name: SureshTestingMultiCloud-Function
Application: SureshTestingMultiCloud-Function
Resource: Azure Storage
Status: Success 


Sample log entry from StorageBlobLogs 

0 results for the KQL query: 

SQL
 
// Query to check for any recorded activity after the network block

StorageBlobLogs
| where TimeGenerated > ago(1h)
| where RequesterObjectId == "00daa177-96c6-4b29-9a5c-53ca603565e9"
| project TimeGenerated, OperationName, StatusCode, StatusText, CallerIpAddress, Uri
| sort by TimeGenerated desc


This result shows that a network-level block is not visible in the resource layer. The Azure administrator sees a successful token issuance in Entra ID but nothing in StorageBlobLogs. To detect this, security teams need to go beyond resource-layer logs and enable additional logging layers such as NSG Flow logs or Defender for Storage - these are outside the scope of this experiment. 

Comparison 

scenario aws azure

Identity model 

Account-scoped 

Tenant scoped 

Who gets audit logs? (when available and enabled) 

Caller-side and resource-owner side (Scenario 1 only) 

Resource-owner side only 

Where are the audit trails located? 

CloudTrail is the logging service. CloudTrail logs are distributed across Caller account, the resource account, and the VPC-owning account 

Token issuance logs are in the Tenant (Entra ID) while resource access logs are in the Subscription 

Caller-controlled metadata visible? 

Yes, visible in caller account and resource account 

Yes, but included in resource account only 

What a network-layer block produces 

When using VPC endpoint policy, AwsVpceEvent is produced and is routed to the VPC-owner account. No logs in resource-owner account. 

No resource-layer log entry. 

Identity context in resource logs 

Full caller identity context included 

Only the caller ID in the form of RequesterObjectId. An operator must correlate this ID with service principal ID in Entra ID logs. 

 

Mitigation 

We saw that in AWS, CloudTrail can be configured to send log events on both the caller account and the resource account. An attacker can use this information to silently exfiltrate small amounts of data at a time. To mitigate this attack vector, an organization must: 

  1. Run their compute services in an Amazon VPC — preferably in a private subnet, and 
  2. Use VPC endpoints with endpoint policies [4] to access their AWS resources for the compute services. The endpoint policies must allow trusted identities to access the resource while blocking everything else. 

AWS already documents these controls in [1], but these experiments show how important it is to enforce these controls. This is in addition to all the controls that an organization already uses, such as Service Control Policies and Resource Control Policies — those policies control the maximum permissible action that can be taken by an identity/resource but do not control the CloudTrail logging behavior. 

While Azure doesn’t have the above attack vector specifically, it has a different problem — an operator must manually correlate Entra ID events with the resource event. An example would be an “identity journey” like — managed identity (like the Azure function) requests a token, then writes to a storage account. Therefore, some tooling must be built to correlate such events — for example, routing both ManagedIdentitySignInLogs and StorageBlobLogs into a single Log Analytics workspace is a minimum. 

Additionally, logs must be captured at different layers such as NSG flow logs/Defender for Storage that can provide anomaly detection beyond standard diagnostic logs. 

 Conclusion 

In this article, we demonstrated how the same access request produces different results in AWS and Azure. In AWS, access logs were sent to the resource account or the VPC account depending on where the deny decision was enforced, while in Azure, access logs were only sent to the resource account. We saw that this difference comes from how each cloud provider fundamentally treats identities and resources. 

The implications of the experiment are that security teams in multi-cloud environments cannot assume that audit coverage works the same way across providers. Each provider models their identities and provides different data perimeter controls. 

Before designing data perimeter controls, security teams must understand each provider’s logging architecture and its differences. 

References 

[1] https://aws.amazon.com/identity/data-perimeters-blog-post-series/  

[2] https://systemweakness.com/a-subtle-audit-log-consideration-in-aws-063752150b20  

[3] https://docs.aws.amazon.com/controltower/latest/userguide/what-shared.html  

[4] https://docs.aws.amazon.com/vpc/latest/privatelink/vpc-endpoints-access.html  

[5] https://learn.microsoft.com/en-us/azure/storage/common/shared-key-authorization-prevent?tabs=portal

AWS azure security

Opinions expressed by DZone contributors are their own.

Related

  • 12 Expert Tips for Secure Cloud Deployments
  • Keep Your Application Secrets Secret
  • Dynatrace Perform: Day Two
  • How To Reduce the Impact of a Cloud Outage

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook