Skip to content
advanced Phase 108 · Code Review

Security Review

1h 30m
3 problems
Topic Progress 0%

Security Review Overview

Security Review Checklist

Security Review Checklist:
├── Input Validation
│   ├── User input sanitization
│   ├── Type checking
│   ├── Length limits
│   └── Format validation
├── Output Escaping
│   ├── HTML escaping
│   ├── JavaScript escaping
│   ├── URL escaping
│   └── SQL escaping
├── Authentication
│   ├── Password hashing (bcrypt/argon2)
│   ├── Session management
│   ├── Token validation
│   └── MFA implementation
├── Authorization
│   ├── ACL checks
│   ├── Resource ownership
│   ├── Role-based access
│   └── Privilege escalation prevention
├── SSRF Prevention
│   ├── URL allowlisting for webhooks
│   ├── Payment gateway callback validation
│   ├── Internal network access restrictions
│   └── DNS rebinding protection
├── RCE Prevention
│   ├── Template injection guards
│   ├── Deserialization safety
│   ├── File upload validation
│   └── Process execution restrictions
├── Threat Modeling
│   ├── STRIDE analysis per component
│   ├── Data flow diagram review
│   ├── Trust boundary identification
│   └── Attack surface enumeration
└── Data Protection
    ├── Encryption at rest
    ├── Encryption in transit
    ├── Sensitive data masking
    └── PII handling (PCI DSS compliance)

OWASP Top 10 (2021)

OWASP Top 10:
├── A01: Broken Access Control
├── A02: Cryptographic Failures
├── A03: Injection (SQL, NoSQL, OS, LDAP)
├── A04: Insecure Design
├── A05: Security Misconfiguration
├── A06: Vulnerable and Outdated Components
├── A07: Identification and Authentication Failures
├── A08: Software and Data Integrity Failures
│   ├── Insecure deserialization
│   ├── CI/CD pipeline compromise
│   └── Auto-update without integrity checks
├── A09: Security Logging and Monitoring Failures
└── A10: Server-Side Request Forgery (SSRF)

Review Process

// Security review checklist
$securityChecklist = [
    'input_validation' => [
        'All user input validated',
        'Input sanitized before use',
        'Type checking implemented',
        'Length limits enforced'
    ],
    'output_escaping' => [
        'HTML output escaped',
        'JavaScript output escaped',
        'URL parameters escaped',
        'Database queries parameterized'
    ],
    'ssrf_prevention' => [
        'Webhook URLs validated against allowlist',
        'Payment callbacks verified for source IP',
        'Internal network ranges blocked',
        'DNS resolution validated before request'
    ],
    'rce_prevention' => [
        'Template variables not user-controlled',
        'Deserialization uses signed/encrypted payloads',
        'File uploads validated (type, size, content)',
        'exec/shell_exec calls removed or sandboxed'
    ],
    'authentication' => [
        'Passwords hashed (bcrypt/argon2)',
        'Session timeout configured',
        'CSRF protection enabled',
        'Rate limiting implemented'
    ],
    'authorization' => [
        'ACL checks implemented',
        'Resource ownership verified',
        'Role-based access enforced',
        'Privilege escalation prevented'
    ]
];

Real-World Incident: Magecart (2018-2020)

Magecart groups compromised over 1,700 Magento stores by injecting malicious JavaScript into checkout pages. The attack vector was:

  1. Compromised admin credentials via phishing
  2. Modified theme files or injected via form_key bypass
  3. Exfiltrated payment card data to external endpoints

Lessons learned: Enforce 2FA for admin, implement CSP headers, monitor file integrity, restrict admin IP ranges.

Input Validation

Validate User Input

// Bad: No validation
public function saveAddress($data)
{
    $this->db->insert('customer_address', $data);
}

// Good: Input validation
public function saveAddress($data)
{
    $validated = $this->validateAddress($data);
    $this->db->insert('customer_address', $validated);
}

private function validateAddress($data)
{
    $validated = [];
    $validated['firstname'] = $this->validateString($data['firstname'], 255);
    $validated['street'] = $this->validateString($data['street'], 255);
    $validated['city'] = $this->validateString($data['city'], 255);
    $validated['postcode'] = $this->validatePostcode($data['postcode'], $data['country_id']);
    $validated['country_id'] = $this->validateCountry($data['country_id']);
    return $validated;
}

Sanitize Input

class InputSanitizer
{
    public function sanitizeString($input)
    {
        return htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
    }

    public function sanitizeEmail($input)
    {
        return filter_var($input, FILTER_SANITIZE_EMAIL);
    }

    public function sanitizeUrl($input)
    {
        return filter_var($input, FILTER_SANITIZE_URL);
    }

    public function sanitizeInt($input)
    {
        return filter_var($input, FILTER_SANITIZE_NUMBER_INT);
    }
}

Type Checking

class TypeChecker
{
    public function isString($value)
    {
        return is_string($value);
    }

    public function isInt($value)
    {
        return is_int($value) || ctype_digit((string)$value);
    }

    public function isFloat($value)
    {
        return is_float($value) || is_numeric($value);
    }

    public function isArray($value)
    {
        return is_array($value);
    }
}

Webhook Input Validation (SSRF Prevention)

// Bad: Accepting any URL for webhook callback
public function processWebhook($request)
{
    $callbackUrl = $request->getParam('callback_url');
    $this->httpClient->get($callbackUrl); // SSRF vulnerability!
}

// Good: Strict URL validation with allowlist
public function processWebhook($request)
{
    $callbackUrl = $request->getParam('callback_url');
    $this->validateWebhookUrl($callbackUrl);
    $this->httpClient->get($callbackUrl);
}

private function validateWebhookUrl($url)
{
    // Parse and validate
    $parsed = parse_url($url);
    if (!$parsed || !isset($parsed['host'])) {
        throw new \Exception('Invalid URL');
    }

    // Block internal/private ranges
    $ip = gethostbyname($parsed['host']);
    if ($this->isInternalIp($ip)) {
        throw new \Exception('Internal URLs not allowed');
    }

    // Allowlist permitted domains
    $allowedDomains = ['api.stripe.com', 'api.paypal.com'];
    if (!in_array($parsed['host'], $allowedDomains)) {
        throw new \Exception('Domain not in allowlist');
    }

    // Enforce HTTPS
    if ($parsed['scheme'] !== 'https') {
        throw new \Exception('HTTPS required');
    }
}

private function isInternalIp($ip)
{
    $reserved = [
        '127.0.0.0/8',    // loopback
        '10.0.0.0/8',     // private class A
        '172.16.0.0/12',  // private class B
        '192.168.0.0/16', // private class C
        '169.254.0.0/16', // link-local
        '0.0.0.0/8',      // current network
        '::1/128',        // IPv6 loopback
        'fc00::/7',       // IPv6 private
    ];
    foreach ($reserved as $range) {
        if ($this->ipInCidr($ip, $range)) {
            return true;
        }
    }
    return false;
}

