Sites sharing one server: one breach reached every site
Several websites ran as one user on one server. When one was hacked, all were, and the security plugins were doing nothing. What separating them changed.
You run several websites on one server, and the least important one is a demo, a parked domain, something nobody has logged into for a year. It has an out-of-date plugin. An attacker gets in through it, and within the same window your main site is compromised too. The security plugins you paid for never raised an alarm, because it turns out they were not doing anything.
That is what happened on a server we took over. Several sites ran through one shared set of worker processes, all as the same system user. One was compromised, and every other site followed in the same window. The attacker did not need a second weakness. Once code ran as the shared user, every other site’s files were already writable by it.
The cost was every site on the machine at once, plus 539 MB of site backups that had been downloadable by anyone who guessed the address. Restoring a whole estate is a far larger job than restoring the one site that was the way in.
What was actually going on
The default way to run a web server is one service user for everything on the machine. It is the path of least resistance, and you get it if you never think about it. The effect only shows under attack. If an attacker can run code in the process, what can they read and write? On a shared setup the answer is every site. That includes the configuration files that hold each site’s database password, and the upload folders that have to be writable so visitors can send in files.
So the number of sites on the machine multiplies the damage from any single weakness. The forgotten site is always the one that falls, because neglect is what makes it fall, and it is the one nobody thought worth protecting.
There was a second failure, and it is the more general one. The sites carried the usual protection plugins, which write rules to block direct access to upload folders, configuration files and backups. Their settings pages showed them as active. They did nothing at all, because this server does not read those per-folder rule files. That mechanism belongs to a different web server. The rules were written to disk and never consulted by anything.
That is how 539 MB of backups sat publicly downloadable. The plugin that made the backups had written a rule denying access to that folder, and the rule was never read. Anyone who guessed the path had a full copy of the site and its database.
What we changed
Each site now runs as its own dedicated user, in its own set of worker processes, confined to its own folder and nothing else. That gives three separate protections. Separate users mean site A’s files are not writable by the user site B runs as, which is what actually contains a breach. Separate worker sets mean a site that hangs stalls only itself; before, a hung site used up the workers for everything, and we had read that as “the server is down” and blamed capacity, when it was isolation. Folder confinement stops the software opening files outside its own folder, a second line in case permissions were set wrong somewhere, which on any machine with a history they will have been.
The protections that were doing nothing were replaced by one server-level fragment, included by every site: no running of programs in upload folders, no serving of configuration or backup file types, no hidden files. It sits in the layer that actually handles requests. We check it by hand by requesting a file that should be refused and confirming the refusal came from that layer.
The same pass turned up a third trap. The server’s remote-login rules are read from a folder of small files, and the first matching rule wins, in alphabetical order. Our hardening file sorted after a default file supplied by the vendor, so every conflict went the default’s way and ours never applied. There was no error, because the configuration was valid. We renamed the file so it sorts first.
What it did not fix
Separate users cost more to run: more files to maintain, more memory because the worker sets do not share capacity, and a deployment step that must get ownership right per site. We judged that cheaper than restoring an estate.
Isolation does not stop a site being compromised. It limits what one compromised site can reach. Passwords, patching and scanning lower the chance of a break-in, and isolation lowers its cost. You need both, but only one of them still helps on the day you learn the other failed.
The pattern, for anyone hosting more than one site on one machine
Two questions, neither needing a security background. If someone ran code in one site, what else could they read and write? Answer by listing folders, not by estimating likelihood. And which of your protections have you personally watched refuse a request you made on purpose? Anything you have not seen refuse something is an intention, not a control.
Where this ends up
Taking over an estate like this, separating its sites and checking that each protection really refuses what it claims to, is the work described under enterprise modernisation.