Ir para o conteúdo principal
AllsWeb
Measurements
  1. The aaPanel benchmark, in full
  2. MariaDB versions, measured — four pairs, no reportable difference
  3. The OS and stack round — six axes, one reportable result
  4. PHP versions, measured — four pairings, four washes
  5. Security and isolation, measured
  6. How the 6amMart optimisation was measured
  7. What we withdrew
  8. How we measure

Security and isolation, measured

On this page

  • What was actually attempted, not asserted
  • The leak side and the attack side are different questions
  • What the kernel namespace closed, and what it cost
  • Per-project Redis, and the ACL that does not exist
  • What the hardening costs
  • The audit that produced this
  • A backup is only proved by a restore
  • Related

Security and isolation, measured

Every project on a SixPanel server gets its own unix user, its own PHP-FPM pool, and its own password-protected Redis instance with kernel-sandboxed background workers. This page is the evidence for that design and the audit that produced it — including the cost, because hardening is not free and publishing only the benefit would be advertising.

The three answers worth having up front. Every cross-project read attempt failed, and what refuses it is the kernel rather than a check inside PHP. The whole security layer costs about 20 microseconds against a 26-millisecond page — roughly 0.08 % — measured as CPU time per request rather than throughput. And a six-dimension audit raised 44 findings, of which 38 survived an independent skeptic whose default was to refute them.

BoundaryHow it is drawnMeasured cost
Project against projectseparate unix user, 0700 application tree, 0600 settings file, own database user with schema-scoped grants, own worker poolnone — it is a file mode
Project against the machinesystemd mount namespace on the worker poolidentical to no sandbox: 509 path-cache entries and 217 filesystem lookups per request, before and after
Project against project, in the cacheone Redis instance per project, reached through a 0750 directoryabout 3.2 MB of RAM per project
Everything above, per request—about 20 µs of ~25,600 µs

What was actually attempted, not asserted

"Isolated" is a claim that can be tested, so it was. Every read below was attempted from inside one project's PHP, on the SixPanel box and on a tuned aaPanel box of the same specification, with the same shop's data.

Every cross-project read failed on both machines. What differs is the kind of refusal, and that difference decides what happens when something goes wrong.

On aaPanel every site runs as the same unix user with world-readable files, so a PHP-level directory restriction is the only barrier — a check inside the process that is being asked to respect it. On SixPanel the refusal comes from the kernel declining a different user id.

Where the two overlap they part company on one measured case: a 0600 file another site had written into the shared temporary directory was readable on aaPanel and denied on SixPanel.

The permission modes behind that, as installed:

ObjectMode and owner
Project application tree0700
Project settings file (.env)0600
PHP-FPM pool socket0660
Redis runtime directory, per projectgroup-owned by that project, not traversable by another

The leak side and the attack side are different questions

This is the finding that changed how the audit was run, and it is worth more than any single number on the page.

Reading the isolation result as "nothing leaks, therefore isolated" was wrong, because it only asked one of two questions. What a project's PHP could not read was every secret: the panel's own settings, the system password file's shadow, every other project's settings file. What it could read was the machine — the web server's configuration, every site definition the panel writes, the other pools' user names and socket paths, the panel's own code, the shared temporary directory and its other users' files, and every other process's command line.

None of that is a secret. All of it is reconnaissance handed to whoever gets code execution through an uploaded theme or a plugin. And one path mattered more than the rest: the directory holding one site definition per project names every domain on the server.

The same distinction cut the other way on the vendor codebase, which is why it is a method point rather than a one-off. An open image proxy in the untouched CodeCanyon storefront was rated high rather than critical for a precise reason: exploiting it costs the owner processor time, disk and reputation — arbitrary remote content re-served under their own certificate — but not data. And a separate gap where any .php file under the web root would execute was rated the same way: a defence-in-depth gap that needs a prior arbitrary-write foothold, not a live remote-code-execution hole.

What to take from this

Audit pairs, not components. A component with nothing to leak can still be a complete attacker, and a component that leaks nothing valuable can still hand over the map. Asking only "what could escape from here" gets a clean answer from a machine that is wide open in the other direction.

What the kernel namespace closed, and what it cost

The reconnaissance surface above was closed with a systemd mount namespace on the worker pool. The cost was measured before the change was accepted, on the same box and the same requests.

Held constant: same box, same shop, same endpoints, before and after the drop-in.