Payment Gateway Webhook Validation

// Validate Stripe/PayPal webhook source
public function validatePaymentWebhook($request)
{
    // 1. Verify HMAC signature
    $signature = $request->getHeader('Stripe-Signature');
    $payload = $request->getContent();
    $expectedSig = hash_hmac('sha256', $payload, $this->webhookSecret);
    if (!hash_equals($expectedSig, $signature)) {
        throw new \Exception('Invalid webhook signature');
    }

    // 2. Verify source IP (Stripe publishes IPs at stripe.com/files/ips/ips.txt)
    $clientIp = $request->getClientIp();
    $allowedIps = $this->paymentConfig->getWebhookIps();
    if (!in_array($clientIp, $allowedIps)) {
        throw new \Exception('Webhook IP not allowed');
    }

    // 3. Check idempotency to prevent replay
    $eventId = $payload['id'];
    if ($this->webhookLog->exists($eventId)) {
        return; // Already processed
    }
}

Output Escaping

HTML Escaping

// Bad: No escaping
$html = '<div>' . $userInput . '</div>';

// Good: HTML escaping
$html = '<div>' . htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8') . '</div>';

// In Magento templates
{{variable|escape}}
{{variable|escapeHtml}}
{{variable|escapeUrl}}
{{variable|escapeJs}}
{{variable|escapeCss}}

JavaScript Escaping

// Bad: No escaping
$script = '<script>var data = ' . $json . ';</script>';

// Good: JavaScript escaping with JSON_HEX flags
$script = '<script>var data = ' . json_encode(
    $data,
    JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT
) . ';</script>';

// In templates - always use json_encode with hex flags
<script>var config = {{variable|json_encode}};</script>

SQL Escaping (Parameterized Queries)

// Bad: SQL injection
$query = "SELECT * FROM customers WHERE email = '" . $email . "'";

// Good: Parameterized query
$query = 'SELECT * FROM customers WHERE email = ?';
$this->db->fetchRow($query, [$email]);

// Good: Magento query builder
$this->db->select()
    ->from('customers')
    ->where('email = ?', $email);

// Good: Repository pattern with criteria
$customer = $this->customerRepository->get($email);

URL Escaping

// Bad: No escaping
$url = '/redirect?url=' . $userInput;

// Good: URL escaping
$url = '/redirect?url=' . urlencode($userInput);

// For path segments
$url = '/product/' . rawurlencode($productSku);

// Redirect validation - prevent open redirect
public function validateRedirect($url)
{
    $parsed = parse_url($url);
    if (isset($parsed['host'])) {
        // Absolute URL - must be same domain
        $currentHost = $_SERVER['HTTP_HOST'];
        if ($parsed['host'] !== $currentHost) {
            throw new \Exception('External redirect not allowed');
        }
    }
    return $url;
}

Content Security Policy (CSP)

// Add CSP headers to prevent XSS
public function sendCspHeaders()
{
    $csp = [
        "default-src 'self'",
        "script-src 'self' 'nonce-{random}' https://js.stripe.com",
        "style-src 'self' 'unsafe-inline'",
        "img-src 'self' data: https:",
        "connect-src 'self' https://api.stripe.com",
        "frame-src 'self' https://js.stripe.com",
        "object-src 'none'",
        "base-uri 'self'",
        "form-action 'self'",
    ];
    header('Content-Security-Policy: ' . implode('; ', $csp));
}

Real-World Incident: British Airways (2018)

Magecart injected skimming code into British Airways' checkout page via a compromised third-party script. The injected code exfiltrated payment data to https://contsoflate.com/. Impact: 380,000+ cards compromised, £20M fine.

Fix: Implemented strict CSP, removed third-party scripts, added Subresource Integrity (SRI) hashes.

Server-Side Request Forgery (SSRF)

What is SSRF?

SSRF occurs when an attacker can make the server send requests to arbitrary destinations. In Magento contexts, this is critical for:

  • Payment webhooks: Server fetches status from gateway callback URLs
  • Import/export: CSV/URL imports from external sources
  • Image proxy: Server-side image resizing from URLs
  • API integrations: Calling external services

SSRF Attack Vectors in Magento

SSRF Attack Surface:
├── Payment Webhooks
│   ├── Stripe/PayPal callback URLs
│   ├── Custom payment module webhooks
│   └── Async order status updates
├── Import/Export
│   ├── CSV import from URL
│   ├── Product image proxy
│   └── RSS feed fetching
├── API Integrations
│   ├── Shipping carrier APIs
│   ├── Tax calculation services
│   └── ERP/CRM sync endpoints
└── Admin Panel
    ├── URL preview features
    ├── Newsletter image embedding
    └── CMS content remote resources

Vulnerable Code Patterns

// Pattern 1: Unvalidated URL fetch (SSRF)
class ProductImport
{
    public function importFromUrl($url)
    {
        $content = file_get_contents($url); // SSRF!
        $this->parseAndImport($content);
    }
}

// Pattern 2: HTTP client with user-controlled URL
public function callback($request)
{
    $webhookUrl = $request->getParam('notify_url');
    $this->client->get($webhookUrl); // SSRF!
}

// Pattern 3: Image proxy without validation
public function proxyImage($request)
{
    $imageUrl = $request->getParam('url');
    $imageData = file_get_contents($imageUrl); // SSRF!
    header('Content-Type: image/jpeg');
    echo $imageData;
}

SSRF Mitigation

// Solution 1: URL allowlisting
class SafeHttpClient
{
    private $allowedHosts = [
        'api.stripe.com',
        'api.paypal.com',
        'checkout.com',
    ];

    private $blockedRanges = [
        '127.0.0.0/8',
        '10.0.0.0/8',
        '172.16.0.0/12',
        '192.168.0.0/16',
        '169.254.0.0/16',
        '0.0.0.0/8',
    ];

    public function fetch($url)
    {
        $parsed = parse_url($url);
        $this->validateScheme($parsed);
        $this->validateHost($parsed);
        $this->validateIp($parsed['host']);
        return $this->client->get($url);
    }

    private function validateScheme($parsed)
    {
        $allowed = ['https'];
        if (!in_array($parsed['scheme'] ?? '', $allowed)) {
            throw new \Exception('Only HTTPS allowed');
        }
    }

    private function validateHost($parsed)
    {
        $host = $parsed['host'] ?? '';
        if (!in_array($host, $this->allowedHosts)) {
            throw new \Exception('Host not in allowlist');
        }
    }

    private function validateIp($hostname)
    {
        $ip = gethostbyname($hostname);
        if ($ip === $hostname) {
            throw new \Exception('DNS resolution failed');
        }
        foreach ($this->blockedRanges as $range) {
            if ($this->ipInCidr($ip, $range)) {
                throw new \Exception('Internal IP blocked');
            }
        }
    }
}

