ER Tech
Security audit · Round 1

Example Shop

We looked at your server, your code and every website you run. Nothing was changed. This report shows what we found, what matters most, and how to fix each item.

example-shop.test · www.example-shop.test · admin.example-shop.test
Prepared for
Example Shop LLCthree domains · one server
Prepared by
ER Techertech.ai
Date
5 November 2026Round 1 · read-only
Executive summary
2Critical
4High
4Medium
3Low · info
2Passed
13 findings and 2 passed checks · 15 items in the report
Immediate action required.

Two items can be reached from the internet and lead to the server or to money. The single biggest step is closing SSH to the internet. Backups and updates are separate, fixable problems.

15
Items in the report
13 findings + 2 passed checks
3
Reachable from the internet
SSH, database, old TLS
412
Days since last restart
61 security updates pending
0
Tested restores
backups on the same disk
The rule

Reachable from the whole internet and leads to root, money or customer data: critical. Needs an insider account, a leaked key or a crafted request: high. Defence in depth: medium. Hygiene: low.

What is not rated

Business choices such as running coding agents are not findings. Only the missing guardrails around them are rated.

How to read

Immediate actions first, then every finding under six area headings, then how the system is set up today. Open a finding for the evidence and the fix.

Action plan

Immediate actions.

The two criticals, then the four highs that remove the most risk. Each line opens the full finding with evidence and steps.

01CriticalC-01One shared root key, open to the internetActionClose SSH to the internet and give each operator their own account.Business impactOne lost laptop would no longer hand over the whole business.OwnerServer adminWhenToday02CriticalC-02Live payment key in git historyActionRoll the payment key and remove it from the repository history.Business impactFormer contractors would no longer be able to issue refunds.OwnerDeveloperWhenToday03HighH-01Database port open to the internetActionBind the database to the server itself and close port 5432.Business impactCustomer and order data stop being one password away from anyone.OwnerServer adminWhenThis week04HighH-04Backups live on the same diskActionSend backups off the server and test a restore.Business impactA disk failure would cost hours, not the business.OwnerServer adminWhenThis week05HighH-0261 security updates pending, 412 days without a restartActionApply the pending updates and restart in a maintenance window.Business impactCloses published vulnerabilities in the running kernel.OwnerServer adminWhenThis week06HighH-03Production is edited by handActionStop editing production by hand; release reviewed builds only.Business impactA bad change could be rolled back in minutes.OwnerFounderWhenThis month

The biggest single step is closing SSH to the internet. Everything else gets easier once the only server is no longer reachable by anyone with a key.

Owners are roles, not names. Dates are our proposal; confirm them with us on the call.

Detailed report

All 15 findings, by area.

Six areas: server access, secrets, updates, deployment, backups, and what is already done well.

Showing 15 of 15

Server access & network

Critical4 items
IDFindingSeverityDetails
C-01SSHOne shared root key, open to the internetCritical

In fullThe SSH port is reachable from the whole internet and one root key is shared by every operator

Port 22 answers from any address. All five operators sign in as root with the same key, so a single leaked laptop gives full control of the only server and there is no way to tell who did what.

Affected · 2
  • shop-01.example-shop.test:22
  • /root/.ssh/authorized_keys (1 key, 5 people)
How to fix
Today
  1. Restrict port 22 to the office and VPN addresses in the cloud firewall.
  2. Create one named account per operator with its own key.
Then
  1. Disable root login in sshd_config.
  2. Rotate the shared key.
WhenToday
Effort2 hours
VerifyAn SSH attempt from a mobile connection times out.
Server access & network
H-01FirewallDatabase port open to the internetHigh

In fullThe database port accepts connections from any address

PostgreSQL listens on all interfaces and the host firewall allows 5432 from anywhere. Password authentication is the only barrier.

Affected · 2
  • shop-01.example-shop.test:5432
  • postgresql.conf listen_addresses = '*'
How to fix
  1. Bind PostgreSQL to 127.0.0.1.
  2. Remove the 5432 allow rule.
  3. Reload and confirm the storefront still connects.
WhenThis week
Effort30 minutes
VerifyA connect test from outside the server is refused.
Server access & network
M-01TLSOld TLS versions still acceptedMedium

In fullTLS 1.0 and 1.1 are still accepted