MeasureBeforeAfter
Entries visible in the shared temporary directory570
Processes visible from inside a project's PHP1386
PHP path-cache entries per request509509
Filesystem lookups per request217217

The last two rows are the point. The confinement is drawn once, when the process starts, so it does not appear in a per-request measurement at all. The same treatment was later applied to the four per-project background workers: the panel's own directories became unreadable to them, visible processes went from 141 to 5, and the application tree stayed writable.

Two directives were deliberately left out, each for a measured reason. One that would have blocked writable-executable memory is excluded because it disables the JIT mode that measures 6.0 % on this application's PHP endpoints — that is a real trade, and it was made in favour of the speed. Two paths cannot be closed at all: the system password file, because the C library needs it to turn a user id into a name, and the pool directory, because the worker master needs to know which pools to run. Both are root-owned and read-only.

Two further proposals were rejected by measurement rather than shipped: hiding the web server's configuration closed nothing and broke the readiness tool, which reads it; and blocking the panel's directory wholesale would have taken the database administration tool down with it, since that runs as another pool of the same service.

What a per-service sandbox cannot do

Every one of those directives applies to a service, and all projects share one PHP-FPM service. It cannot separate project A from project B. That is the unix user's job, and the unix user is what actually does it. A sandbox presented as tenant isolation is a category error.

Per-project Redis, and the ACL that does not exist

This is the clearest case on the page of a design forced by a measurement rather than chosen.

The original arrangement was one Redis instance behind one password, and every project's own settings file had to contain that password for its own application to work. Which means the password isolates nothing: starting from one project's unix user, a probe read another project's cached SMTP password, Firebase credentials, Maps keys, SMS gateway key, reCAPTCHA secret and two payment gateways' secrets — and writes were permitted, so the victim's cached mail configuration was poisonable.

The obvious fix is an access-control list scoping each project to its own database number. It does not exist. Redis 7.0 — which is what Ubuntu 24.04's archive ships, and the archive is where every component comes from — has no per-database ACL. A key pattern applies in every database, and the commands that enumerate keys leak key names whatever the patterns say. Denying the one command that would stop one shop emptying another's cache breaks the admin panel's own "clear cache" button, on both the optimised and the untouched codebase.

So the boundary is the filesystem, not the password. One instance per project, its socket inside a runtime directory group-owned by that project alone. A process running as another project's user cannot traverse that directory, so it never reaches the protocol — knowing the password would not help it. The per-instance password is the second lock.

The reproducer went from failing to passing in both directions: refused with a permission error while holding the target's own password.

What it costs, measured on a 2-core box:

MeasureResult
RAM per instance, idle+3.2 MB
Memory budgetsplit N ways across projects, not multiplied
Worker cap unchanged18 of 20 box-and-project combinations; the two that move lose one worker
Application code changesnone — a socket path and a zero port
Migration46 keys, 45,550 bytes, 13.9 ms; key set and value fingerprints identical; nobody signed out

The change also retired a 15-project ceiling and the stack-wide shared password. And moving to a unix socket was faster rather than slower, though not in a way a shopper feels: at 50 concurrent clients, SET 40,053 to 66,519 operations per second and GET 48,780 to 65,789, with single-client GET median 0.039 ms to 0.031 ms. At the endpoint the same change measured 19.1 to 19.6 ms on one arm and 54.4 to 56.2 ms on the other — both inside run-to-run spread, so no endpoint claim is made from it.

What the hardening costs

The whole security layer was measured, because a claim that it is free needs a number behind it.

The first attempt was thrown away and is worth saying so. It measured throughput, and on a 2-core box the benchmark client competes with the web server and PHP for the same processors: turning security off made the box slower in 5 of 7 per-control samples, which is impossible. Those numbers are not reported.

The reported run measures processor time per request, read from the service manager around an exact request count, so the client's own work cannot enter it and contention cannot fake a win. Every state is interleaved, and the shop is verified answering normally after every toggle.

Held constant: Ubuntu 24.04, 2 cores, 3,915 MB, a real shop with 190 tables, client running on the box itself over loopback with the shop's real hostname — so no network is in the figure. One /login/admin page: a genuine PHP request with a session and a rendered view.

Processor time per request
PHP workers~25,600 µs
Web server~376 µs
Everything added for security~20 µs

Four interleaved pairs, in microseconds per request:

        web server                        PHP workers
ON      384.6  390.8  381.6  345.4        25155  26784  25489  25007
OFF     364.6  363.9  323.1  371.0        25753  26576  25572  25966