// Solution 2: Use Magento's HTTP client with restrictions
$httpClient = $this->curlFactory->create();
$httpClient->setOption(CURLOPT_SSL_VERIFYPEER, true);
$httpClient->setOption(CURLOPT_TIMEOUT, 10);
$httpClient->setOption(CURLOPT_FOLLOWLOCATION, false);
$httpClient->setOption(CURLOPT_MAXREDIRS, 0);

// Solution 3: Disable unnecessary URL schemes
// In php.ini: allow_url_fopen = Off
// Or use stream context with restricted protocols
$context = stream_context_create([
    'http' => ['timeout' => 5],
    'ssl' => ['verify_peer' => true],
]);

DNS Rebinding Attack

DNS Rebinding:
1. Attacker registers evil.com
2. First DNS response: evil.com -> 127.0.0.1
3. Server makes request to 'evil.com' (resolved to localhost)
4. Attacker's DNS now returns: evil.com -> 192.168.1.1
5. Subsequent requests hit internal services

Mitigation:
- Resolve DNS once and cache the IP
- Validate IP before making request
- Don't follow redirects
- Use a dedicated DNS resolver with rebinding protection

Real-World Incident: Shopify SSRF (2021)

Shopify had an SSRF vulnerability in their webhook handling that allowed attackers to access internal metadata services (169.254.169.254) and exfiltrate cloud credentials.

Impact: Exposed API keys, database credentials, and internal service tokens.
Fix: Implemented strict IP allowlisting for webhook destinations, disabled metadata service access from application servers.

Remote Code Execution (RCE)

What is RCE?

RCE allows an attacker to execute arbitrary code on the server. In Magento, RCE vectors include:

  • Template injection: Twig/PHP template rendering with user input
  • Unsafe deserialization: unserialize() with user data
  • File upload: Executable file upload
  • eval()/preg_replace(): Dynamic code execution
  • LFI/RFI: Local/Remote file inclusion

RCE Attack Surface in Magento

RCE Attack Surface:
├── Template Engine
│   ├── Twig sandbox escapes
│   ├── Custom template rendering
│   └── Email template variables
├── Serialization
│   ├── Session data
│   ├── Cache serialization
│   └── Import/export data
├── File Operations
│   ├── Product image uploads
│   ├── CSV/XML imports
│   └── CMS media uploads
├── Dynamic Code Execution
│   ├── eval() in custom modules
│   ├── preg_replace /e modifier
│   ├── call_user_func() with input
│   └── Reflection class usage
└── Server Configuration
    ├── allow_url_include
    ├── disable_functions
    └── open_basedir

Unsafe Deserialization

// Critical: RCE via deserialization
// Bad: unserialize with user input
$data = $request->getParam('data');
$object = unserialize($data); // RCE if class has __wakeup or __destruct

// Bad: session data deserialization vulnerability
// PHP sessions store serialized data - if session storage is compromised
// or user can influence session ID, RCE is possible

// Good: Use JSON instead of PHP serialization
$data = json_decode($request->getParam('data'), true);

// Good: If you must use unserialize, restrict allowed classes
$object = unserialize($data, ['allowed_classes' => false]);
// Or restrict to specific classes
$object = unserialize($data, ['allowed_classes' => [\Magento\Framework\DataObject::class]]);

// Good: Use signed/encrypted serialization
$payload = $this->crypto->encrypt($serializedData);
$object = unserialize($this->crypto->decrypt($payload));

Template Injection

// Bad: Twig sandbox escape
$twig = new \Twig\Environment(new \Twig\Loader\ArrayLoader([
    'template' => $userTemplate, // User-controlled template = RCE
]));
$twig->render('template', $context);

// Good: Restrict Twig sandbox
$policy = new \Twig\Sandbox\SecurityPolicy(
    ['if', 'for', 'block', 'macro'],  // allowed tags
    ['upper', 'length'],                // allowed filters
    [],                                 // allowed methods
    [],                                 // allowed properties
    ['dump']                            // denied tags
);
$sandbox = new \Twig\Sandbox\SecuritySandbox($policy);
$twig->addExtension(new \Twig\Extension\SandboxExtension($sandbox));

// Bad: Email template with user input in subject/body
$mailer->setSubject($request->getParam('subject'));
$mailer->setBody($request->getParam('body')); // Potential template injection

// Good: Sanitize before passing to template engine
$subject = strip_tags($request->getParam('subject'));
$body = $this->templateFilter->filter($request->getParam('body'));

File Upload Validation

// Bad: No file validation
public function uploadImage($request)
{
    $file = $request->getFile('image');
    $file->move($this->mediaDir, $file->getClientOriginalName()); // RCE!
}

// Good: Strict file validation
public function uploadImage($request)
{
    $file = $request->getFile('image');

    // 1. Validate MIME type (not just extension)
    $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    $finfo = new \finfo(FILEINFO_MIME_TYPE);
    $mimeType = $finfo->file($file->getPathname());
    if (!in_array($mimeType, $allowedTypes)) {
        throw new \Exception('Invalid file type');
    }

    // 2. Validate file content (not just extension)
    $imageInfo = getimagesize($file->getPathname());
    if ($imageInfo === false) {
        throw new \Exception('Invalid image file');
    }

    // 3. Check for embedded PHP/HTML
    $content = file_get_contents($file->getPathname());
    if (preg_match('/<\?php|<\?=|<script/i', $content)) {
        throw new \Exception('File contains executable code');
    }

    // 4. Generate safe filename
    $safeName = bin2hex(random_bytes(16)) . '.' . $this->getExtension($mimeType);

    // 5. Move to non-executable directory
    $file->move($this->mediaDir . '/images/', $safeName);

    // 6. Set correct permissions (no execute)
    chmod($this->mediaDir . '/images/' . $safeName, 0644);
}

Dangerous PHP Functions

// Functions that enable RCE - flag in code review:
// - eval()                          // Direct code execution
// - assert()                        // Code execution when string argument
// - preg_replace /e modifier         // Code execution in replacement
// - call_user_func()                 // Dynamic function call
// - call_user_func_array()           // Dynamic function call
// - create_function()                // Creates function from string
// - extract()                        // Variable overwrite
// - include/require with user input  // File inclusion
// - file_get_contents with user URL  // SSRF leading to RCE
// - file_put_contents with user path // Arbitrary file write
// - exec(), shell_exec(), system()   // OS command execution
// - proc_open()                      // Process execution
// - popen()                          // Process execution
// - $_SERVER variables in eval       // Superglobal injection

// Magento-specific dangerous patterns:
// - Magento\Framework\Unserialize::unserialize() with user input
// - Custom template rendering with user content
// - Loading classes from user-specified namespace

