If your OpenCart store is hacked, the priority order is different from a blog or brochure site: first stop any card skimmer from reaching customers, then preserve the evidence and the order data, and only then start cleaning. OpenCart malware tends to hide in a handful of predictable places — PHP files dropped into image/, malicious OCMOD or vQmod modifications, scripts injected into Twig templates or the settings table, and admin users you never created. This is the process I follow to clean a hacked OpenCart store, bring it up to a supported release and lock it down so it does not happen again.
Signs your OpenCart store has been hacked
The symptoms I look for:
- Customers report card fraud shortly after buying from you, or your payment provider contacts you about a pattern of compromised cards. This is the most serious sign and usually means a checkout skimmer.
- Unfamiliar JavaScript on the checkout page — a script loaded from a domain you do not recognize, often named to look like analytics, a CDN or jQuery, or an extra payment form that appears before the real gateway.
- Redirects to spam or scam sites, often only for mobile visitors or visitors arriving from Google.
- Spam pages or keywords in Google results for your domain, or product descriptions and information pages you did not write.
- Admin users you did not create under System > Users, or a user group with permissions it should not have.
- PHP files inside
image/, which should only ever contain images. - Your host suspends the account, or Chrome shows a “Dangerous site” warning.
Contain it first, and keep the order data
- Take a full copy of the store as it is now — files and a
mysqldumpof the database — and store it off the server. You need it to find the entry point, and if card data was involved your payment provider may ask for it. - Stop the checkout from serving malware. Turn on maintenance mode under System > Settings > Server, and on a server you control also restrict the site to your own IP, since logged-in admins bypass maintenance mode and backdoors can still be called directly.
- Change the hosting panel, SFTP/SSH and database passwords from a computer you trust.
- Tell your host. Other sites in the same hosting account can reinfect yours.
# evidence copy: files plus database, timestamped, kept off the server
tar -czf store-infected-$(date +%F).tar.gz /var/www/store
mysqldump --single-transaction -u store_user -p store_db > store-infected-$(date +%F).sqlDo not restore last month’s backup and walk away. On a store, the database is live business data, so I clean it in place rather than replace it.
Diff the files against a clean copy of your exact version
OpenCart has no built-in checksum tool, but the next best thing works well: download the release that matches your installed version from the official OpenCart GitHub releases and compare every file. The version is shown in the admin footer, and in the VERSION constant in index.php.
grep "define('VERSION'" /var/www/store/index.php
# compare live files against the clean release (the upload/ folder of the download)
diff -rq --exclude=image --exclude=storage --exclude=config.php \
./opencart-clean/upload/ /var/www/store/ | grep -v '^Only in ./opencart-clean'Anything reported as different or only present on the live store is either an extension, a legitimate customization or malware, and each one gets a look. Core files that differ I replace from the clean copy. Extension files I replace with a fresh download from the vendor where possible.
Find backdoors in image/, storage and extensions
The diff cannot help with directories that hold user content, so those need their own checks:
# PHP (or disguised PHP) in the image folder should not exist
find image/ -type f \( -name '*.php' -o -name '*.phtml' -o -name '*.php.*' -o -name '.htaccess' \)
# the storage folder holds cache, logs, sessions and uploads, not executable PHP of your own
find system/storage -type f -name '*.php' -not -path '*/vendor/*' -not -path '*/modification/*'
# PHP files changed recently (adjust the window to when the trouble started)
find . -type f -name '*.php' -mtime -21 -printf '%TY-%Tm-%Td %p\n' | sort
# common obfuscation and web-shell patterns
grep -rlE "eval\s*\(\s*(base64_decode|gzinflate|str_rot13)|\\\$_(POST|REQUEST|COOKIE)\[[^]]+\]\s*\(" \
--include=*.php .The system/storage/vendor/ folder contains Composer libraries that ship with OpenCart and system/storage/modification/ contains generated copies of modified files, which is why I exclude them from the first search. Compare the vendor folder against the clean download instead.
For card skimming, look beyond JavaScript. Server-side skimmers are small PHP additions to the payment extension’s catalog controller or to the order model that copy the posted card fields to a file or a remote URL. Any payment extension that takes card numbers on your own page deserves a line-by-line read against the vendor’s original.
Finally, check the install/ folder. It should have been deleted after installation. If it is still there, delete it now; it is a common target and has no purpose on a live store. Also check .htaccess files in every directory for redirect rules, and the system crontab (crontab -l) for anything that re-downloads the malware.
Check OCMOD and vQmod modifications
Modifications are a favourite hiding place because they patch core files at runtime without changing them on disk, so a clean diff can still leave the store infected. In the admin, open Extensions > Modifications and go through the list: every entry should map to an extension you know you installed. Then check the underlying table and folders directly, because the admin list is only as trustworthy as the admin itself:
-- installed OCMOD modifications (replace oc_ with your table prefix)
SELECT modification_id, name, code, author, status, date_added
FROM oc_modification ORDER BY date_added DESC;
-- search the XML itself for script tags, remote URLs or eval
SELECT modification_id, name FROM oc_modification
WHERE xml LIKE '%<script%' OR xml LIKE '%eval(%' OR xml LIKE '%base64_decode%';- OCMOD files on disk: in OpenCart 3, files named
*.ocmod.xmlinsystem/are also applied, so list them too. - vQmod: if the store uses it, review every file in
vqmod/xml/.
After deleting or disabling anything malicious, press Refresh on the Modifications page so OpenCart rebuilds system/storage/modification/ from the clean list, then clear the cache in system/storage/cache/ and the theme cache. Until you refresh, the old generated files keep running.
Clean injected scripts from templates and the database
Most checkout skimmers I look for are JavaScript, and in OpenCart they typically sit in one of three places: the theme’s Twig templates (header.twig, footer.twig and the checkout templates), a JavaScript file the theme already loads, or the database. The files are covered by the diff and a grep for <script and unknown domains in catalog/view/. For the database:
-- settings: analytics, custom code fields and anything else that renders on the storefront
SELECT store_id, `code`, `key` FROM oc_setting
WHERE `value` LIKE '%<script%' OR `value` LIKE '%eval(%' OR `value` LIKE '%atob(%';
-- OpenCart 3 stores templates edited in Design > Theme Editor here
SELECT theme_id, route, date_added FROM oc_theme;
-- content that renders as HTML on the storefront
SELECT information_id FROM oc_information_description WHERE description LIKE '%<script%';
SELECT product_id FROM oc_product_description WHERE description LIKE '%<script%';
SELECT module_id, name FROM oc_module WHERE setting LIKE '%<script%';Some hits are legitimate, such as a tracking snippet you added on purpose, so review each one before removing it, and take a fresh database backup before any UPDATE or DELETE.
Then check who can log in:
SELECT user_id, username, email, user_group_id, status, date_added, ip
FROM oc_user ORDER BY date_added DESC;
-- which groups have which permissions
SELECT user_group_id, name FROM oc_user_group;Delete any user you do not recognize, and check that no user group has been quietly given modify rights it should not have. Also look at oc_api: the API user is used by the admin to edit orders, and an attacker who adds an IP to oc_api_ip or reads the API key can use it.
If the checkout was skimmed: your card data obligations
If there is any sign that card details were captured, this stops being only a technical problem. Contact your payment provider or acquirer promptly with what you found and the likely time window; under your merchant agreement they may require a formal investigation. Depending on where you and your customers are, data protection law may also require you to notify a regulator or the affected customers. That is a legal question, so get proper advice rather than guessing, and keep the evidence copy you took at the start.
The long-term fix is to keep card numbers off your server entirely. A gateway that uses a hosted payment page, or hosted fields and tokenization where the card inputs are served by the provider, means a compromised store has far less to steal and shrinks your PCI scope, though a skimmer can still inject a fake form in front of the real one. If your current gateway extension posts raw card numbers through your own server, replacing it is worth putting on the list.
Upgrade to a supported release
Cleaning an old version leaves the original hole open. Once the store is clean, I bring it up to the latest release of its branch, and check that the server runs a PHP version that still gets security fixes and that the release supports. For a store on a 3.x release, that means the current 3.0.x patch release at minimum. The larger question is the move to OpenCart 4, which is a rebuild rather than an update; I cover that separately in the OpenCart 4 upgrade guide.
Update every extension too, and remove any that are abandoned, pirated or no longer used. A “free” copy of a paid extension from a download site is one of the most common ways stores get backdoored in the first place.
Lock it down: OpenCart security hardening
- Rename the admin folder and update the paths in
admin/config.php(now in the renamed folder) to match. - Move
storage/outside the web root and updateDIR_STORAGEin both config files. Recent releases show an admin dashboard warning whenstorage/is still inside the web root. - Restrict the admin by IP allowlist or HTTP basic authentication at the web server, so the login form is not reachable from the whole internet.
- Block PHP execution in
image/at the web server, so an uploaded shell cannot run even if one gets in. - Make both
config.phpfiles read-only (for examplechmod 444) once the store is configured. - Rotate every credential: database password, every admin password, hosting and SFTP/SSH, the OpenCart API key, SMTP, and all payment gateway API keys and webhook secrets. Assume the attacker read
config.phpand the settings table. - Put a WAF in front. Cloudflare’s managed rules and rate limiting on the admin login and checkout block a lot of automated attacks before they reach PHP. My Cloudflare setup work covers this.
- Backups and file-integrity monitoring: automated daily database and file backups stored off the server, plus a check that alerts when PHP files or templates change unexpectedly.
# nginx: no PHP execution under image/ and storage
location ~* ^/(image|system/storage)/.*\.php$ { deny all; }
# nginx: admin (renamed) reachable only from your IP
location ^~ /secret-admin-7f3/ {
allow 203.0.113.10;
deny all;
# keep your existing PHP handling for this location here
}On a VPS, the server matters as much as the store: unexpected SSH keys, unknown system users and outdated packages can all bring the attacker back. I tighten that side through server security hardening, and the Linux server hardening checklist lists what I check.
When to hand it over
A careful owner with SSH access can follow these steps. If the checkout may have been skimmed, the store runs a stack of custom modifications, or you are not comfortable judging which code is malicious, it is usually cheaper to have the cleanup done once properly than twice. My OpenCart development and cleanup service covers the full process above: containment, the file and database cleanup, the entry point, credential rotation, patching and hardening. Small jobs are billed at $20/hour; a larger cleanup gets a fixed price agreed in writing before I start.
Frequently asked questions
How do I know if my OpenCart checkout has a card skimmer?
Open the checkout as a guest in a private window and look at the browser's network tab for scripts or form submissions going to domains you do not recognize. Then search your theme templates, JavaScript files, the oc_setting table and the modification list for script tags and unknown URLs. Skimmers are often conditional, so a clean look once does not prove the store is clean.
Can I just restore a backup to fix a hacked OpenCart store?
Only if the backup predates the infection and you also fix how the attacker got in. Even then, restoring loses every order, customer and stock change since the backup, which is why I usually clean the live database in place instead.
How do attackers usually get into OpenCart stores?
The common routes are outdated OpenCart versions and extensions with known vulnerabilities, pirated extensions that ship with backdoors, weak or reused admin passwords on an admin panel open to the internet, and other compromised sites on the same hosting account. The access logs around the time the first malicious file appeared usually show which one.
Does renaming the admin folder make OpenCart secure?
It cuts down automated login attempts, but it is not a security control on its own. Pair it with an IP allowlist or HTTP authentication, strong unique passwords, an up-to-date release and a WAF in front of the store.
Do I have to tell customers if my store was hacked?
If card data or personal data may have been exposed, you may have obligations to your payment provider under your merchant agreement and legal duties to notify regulators or customers, depending on where you and they are. Contact your payment provider first and get legal advice on notification.