PHP is higher with security off, which is the honest way of saying there is no PHP-side cost to measure. The entire difference is on the web server's side. A static file, which never reaches PHP, moved 60.5 µs to 58.6 µs — about 2 µs.

Per control, on the PHP path, two samples each:

ControlCostNote
Security headers (61 of them)~70 µslargest single item
Rate limiters (49 rules)~45 µsthe lookup only — see below
Origin lock~5 µs
Disabled dangerous PHP functions0PHP time identical both ways
systemd sandbox0process attributes, set once at start
Log-reading ban service0never touches a request

Those per-control figures sit at the noise floor and add up to more than the 20 µs the aggregate showed, so they are ceilings rather than a budget. The true total is under about 100 µs — under 0.4 % of a page.

Why the PHP side is free

Because there is no open_basedir. That setting validates a path on every file operation, and it is the single largest measured item in a competing panel's slowness: about 60 % of a gap that had been sitting unattributed, worth 17 to 23 ms per request with filesystem lookups up 19- to 33-fold. The aaPanel benchmark has that measurement in full.

The same boundary is drawn once, in the kernel, by the sandbox above — and the table two sections up is what "once" costs: nothing per request. That single choice is why the PHP column shows no cost to find.

The limiter gives CPU back

Loopback is exempt from every rate limiter by design, so the run above measured the lookup and never the accounting. With the exemption lifted for the length of one test and 4 concurrent clients pushed at a 120-per-second zone:

600 requests -> 200: 225   429: 375     (3,370 per second offered)

62.5 % refused, and a refusal costs about what serving a small static file costs. Under attack this control saves processor time rather than spending it.

Not in that number

TLS is in both baselines, so it is not attributable to hardening — and it is not optional. The CDN is bypassed by a loopback client entirely; measured separately from a real connection, an edge cache hit answered in 341–368 ms against 456–595 ms when it had to reach the server, so roughly 150–200 ms saved on requests that never arrive at all. Two-factor authentication, panel bytecode and signed manifests are panel-only and never on a shopper's path. And everything above was measured at an idle load average — under real concurrency the relative share should hold, since all of it is fixed work per request, but that is reasoning rather than a measurement.

The audit that produced this

Six dimensions: kernel and firewall posture, rate limiting, anti-tampering, application and server protection, setup-flow completeness, and code health. Every finding was then re-read by an independent skeptic whose default was to refute it.

Count
Findings raised44
Survived the skeptic38
Refuted6

The most serious of them is a good illustration of what an audit is for. Every rate limit tier — authentication, search, writes — was bypassable by prefixing the request path with the front controller's own filename. The limits were all correctly configured and all of them could be walked around.

The refutations are worth as much as the confirmations, and two are worth naming. A proposed host firewall turned out to have a default rule set byte-for-byte identical to the box's existing reachability — it would have added a component and changed nothing. And a proposed rate limit on the image endpoint would have cut a real shopper's image fan-out 4× on the first page they loaded.

The defect that mattered most was not a security finding

A project's installation ran against a different project's database. Measured on a project whose own settings file correctly names its own database:

BeforeAfter
Tables in the project's own database0190
Tables in the other project's database1900
What the install job reportedevery step okevery step ok

The cause is a genuinely subtle one: the panel's service unit loads the panel's own environment, and the framework loads its settings file in immutable mode — a name already present in the process environment is never overwritten by the file. The same mechanism, found earlier, had a security consequence too: per-project background workers were running against the first project's database as the first project's database user, and were additionally handed the database root password and the panel's own admin password. Six colliding names plus two panel secrets. The default project was shielded by accident, because its cached configuration means the environment is never consulted — and the untouched vendor tree, which must not be config-cached, was fully exposed.

That is a standing reason this product is tested on both codebases rather than one.

Two related classes worth publishing

A file mode that reported green. A project's settings file ended up owned by root at 0600 while PHP runs as the project user, so the application could not read it. The shop worked for ninety minutes on a cached configuration file that root had written 0644 with the database password inside it; the first update cleared that cache and every page failed, naming the framework's built-in default database — which is the proof that the settings file was never read at all. The panel's own health check read the file successfully every time and reported green, because the panel runs as root. A root-owned tool cannot answer "can this application read this file." The check now asks the application.