Real-World Incident: Magento 2 RCE via Setup Wizard (CVE-2020-13756)

A critical RCE vulnerability in Magento 2.4.0-2.4.0p1 allowed unauthenticated attackers to execute arbitrary code through the setup wizard when exposed in production.

Impact: Complete server compromise, payment data theft, crypto mining.
Fix:

  1. Disable setup wizard in production (bin/magento setup:set-flag --cleanup-database false)
  2. Restrict access to /setup via web server config
  3. Apply security patch SUPEE-11314
# Block setup wizard in production
<Directory "pub/setup">
    Require all denied
</Directory>

# Or in Nginx
location /setup/ {
    deny all;
}

Real-World Incident: Magento Payment Skimmer via LFI (2020)

Attackers exploited a Local File Inclusion vulnerability in a custom payment module to:

  1. Upload a PHP shell via image upload
  2. Include it through LFI vulnerability
  3. Inject payment skimmer into checkout

Impact: 200+ stores compromised, millions of card numbers stolen.

Authentication Review

Password Hashing

// Bad: MD5 or SHA1
$password = md5($password);

// Good: bcrypt
$password = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);

// Better: argon2id
$password = password_hash($password, PASSWORD_ARGON2ID, [
    'memory_cost' => 65536,  // 64MB
    'time_cost' => 4,        // 4 iterations
    'threads' => 3,          // 3 threads
]);

// Verify password
if (password_verify($input, $storedHash)) {
    // Password correct
}

// Rehash if algorithm or cost changed
if (password_needs_rehash($storedHash, PASSWORD_ARGON2ID)) {
    $newHash = password_hash($input, PASSWORD_ARGON2ID);
    $this->userRepository->updateHash($userId, $newHash);
}

Session Management

// Session security configuration
$sessionConfig = [
    'cookie_secure' => true,      // HTTPS only
    'cookie_httponly' => true,     // No JavaScript access
    'cookie_samesite' => 'Lax',   // CSRF protection
    'use_strict_mode' => true,    // Reject uninitialized sessions
    'use_only_cookies' => true,   // No session ID in URL
    'session_lifetime' => 3600,   // 1 hour timeout
    'gc_maxlifetime' => 7200,     // Garbage collection
];

// Regenerate session ID after login
session_regenerate_id(true);

// Session fixation prevention
public function login($credentials)
{
    if ($this->authenticate($credentials)) {
        session_regenerate_id(true);
        $this->session->setCustomerId($customerId);
        $this->session->setUserGroupId($customer->getGroupId());
    }
}

// Session validation on each request
public function validateSession()
{
    if (!$this->session->isLoggedIn()) {
        return false;
    }

    // Validate session fingerprint
    $fingerprint = hash('sha256', $_SERVER['HTTP_USER_AGENT'] . $_SERVER['REMOTE_ADDR']);
    if ($this->session->getFingerprint() !== $fingerprint) {
        $this->session->destroy();
        throw new \Exception('Session hijacking detected');
    }

    // Check session timeout
    $lastActivity = $this->session->getLastActivity();
    if (time() - $lastActivity > 1800) {
        $this->session->destroy();
        throw new \Exception('Session expired');
    }

    $this->session->setLastActivity(time());
    return true;
}

CSRF Protection

// Generate CSRF token
$token = $this->formKey->getFormKey();

// In form
<input type="hidden" name="form_key" value="<?php echo $token; ?>" />

// Validate CSRF token
if ($this->request->getParam('form_key') !== $this->formKey->getFormKey()) {
    throw new \Exception('Invalid form key');
}

// For API endpoints - use double-submit cookie pattern
public function validateApiCsrf($request)
{
    $headerToken = $request->getHeader('X-CSRF-Token');
    $cookieToken = $request->getCookie('csrf_token');

    if (!$headerToken || !$cookieToken || !hash_equals($headerToken, $cookieToken)) {
        throw new \Exception('CSRF validation failed');
    }
}

Rate Limiting and Brute Force Protection

class RateLimiter
{
    public function check($identifier, $maxAttempts = 5, $timeWindow = 900)
    {
        $key = 'rate_limit_' . $identifier;
        $attempts = $this->cache->load($key);

        if ($attempts >= $maxAttempts) {
            $lockoutKey = 'lockout_' . $identifier;
            $lockoutTime = $this->cache->load($lockoutKey);

            if (!$lockoutTime) {
                $this->cache->save(time(), $lockoutKey, [], $timeWindow * 2);
                throw new \Exception('Account locked for ' . ($timeWindow * 2) . ' seconds');
            }
            throw new \Exception('Account temporarily locked');
        }

        $this->cache->save($attempts + 1, $key, [], $timeWindow);
    }

    public function reset($identifier)
    {
        $this->cache->remove('rate_limit_' . $identifier);
        $this->cache->remove('lockout_' . $identifier);
    }
}

// Login attempt tracking
public function login($credentials)
{
    $identifier = 'login_' . $credentials['email'];
    $this->rateLimiter->check($identifier, 5, 900);

    if ($this->authenticate($credentials)) {
        $this->rateLimiter->reset($identifier);
        return true;
    }

    $this->auditLog->log('login_failed', $credentials['email']);
    throw new \Exception('Invalid credentials');
}

Two-Factor Authentication (2FA)

// Magento 2FA configuration
$twoFactorConfig = [
    'enabled' => true,
    'enforced_for_roles' => ['admin'],
    'providers' => [
        'google' => true,    // Google Authenticator
        'authy' => true,     // Authy
        'u2f' => true,       // Hardware keys
        'totp' => true,      // TOTP
    ],
    'grace_period' => 30, // Days before 2FA is mandatory
];

// Enforce 2FA for admin
public function enforce2FA($userId)
{
    $user = $this->userRepository->get($userId);
    $role = $user->getRole();

    if ($role === 'admin' && !$user->has2FA()) {
        if (!$this->is2FAGracePeriod($user)) {
            throw new \Exception('2FA required for admin access');
        }
    }
}

Real-World Incident: Magento Admin Brute Force (2019)

Automated bots brute-forced Magento admin panels using common credentials (admin/admin, admin/magento123). Compromised stores had payment skimmers injected.

Fixes applied:

  1. Enforced 2FA for all admin accounts
  2. Implemented IP allowlisting for admin access
  3. Added CAPTCHA after 3 failed attempts
  4. Implemented account lockout after 5 failures

Authorization Review

ACL Checks

// Check ACL permission
public function execute()
{
    if (!$this->authorization->isAllowed('Vendor_Module::action')) {
        throw new \Magento\Framework\Exception\AuthorizationException(
            __('You don\'t have permission to access this resource')
        );
    }
    // Proceed with action
}