nginx negotiates TLS 1.0 and 1.1. No current browser needs them and payment-card rules forbid them.

Affected · 1
  • nginx.conf ssl_protocols
How to fix
  1. Set ssl_protocols TLSv1.2 TLSv1.3.
  2. Reload nginx.
VerifyA TLS 1.1 handshake fails.
Server access & network
L-01HeadersSecurity headers missingLow

In fullSecurity headers are missing on the storefront

No Content-Security-Policy, X-Frame-Options or Referrer-Policy is sent.

Affected · 1
  • https://example-shop.test/
How to fix
  1. Add the three headers in nginx.
  2. Check the checkout still renders.
Server access & network

Secrets & keys

Critical2 items
IDFindingSeverityDetails
C-02GitLive payment key in git historyCritical

In fullThe live payment secret key is committed in the repository history

The production payment key appears in 14 commits. Anyone with read access to the repository, including two former contractors, can issue refunds and read customer payment records.

Affected · 2
  • config/payments.js (14 commits)
  • 2 former contractor accounts with read access
How to fix
  1. Roll the key in the payment dashboard.
  2. Move it to the secrets manager and load it from the environment.
  3. Purge the history with git filter-repo and force every clone to be re-cloned.
WhenToday
Effort3 hours
VerifyA secret scan of the full history returns no live keys.
Secrets & keys
M-02EnvEnvironment file readable by all accountsMedium

In fullThe environment file is readable by every local account

The file holding API keys has mode 644. Any process on the server can read every credential.

Affected · 1
  • /opt/shop/.env (mode 644)
How to fix
  1. chown to the service account.
  2. chmod 600.
  3. Restart the service.
Secrets & keys

Server updates & patching

High2 items
IDFindingSeverityDetails
H-02Kernel61 security updates pending, 412 days without a restartHigh

In full61 security updates are pending and the server has not restarted in 412 days

Unattended upgrades download packages but the kernel in memory is the one from the last boot. Published vulnerabilities for that kernel version include local privilege escalation.

Affected · 2
  • shop-01 kernel 5.15.0-91
  • 61 packages listed by apt list --upgradable
How to fix
  1. Take a snapshot.
  2. Apply updates in a maintenance window.
  3. Restart and confirm services return.
  4. Add a monthly restart window to the calendar.
WhenThis week
Effort1 hour
Expected GainCloses every pending kernel fix
Verifyuname -r matches the newest installed kernel.
Server updates & patching
M-04RuntimeNode 16 is past end of lifeMedium

The storefront runs on a Node release that no longer receives security fixes.

Affected · 1
  • storefront.service (node v16.20.2)
How to fix
  1. Build against the current LTS on staging.
  2. Run the smoke test.
  3. Switch the service.
Server updates & patching

Deployment & operations

High4 items
IDFindingSeverityDetails
H-03DeployProduction is edited by handHigh

In fullChanges are edited directly on the production server with no review and no rollback

Staff and coding agents write to the live working tree. A watcher commits whatever it finds every two minutes. There is no review, no test gate and no tested way back.

Affected · 2
  • /opt/shop (live working tree)
  • watcher.service
How to fix
Short term
  1. Make the working tree read-only for everyone except the deploy user.
  2. Require a pull request for main.
Structural
  1. Build once in CI.
  2. Promote the tested build by version.
WhenThis month
Effort2 days
Deployment & operations
M-03MonitoringNo outside uptime checkMedium

In fullNothing outside the server checks whether the shop is up

The only health check runs on the server it is checking. When the server is down, so is the alert.

Affected · 1
  • healthcheck.timer (local only)
How to fix
  1. Add an external uptime check on the home page and the checkout.
  2. Send alerts to two people.
Deployment & operations
L-02LogsLogs kept for three daysLow

In fullApplication logs are kept for three days

Three days is too short to investigate an incident found a week later.

Affected · 1
  • /etc/logrotate.d/shop
How to fix
  1. Keep 30 days.
  2. Ship a copy off the server.
Deployment & operations
I-01InventoryThree scheduled jobs run as rootInfo

Listed for completeness. None is reachable from outside.

Affected · 3
  • backup.timer
  • cert-renew.timer
  • cleanup.timer
Deployment & operations

Backups & recovery