A newer file shadowing the release's own. The module loader resolves a plain JavaScript file before its compiled form, and an update lays a new archive over the install directory without deleting anything. So a box could report the new version and run older code. All 18 route modules were affected. It is a supply-chain shape as much as an update bug — it is the cheapest way to swap out a piece of a protected panel on somebody's server. The first version of the sweep that was meant to fix it removed nothing on a box holding 62 shadowed files, because a substring match made every shadow look like a shipped file. The corrected sweep counts: 62 before, 62 removed, 0 after.

Integrity, and the panel's own exposure

Three read-only answers now exist to "is this still the software I installed": a build-time manifest for the panel's own files, a git-plus-stat baseline for the application (21,777 files under the dependency tree unchanged since the last deploy, bounded to 900 ms), and a write-time ledger over the 37 configuration files the panel writes — a hand edit to one was reported and the file restored.

The panel's own front door was closed in the same round, and the reason it needed closing is the reusable part. Direct access by IP and port:

RequestBeforeAfter
Panel domain with token200200
Raw IP and port with token200404
Same with a forged host header200404
Health endpoint on the raw IP200404

The host header was the only thing the lockdown checked, and the caller writes that header. The peer decides now. Worse, the check that was supposed to close the door ran about 40 seconds after the DNS record was created, failed on a clean install where nothing was actually wrong, left raw access on — and nothing re-ran it. That box would have served its whole life with the panel reachable on its raw address.

The brute-force ban service earned its place quickly: one address banned within four hours, on a box that had existed for half a day and whose address had never been published. Two other ban rules were deliberately not shipped, because both see addresses arriving through a CDN, and a kernel-level ban there could blackhole an entire edge range and take the whole shop off the internet.

Below the web server, kernel network settings went from 1 to 13, the device backlog from 1,000 to 16,384, the half-open queue from 1,024 to 8,192, the panel's descriptor limit from 1,024 to 65,535, and its concurrent connection ceiling from none to 512. The accept queue was already at its 4,096 default and is now named and checked rather than left implicit.

Every one of those is a queue or a timer. Nothing there decides whether a packet is allowed, so nothing there can lock an owner out of their own server — filtering belongs at the provider's edge, where dropped packets never reach the machine at all.

A backup is only proved by a restore

Backups reported success while containing no database, which is why this is measured by reading the artifact rather than the exit code.

One full backup of a live box with the real dataset: 10,428 files and 903.0 MiB processed, 820.2 MiB added, 16 seconds wall clock, three database dumps with the largest 39.1 MB compressed. Read back from the repository, a snapshot contains the certificates, the rendered site definitions, the server's own settings, the panel's records, the shop's database dump, the shop's settings, its uploads, and the API signing keypair.

The restore was verified by row count, not by a status: 192 tables, 236 settings rows, 2 zones, 17,534 customers, 85 stores, 3,865 items, 66,701 orders.

Two defects came out of that check. One project was backing up its entire storage directory — logs, sessions, every compiled view — while another got uploads only. And both codebases keep the API signing keypair directly under storage rather than under the application's own subdirectory, so one project's backup never contained it and a restore would have signed out every phone.

Related

The aaPanel benchmark is where the isolation comparison sits beside the speed one · What we withdrew · How we measure · SixPanel vs SixPanel Docker vs aaPanel for the decision.

PreviousPHP versions, measured — four pairings, four washesNextHow the 6amMart optimisation was measured
AllsWeb

AI + Automation + Human Engineers — builds prontos para produção entregues em 1 a 3 dias. Instalação, personalização, publicação de apps e suporte gerenciado para qualquer script ou código.

  • hi@allsweb.com
  • +91 72328 80007

Explorar

  • Agente IA
  • Automações e Fluxos com IA
  • Otimização de Busca com IA
  • Todas as Soluções
  • Todos os scripts de terceiros
  • Todos os serviços
  • 6amMart otimizado
  • SixPanel
  • SixPreflight
  • Serviço de atualização / upgrade
  • Correção 16 KB Play Store
  • Ofertas e cupons

Empresa

  • Sobre
  • Contrate-nos
  • Suporte e Contato
  • Programa de Afiliados
  • Em Breve

Jurídico

  • Termos e Condições
  • Política de Privacidade
  • Política de Reembolso
  • Política de Pagamento
  • Política de Suporte
  • Uso Aceitável
  • Política de Cookies
  • Termos de Afiliados
  • Aviso Legal

© 2026 AllsWeb. Todos os direitos reservados.