// Dynamic ACL check based on resource
public function deleteProduct($productId)
{
    $product = $this->productRepository->getById($productId);

    // Check delete permission
    if (!$this->authorization->isAllowed('Magento_Catalog::products')) {
        throw new AuthorizationException(__('Insufficient permissions'));
    }

    // Additional check: can only delete own products unless admin
    if ($product->getOwnerId() !== $this->session->getUserId()
        && !$this->authorization->isAllowed('Magento_Catalog::products_all')) {
        throw new AuthorizationException(__('Cannot delete products from other sellers'));
    }
}

Resource Ownership

// Check resource ownership
public function getOrder($orderId)
{
    $order = $this->orderRepository->get($orderId);

    // Check if user owns this order
    if ($order->getCustomerId() !== $this->session->getCustomerId()) {
        throw new \Exception('Unauthorized access');
    }

    return $order;
}

// Mass action ownership check
public function cancelOrders($orderIds)
{
    $orders = $this->orderRepository->getByIds($orderIds);
    $customerId = $this->session->getCustomerId();

    foreach ($orders as $order) {
        if ($order->getCustomerId() !== $customerId) {
            throw new \Exception('Cannot cancel orders you do not own');
        }
    }
}

Role-Based Access Control

class RoleBasedAccess
{
    public function checkAccess($resource, $action)
    {
        $role = $this->getRole();

        switch ($role) {
            case 'super_admin':
                return true;
            case 'admin':
                return $this->adminAccess($resource, $action);
            case 'manager':
                return $this->managerAccess($resource, $action);
            case 'seller':
                return $this->sellerAccess($resource, $action);
            case 'customer':
                return $this->customerAccess($resource, $action);
            default:
                return false;
        }
    }

    private function sellerAccess($resource, $action)
    {
        // Sellers can only access their own products/orders
        $allowed = [
            'Magento_Catalog::products' => ['read', 'update'],
            'Magento_Sales::orders' => ['read'],
            'Magento_Catalog::categories' => ['read'],
        ];

        return isset($allowed[$resource]) && in_array($action, $allowed[$resource]);
    }
}

Privilege Escalation Prevention

// Prevent privilege escalation
public function updateUser($userId, $data)
{
    $currentUser = $this->getCurrentUser();

    // Prevent non-admin from assigning admin role
    if (isset($data['role']) && $data['role'] === 'admin') {
        if ($currentUser->getRole() !== 'super_admin') {
            throw new \Exception('Cannot assign admin role');
        }
    }

    // Prevent user from updating other users
    if ($userId !== $currentUser->getId() && $currentUser->getRole() !== 'super_admin') {
        throw new \Exception('Cannot update other users');
    }

    // Prevent self-demotion from super_admin
    if ($userId === $currentUser->getId() && isset($data['role'])) {
        if ($currentUser->getRole() === 'super_admin' && $data['role'] !== 'super_admin') {
            throw new \Exception('Cannot self-demotion from super admin');
        }
    }

    $this->userRepository->save($userId, $data);
}

// Insecure Direct Object Reference (IDOR) prevention
public function getInvoice($invoiceId)
{
    $invoice = $this->invoiceRepository->get($invoiceId);
    $order = $this->orderRepository->get($invoice->getOrderId());

    // Verify customer owns the order
    if ($order->getCustomerId() !== $this->session->getCustomerId()) {
        throw new AuthorizationException(__('Invoice not found'));
    }

    return $invoice;
}

GraphQL Authorization

// GraphQL resolver authorization
public function resolve($root, $args, $context, $info)
{
    // Check if query requires authentication
    if ($this->requiresAuth($info->fieldName)) {
        if (!$context->isLoggedIn()) {
            throw new \Exception('Authentication required');
        }
    }

    // Check if user can access requested fields
    $requestedFields = $this->getFieldSelection($info);
    foreach ($requestedFields as $field) {
        if (!$this->authorization->isAllowed($field)) {
            throw new AuthorizationException(__('Insufficient permissions'));
        }
    }

    // Apply data filtering based on user role
    return $this->applyAccessControl($data, $context);
}

Threat Modeling for Magento Stores

STRIDE Threat Model

STRIDE is a threat modeling methodology that categorizes threats into six types:

STRIDE Categories:
├── Spoofing - Impersonating a user or system
├── Tampering - Modifying data or code
├── Repudiation - Denying actions without proof
├── Information Disclosure - Exposing sensitive data
├── Denial of Service - Making system unavailable
└── Elevation of Privilege - Gaining unauthorized access

Threat Model for Magento Components

Magento Store Threat Model:
│
├── 1. Customer-Facing Storefront
│   ├── Spoofing: Account takeover via credential stuffing
│   ├── Tampering: Price manipulation in cart
│   ├── Repudiation: Customer disputes order placement
│   ├── Info Disclosure: PII leakage via XSS
│   ├── DoS: Cart flooding attacks
│   └── EoP: Accessing other customer orders
│
├── 2. Admin Panel
│   ├── Spoofing: Admin credential theft
│   ├── Tampering: Modifying payment settings
│   ├── Repudiation: Admin actions without audit trail
│   ├── Info Disclosure: Exporting customer database
│   ├── DoS: Locking out admin accounts
│   └── EoP: Escalating to super admin
│
├── 3. Payment Processing
│   ├── Spoofing: Forged payment webhooks
│   ├── Tampering: Modifying transaction amounts
│   ├── Repudiation: Denying payment receipt
│   ├── Info Disclosure: Card data exposure
│   ├── DoS: Payment gateway flooding
│   └── EoP: Accessing payment API keys
│
├── 4. REST/GraphQL API
│   ├── Spoofing: API key theft
│   ├── Tampering: Modifying API responses
│   ├── Repudiation: API abuse without logging
│   ├── Info Disclosure: Over-permissive queries
│   ├── DoS: Resource exhaustion attacks
│   └── EoP: Accessing admin-only endpoints
│
├── 5. Import/Export
│   ├── Spoofing: Malicious file uploads
│   ├── Tampering: Modifying imported data
│   ├── Repudiation: Data modification without log
│   ├── Info Disclosure: CSV injection
│   ├── DoS: Large file uploads exhausting storage
│   └── EoP: Uploading executable files (RCE)
│
└── 6. Infrastructure
    ├── Spoofing: DNS hijacking
    ├── Tampering: Modifying server configuration
    ├── Repudiation: Unauthorized server access
    ├── Info Disclosure: Exposed debug endpoints
    ├── DoS: DDoS attacks
    └── EoP: Container escape (if containerized)

Data Flow Diagram Review