High1 items
IDFindingSeverityDetails
H-04BackupBackups live on the same diskHigh

In fullDatabase backups are written to the same disk as the database

The nightly dump lands in /var/backups on the only volume. A disk failure, a ransomware event or a mistaken delete removes the database and every backup together. No restore has ever been tested.

Affected · 1
  • /var/backups/pg (same volume as /var/lib/postgresql)
How to fix
  1. Send the nightly dump to off-site object storage with a write-only key.
  2. Keep 30 days.
  3. Run a restore drill and record how long it takes.
WhenThis week
Effort4 hours
VerifyA restore into a scratch database returns yesterday's order count.
Backups & recovery

Done well

Pass2 items
IDFindingSeverityDetails
P-01TLSCertificates renew automaticallyPass

In fullCertificates renew automatically and are valid on every domain

All three domains present a valid certificate with more than 60 days left and automatic renewal is working.

Affected · 3
  • example-shop.test
  • www.example-shop.test
  • admin.example-shop.test
Done well
P-02AdminAdmin panel requires two-factor sign-inPass

In fullThe admin panel requires two-factor sign-in

Every admin account has two-factor authentication enforced.

Affected · 1
  • admin.example-shop.test
Done well
Nothing matches. Clear the search or the severity filter.
System overview

How it is set up today.

One server runs the shop, the database and the scheduled jobs. Two outside providers sit in front of it.

Where visitors and data go

White boxes are other providers. The coral area is the one server the company runs.

Where visitors and data go todayONE CLOUD SERVER · NO CONTAINERS, ALL SYSTEM SERVICESVisitorsUS and EURegistrar DNSexample-shop.testCDNstatic assets onlynginxweb front door, TLSStorefrontNode, port 8001Shop backendport 9000PostgreSQLcustomers, ordersOutside services the server talks topayments · email · AI provider · fulfilmentcached assetsSSH port 22 is open to thewhole internet (C-01)Only static assets sit behind the CDN. Shop visitors reach the server directly.

Swipe sideways to see the whole diagram.

Server at a glance

ItemTodayStatus
Operating systemUbuntu 22.04 LTSSupported
Kernel5.15.0-91, 412 days since restartRestart due
Node runtime16.20.2End of life
PostgreSQL14.9Supported
BackupsNightly, same diskAt risk
MonitoringLocal timer onlyGap

Scroll sideways to see all columns.

Where this should go

Green is the target state: controls that should be in place before the next release.

Recommended target: two servers, git-driven releasesAROUND PRODUCTION: IN PLACE BEFORE IT GOES LIVEPeople and agentsedit on branchesGit host, protected mainpull request + review + CIWork serverstaging, agents, queuesProduction servernginx, database, storefrontSSH only through VPNfirewall, port 22 closedCDN in frontcached assets, WAFExternal monitoringuptime, heartbeat, alertsOff-site backupsnightly, restore drillsCI-green buildpromote by versionContractors and agents never get a shell on production.Releases are built once and can be rolled back in minutes.

Swipe sideways to see the whole diagram.

Methodology

How we rated and what we looked at.

Read-only throughout. Nothing was changed.

How severity was decided

RatingMeans
CriticalReachable from the whole internet without credentials and leads to the server, money or customer data. Or an active sign of compromise.
HighOne realistic step away: an insider or contractor account, a leaked key, a crafted email or request. Or a credible data-loss or outage risk.
MediumA defence-in-depth gap, drift, or a missing guardrail.
LowHygiene.
InfoInventory or a verdict with no rating.
PassControls that are in place and working.

Automation itself is a business choice and is not rated. Only the missing guardrails around it are.

Evidence

Server
  • Read-only SSH with sudo
  • sshd, nginx and systemd configuration
  • Read-only database queries
Code
  • Every repository directory read
  • Secret scan of tree and full history
  • Dependency audit from lockfiles
Sites
  • Passive DNS, TLS and header checks
  • Connect tests on every site

Confidence

VerifiedConfirmed on the live server or traced end-to-end.
PlausibleThe code path is real but the live effect was not exercised.

Inputs

  • ssh audit@shop-01.example-shop.test (read-only, sudo)
  • git repository · full history
  • DNS, TLS, HTTP headers
  • dependency audit from package-lock.json