Data Flow Analysis:
│
├── Customer Browser → Storefront
│   ├── Threats: XSS, CSRF, session hijacking
│   └── Controls: CSP, CSRF tokens, secure cookies
│
├── Storefront → Magento Backend
│   ├── Threats: IDOR, privilege escalation
│   └── Controls: ACL, input validation, authorization
│
├── Magento Backend → Database
│   ├── Threats: SQL injection, data exfiltration
│   └── Controls: Parameterized queries, encryption
│
├── Magento Backend → Payment Gateway
│   ├── Threats: SSRF, webhook forgery, MITM
│   └── Controls: HTTPS, signature verification, IP allowlist
│
├── Magento Backend → Email Service
│   ├── Threats: Email injection, open relay
│   └── Controls: SMTP authentication, input sanitization
│
├── Magento Backend → File Storage
│   ├── Threats: Path traversal, file upload RCE
│   └── Controls: File validation, non-executable storage
│
└── Magento Backend → External APIs
    ├── Threats: SSRF, API key exposure
    └── Controls: URL allowlisting, secret management

Trust Boundary Identification

// Trust boundaries in a Magento store
$trustBoundaries = [
    'external' => [
        'customer_browser',
        'mobile_app',
        'third_party_integrations',
    ],
    'dmz' => [
        'cdn',
        'waf',
        'load_balancer',
    ],
    'internal' => [
        'web_server',
        'magento_application',
        'cache_server',
    ],
    'secure' => [
        'database',
        'payment_processor',
        'file_storage',
    ],
];

// Each trust boundary crossing requires validation
public function validateTrustBoundary($source, $destination, $data)
{
    $boundary = $this->getTrustBoundary($source, $destination);

    switch ($boundary) {
        case 'external_to_dmz':
            $this->validateInput($data);
            $this->applyRateLimit($source);
            break;
        case 'dmz_to_internal':
            $this->validateAuthentication($data);
            $this->validateAuthorization($data);
            break;
        case 'internal_to_secure':
            $this->validateEncryption($data);
            $this->validatePermissions($data);
            break;
    }
}

Attack Surface Enumeration Checklist

Attack Surface Checklist:
├── Public Endpoints
│   ├── Customer registration/login
│   ├── Product browsing/search
│   ├── Cart/checkout
│   ├── REST/GraphQL APIs
│   └── RSS/Atom feeds
├── Semi-Protected Endpoints
│   ├── Customer account pages
│   ├── Order history
│   ├── Address management
│   └── Wishlist management
├── Admin-Only Endpoints
│   ├── Admin panel (/admin)
│   ├── Setup wizard (/setup)
│   ├── CLI tools (bin/magento)
│   └── Cron jobs
├── Integration Endpoints
│   ├── Payment webhooks
│   ├── Shipping API callbacks
│   ├── Tax calculation APIs
│   └── ERP/CRM sync
└── Infrastructure
    ├── Database connections
    ├── Cache (Redis/Memcached)
    ├── Message queue (RabbitMQ)
    ├── File system
    └── Log storage

Security Audit Checklist

Magento-Specific Security Audit Checklist

Magento Security Audit Checklist:
│
├── 1. Server Configuration
│   ├── [ ] PHP version 8.1+ (EOL check)
│   ├── [ ] PHP disabled functions: exec, shell_exec, system, passthru, eval, assert
│   ├── [ ] allow_url_fopen = Off
│   ├── [ ] allow_url_include = Off
│   ├── [ ] expose_php = Off
│   ├── [ ] display_errors = Off (production)
│   ├── [ ] log_errors = On
│   ├── [ ] open_basedir configured
│   ├── [ ] session.cookie_httponly = On
│   ├── [ ] session.cookie_secure = On
│   └── [ ] session.use_strict_mode = On
│
├── 2. Web Server Configuration
│   ├── [ ] Server tokens hidden (ServerSignature Off)
│   ├── [ ] Directory listing disabled
│   ├── [ ] /setup directory blocked
│   ├── [ ] /pub/static directory protected
│   ├── [ ] /var directory not web-accessible
│   ├── [ ] /app/code directory not web-accessible
│   ├── [ ] /app/etc directory not web-accessible
│   ├── [ ] .htaccess files in place
│   └── [ ] Security headers configured (CSP, X-Frame-Options, etc.)
│
├── 3. Magento Configuration
│   ├── [ ] Production mode enabled (bin/magento deploy:mode:set production)
│   ├── [ ] Developer mode disabled in production
│   ├── [ ] Default admin URL changed (not /admin)
│   ├── [ ] Admin account uses strong password
│   ├── [ ] 2FA enabled for all admin accounts
│   ├── [ ] Admin IP allowlisting configured
│   ├── [ ] Admin CAPTCHA enabled
│   ├── [ ] Session timeout configured (15-30 min)
│   ├── [ ] Password minimum 12 characters enforced
│   └── [ ] reCAPTCHA on login/registration
│
├── 4. File System Security
│   ├── [ ] File permissions: 644 for files, 755 for directories
│   ├── [ ] app/etc/env.php permissions: 640
│   ├── [ ] var/ permissions: 770
│   ├── [ ] pub/media/ permissions: 775
│   ├── [ ] Generated files not writable by web server
│   ├── [ ] .user.ini or php.ini in pub/
│   └── [ ] No .git directory web-accessible
│
├── 5. Database Security
│   ├── [ ] Database user has minimum required privileges
│   ├── [ ] Database not accessible from public internet
│   ├── [ ] Database password not in version control
│   ├── [ ] Database prefix changed from default
│   ├── [ ] Backups encrypted and stored securely
│   └── [ ] SQL mode includes STRICT_TRANS_TABLES
│
├── 6. Payment Security
│   ├── [ ] PCI DSS compliance verified
│   ├── [ ] Payment gateway uses tokenization (no card storage)
│   ├── [ ] Webhook signatures verified
│   ├── [ ] Webhook source IPs validated
│   ├── [ ] Payment API keys in env.php (not config files)
│   ├── [ ] Test mode disabled in production
│   └── [ ] 3D Secure enabled for card payments
│
├── 7. API Security
│   ├── [ ] REST API rate limiting enabled
│   ├── [ ] GraphQL query depth limited
│   ├── [ ] GraphQL query complexity analyzed
│   ├── [ ] API keys rotated periodically
│   ├── [ ] OAuth tokens have expiry
│   ├── [ ] Customer API only returns own data
│   └── [ ] Admin API requires admin authentication
│
├── 8. Monitoring & Logging
│   ├── [ ] Security event logging enabled
│   ├── [ ] Login attempts logged
│   ├── [ ] Failed payment attempts logged
│   ├── [ ] Admin actions logged
│   ├── [ ] File changes monitored (file integrity)
│   ├── [ ] Outbound connection monitoring
│   └── [ ] Alert on suspicious activity
│
├── 9. Vulnerability Management
│   ├── [ ] Magento security patches applied (check composer.json)
│   ├── [ ] Third-party extensions updated
│   ├── [ ] No deprecated/abandoned extensions
│   ├── [ ] Composer audit run regularly
│   ├── [ ] CVE monitoring for dependencies
│   └── [ ] Security scanning tool integrated
│
└── 10. Backup & Recovery
    ├── [ ] Daily automated backups
    ├── [ ] Backups tested monthly
    ├── [ ] Backup storage encrypted
    ├── [ ] Off-site backup location
    ├── [ ] Recovery procedure documented
    └── [ ] RTO/RPO defined and tested

Automated Security Scanning

// Security audit script example
class SecurityAuditor
{
    public function runFullAudit()
    {
        $results = [];

        // 1. Check PHP configuration
        $results['php'] = $this->checkPhpConfig();

        // 2. Check file permissions
        $results['permissions'] = $this->checkFilePermissions();

        // 3. Check for known vulnerabilities
        $results['vulnerabilities'] = $this->scanVulnerabilities();

        // 4. Check for exposed debug endpoints
        $results['endpoints'] = $this->scanExposedEndpoints();

        // 5. Check SSL/TLS configuration
        $results['ssl'] = $this->checkSslConfig();

        // 6. Check security headers
        $results['headers'] = $this->checkSecurityHeaders();

        // 7. Check for hardcoded secrets
        $results['secrets'] = $this->scanHardcodedSecrets();

        return $results;
    }

    private function checkPhpConfig()
    {
        $critical = [
            'allow_url_fopen' => 'Off',
            'allow_url_include' => 'Off',
            'expose_php' => 'Off',
            'display_errors' => 'Off',
        ];

        $results = [];
        foreach ($critical as $setting => $expected) {
            $current = ini_get($setting);
            $results[$setting] = [
                'current' => $current,
                'expected' => $expected,
                'pass' => $current === $expected,
            ];
        }
        return $results;
    }

    private function scanHardcodedSecrets()
    {
        $patterns = [
            '/password\s*[=:]\s*[\'"](.*?)[\'"]/i',
            '/api[_-]?key\s*[=:]\s*[\'"](.*?)[\'"]/i',
            '/secret\s*[=:]\s*[\'"](.*?)[\'"]/i',
            '/aws[_-]?(?:access|secret)[_-]?key\s*[=:]\s*[\'"](.*?)[\'"]/i',
        ];

        $results = [];
        foreach ($patterns as $pattern) {
            $matches = $this->grepFiles($pattern, 'app/etc/');
            $results[] = $matches;
        }
        return $results;
    }
}

Real-World Incident: Magento Marketplace Compromise (2022)

A malicious extension on the Magento Marketplace contained backdoor code that:

  1. Collected admin credentials via modified login form
  2. Injected payment skimmer on frontend
  3. Exfiltrated data to attacker-controlled server

Audit controls that would have caught this:

  1. Extension code review before installation
  2. File integrity monitoring (FIM)
  3. Outbound connection monitoring
  4. Regular composer audit
  5. Principle of least privilege for file permissions

Real-World Security Incidents

Major Magento Security Incidents

1. Magecart Campaigns (2018-2024)

Incident Timeline:
├── 2018: British Airways - 380K cards stolen
├── 2019: Ticketmaster UK - 40K cards compromised
├── 2020: Multiple retailers via form_key bypass
├── 2021: New cart skimmer variants
├── 2022: Supply chain attacks via extensions
├── 2023: Targeted high-volume retailers
└── 2024: AI-powered evasion techniques

Attack Vector:
1. Compromise admin (phishing, brute force, extension backdoor)
2. Modify checkout template or inject JS
3. Skimmer captures card data client-side
4. Data exfiltrated to attacker infrastructure

Impact: $Billions in losses, regulatory fines

Prevention checklist:

  • 2FA for all admin accounts
  • CSP headers with strict policy
  • File integrity monitoring (FIM)
  • Admin IP allowlisting
  • No third-party scripts in checkout
  • Subresource Integrity (SRI) for external resources
  • Regular security scanning

2. Magento 2 SQL Injection CVE-2022-24086

// Vulnerability: Unescaped parameter in email template
// Affected: Magento 2.3.7, 2.4.3-p1 and earlier

// Attack: Email template injection via subject field
POST /rest/V1/guest-carts/{cartId}/shipping-information
{
    "addressInformation": {
        "shippingAddress": {
            "firstname": "' UNION SELECT * FROM admin_user --"
        }
    }
}

// Impact: Full database dump including admin passwords
// Fix: SUPEE-11314, update to patched version

3. Unsafe Deserialization CVE-2020-5770

// Vulnerability: Unsafe deserialization in import functionality
// Affected: Magento 2.3.3 and earlier

// Attack: Crafted serialized payload in import file
$payload = 'O:30:"Magento\Framework\App\ObjectManager":1:{s:3:"_c";s:10:"eval";}';

// Impact: Remote code execution on server
// Fix: Implement allowed_classes restriction
// Patch: Disable unserialize in favor of JSON imports

4. PayPal Checkout Bypass (2023)

Incident: Attackers manipulated PayPal webhook signatures
by exploiting weak HMAC validation in custom payment modules.

Vulnerable code pattern:
public function validateWebhook($request)
{
    $signature = $request->getHeader('PAYPAL-SIGNATURE');
    // Missing: Actual HMAC verification
    // Only checks header exists
    return true; // Always valid!
}

Fix:
public function validateWebhook($request)
{
    $signature = $request->getHeader('PAYPAL-SIGNATURE');
    $expected = $this->computeHmac($request->getContent());
    return hash_equals($expected, $signature);
}

5. Supply Chain Attack via Marketplace Extension (2022)

Incident: Popular SEO extension contained obfuscated backdoor

Backdoor mechanism:
1. Checks if request is from admin panel
2. Creates hidden admin account
3. Injects payment skimmer on frontend
4. Sends data to attacker server

Detection:
- Reviewed extension source code before installation
- Ran static analysis tool (phpcs with security rules)
- Monitored outbound connections
- Noticed unusual database queries

Response:
1. Removed extension
2. Purged backdoor admin accounts
3. Audited all database tables
4. Rotated all credentials
5. Implemented extension vetting process

Incident Response Playbook

// Magento incident response procedure
$incidentResponse = [
    'detection' => [
        '1. Identify suspicious file changes (FIM)',
        '2. Check for unauthorized admin accounts',
        '3. Review access logs for anomalies',
        '4. Scan for known malware signatures',
    ],
    'containment' => [
        '5. Take site to maintenance mode',
        '6. Block attacker IP addresses',
        '7. Rotate all credentials',
        '8. Revoke active sessions',
    ],
    'eradication' => [
        '9. Remove malicious files/code',
        '10. Restore from known-good backup',
        '11. Apply security patches',
        '12. Clean database of injected data',
    ],
    'recovery' => [
        '13. Verify site integrity',
        '14. Enable monitoring',
        '15. Test payment processing',
        '16. Resume normal operations',
    ],
    'lessons' => [
        '17. Document incident timeline',
        '18. Identify root cause',
        '19. Update security controls',
        '20. Train team on prevention',
    ],
];

Practice Problems

0 / 3 solved
Security Review Exercise

Review code for XSS, SQL injection, SSRF, RCE, and authentication vulnerabilities.

Solution
// Findings:
// 1. XSS: No output escaping in templates
// 2. SQLi: String concatenation in queries
// 3. SSRF: Unvalidated URL fetch in webhook handler
// 4. RCE: unserialize() with user input
// 5. Auth: MD5 password hashing, no 2FA
// 6. CSRF: Missing form key
// Fixes:
// 1. Add escape filter + CSP headers
// 2. Use parameterized queries
// 3. Implement URL allowlist + IP validation
// 4. Replace with json_decode()
// 5. Upgrade to bcrypt + enforce 2FA
// 6. Add CSRF token + SameSite cookies
Threat Modeling Exercise

Create a STRIDE threat model for a Magento 2 checkout flow including payment processing.

Solution
/*
STRIDE Analysis for Checkout Flow:

1. Cart → Checkout (External to Internal)
   - Spoofing: Session hijacking → Mitigation: Secure cookies + session fingerprint
   - Tampering: Price manipulation → Mitigation: Server-side price validation
   - Info Disclosure: Cart data leakage → Mitigation: HTTPS only

2. Checkout → Payment Gateway (Internal to External)
   - SSRF: Forged webhook URL → Mitigation: URL allowlist
   - Tampering: Amount modification → Mitigation: HMAC signature verification
   - Repudiation: Denying order → Mitigation: Webhook audit log

3. Payment Gateway → Magento (External to Internal)
   - Spoofing: Forged webhook → Mitigation: IP allowlist + HMAC verification
   - Tampering: Status manipulation → Mitigation: Idempotency keys

4. Order → Database (Internal to Secure)
   - SQLi: User input in queries → Mitigation: Parameterized queries
   - Info Disclosure: Card data exposure → Mitigation: Tokenization (no card storage)
*/
Security Audit Implementation

Implement automated security checks for a Magento 2 store covering file permissions, exposed endpoints, and configuration validation.

Solution
/*
class MagentoSecurityAudit {
    public function run() {
        $results = [
            'php_config' => $this->checkPhpConfig(),
            'file_permissions' => $this->checkPermissions(),
            'exposed_endpoints' => $this->scanEndpoints(),
            'security_headers' => $this->checkHeaders(),
        ];
        return $results;
    }

    private function checkPhpConfig() {
        $checks = [
            'allow_url_fopen' => 'Off',
            'allow_url_include' => 'Off',
            'expose_php' => 'Off',
        ];
        // Compare ini_get() against expected values
    }

    private function checkPermissions() {
        $critical_files = [
            'app/etc/env.php' => '0640',
            'var/' => '0770',
        ];
        // Verify actual vs expected permissions
    }
}
*/

Quiz

1. What is XSS?

Question 1 options

2. How should passwords be stored?

Question 2 options

3. What is CSRF?

Question 3 options

4. How to prevent SQL injection?

Question 4 options

5. What is SSRF and why is it critical for payment webhooks?

Question 5 options

6. Which PHP function enables Remote Code Execution when used with user input?

Question 6 options

7. What does STRIDE stand for in threat modeling?

Question 7 options

8. How should Magento admin panel access be secured?

Question 8 options

Flashcards

Question

XSS prevention?

Answer

Escape output with htmlspecialchars(ENT_QUOTES), use CSP headers, SRI for external scripts

Question

Password storage?

Answer

bcrypt (cost 12+) or argon2id hashing, never plain text, rehash if algorithm changes

Question

CSRF prevention?

Answer

CSRF token in forms, SameSite cookie attribute, double-submit for APIs

Question

SQL injection prevention?

Answer

Parameterized queries, query builder, repository pattern, never string concatenation

Question

Input validation?

Answer

Validate type, length, format, range; sanitize before use; allowlist over blocklist

Question

SSRF prevention for webhooks?

Answer

URL allowlisting, IP verification, block internal ranges (127.0.0.0/8, 10.0.0.0/8, etc.), HTTPS only, verify HMAC signatures

Question

RCE prevention?

Answer

Avoid unserialize() with user input (use json_decode), validate file uploads (MIME + content), remove eval/exec/shell_exec, sandbox templates

Question

STRIDE threat modeling?

Answer

Spoofing, Tampering, Repudiation, Info Disclosure, Denial of Service, Elevation of Privilege - analyze each component

Question

Magento admin security checklist?

Answer

Custom URL, 2FA enforced, IP allowlisting, CAPTCHA, session timeout, strong passwords, audit logging

Question

File upload security?

Answer

Validate MIME type + content (not extension), generate safe filename, store in non-executable directory, set 644 permissions

Revision Notes

Key Takeaways

  • 1. XSS: Escape output with htmlspecialchars(ENT_QUOTES), implement CSP
  • 2. Passwords: bcrypt/argon2 with sufficient cost, rehash on algorithm change
  • 3. CSRF: Token + SameSite cookies, double-submit for APIs
  • 4. SQLi: Parameterized queries, query builder, never concatenate
  • 5. SSRF: URL allowlisting, IP validation, block internal ranges, HTTPS only
  • 6. RCE: Never unserialize user input, validate file uploads, remove dangerous functions
  • 7. Threat Modeling: Apply STRIDE to each Magento component
  • 8. Audit: Run comprehensive checklist quarterly, automate scanning
  • 9. Admin Security: Custom URL + 2FA + IP allowlisting + CAPTCHA
  • 10. Incidents: Magecart skimmers, CVE-2022-24086 SQLi, supply chain attacks

Interview Tips

  • Explain SSRF with payment webhook example
  • Describe RCE vectors in Magento context
  • Walk through STRIDE analysis for checkout flow
  • Discuss real Magecart attack vectors and prevention
  • Explain why unserialize() is dangerous and alternatives
  • Describe defense-in-depth for admin panel security
  • Outline incident response procedure for compromised store
  • Explain CSP implementation for Magento
  • Discuss PCI DSS requirements for payment handling
  • Describe file integrity monitoring approach

Cheat Sheet

Magento Security Review

XSS: htmlspecialchars(ENT_QUOTES) + CSP + SRI
Passwords: bcrypt(cost>=12) / argon2id
CSRF: form_key + SameSite=Lax
SQLi: Parameterized queries / Query builder
SSRF: URL allowlist + IP validation + HTTPS only
RCE: json_decode over unserialize + file validation + no eval
Admin: Custom URL + 2FA + IP allow + CAPTCHA
STRIDE: Spoofing, Tampering, Repudiation, Info Disclosure, DoS, EoP
Audit: Quarterly checklist + automated scanning + FIM
Incidents: Magecart (CSP/FIM), CVE-2022-24086 (SQLi), CVE-2020-5770 (deserialization)