Production Security And Server Hardening

Server Compromise Monitoring

Hardening lowers the odds of compromise; monitoring bounds its duration. The industry's ugly statistic has long been that intrusions go unnoticed for weeks or months — not because signals were absent, but because nobody was positioned to see them. Compromise monitoring is the discipline of knowing what your server normally does, so that what it abnormally does has somewhere to register.

Baseline, Then Anomaly

Every detection below is a diff against expected behavior, which is why this lesson leans on baselines built earlier in the track: expected listeners and services from the attack-surface lesson, expected file hashes from the integrity lesson, expected ports from the firewall lesson. Monitoring is those baselines, on a schedule, with an alert path.

The signals worth watching on a PHP server, roughly in order of value:

  • Authentication events: every SSH login and every sudo invocation, alerting on the unexpected — new source address, odd hour, a service account logging in interactively. On a small team, alerting on every successful SSH login costs a few messages a day and detects credential theft almost immediately.
  • New listeners and processes: a process appearing outside the baseline, a shell bound to a port, an outbound connection from a worker that should only talk to the database. ss diffs and process-tree snapshots catch the persistence stage of most intrusions.
  • Outbound traffic shape: compromised servers phone home, join booters, send spam, or mine. Unexpected destinations, resolution of odd domains, or a traffic-volume jump from a box with a known egress profile is a high-signal alarm precisely because egress is usually ignored.
  • Scheduled-work changes: new cron entries and systemd timers are beloved persistence mechanisms and change rarely enough that every change deserves a notification.
  • Resource anomalies: sustained CPU from an unfamiliar process is the crypto-miner signature; disk filling with someone else's data is the exfiltration-staging signature.

Logs Leave The Host

The first act of a competent intruder is cleaning the logs that describe their arrival. Ship auth logs, syslog, and audit trails off-host in near-real-time — to a log service or a separate hardened box — so that erasure on the compromised machine erases nothing that matters, and so the response playbook has a trustworthy timeline to reconstruct from. An unexplained gap in shipped logs is itself an alarm.

Linux auditd records the syscall-level detail — who executed what, what touched a watched file — that turns "something happened" into "this happened." A modest ruleset watching credential files, web roots, and executed commands in unusual paths provides the forensic depth that plain syslog lacks. Rootkit scanners exist and are cheap to run, with a caveat the integrity lesson already taught: tools running on a compromised host report what the compromise permits; off-host comparison is the trustworthy layer.

Alerts Someone Reads

Detection that pages nobody is archival, not monitoring. Route the high-signal, low-volume alarms — logins, new services, cron changes, egress anomalies — to a channel humans actually see, and keep that channel quiet enough that its messages retain meaning. Every alert should carry its evidence (the diff, the log lines) and imply its first response step, because the reader at 03:00 is the responder.

What To Check

Before moving on, make sure you can:

  • explain why baselines from earlier lessons are the raw material of detection
  • rank the server signals by value and name what each detects
  • justify off-host log shipping with the log-erasure threat
  • describe auditd's role beyond syslog
  • state what makes an alert actionable rather than archival

What You Should Be Able To Do

After this lesson, you should be able to instrument a PHP server so that credential theft, persistence, and exfiltration each cross at least one monitored tripwire, with evidence shipped beyond the intruder's reach and alarms a small team will actually read.

Practice

Practice: Design Server Monitoring
Show solution

Signals: every successful SSH login and sudo command notifies the team channel (a few per day, read at a glance); logins from unlisted addresses or at odd hours page. Hourly diffs of listeners, running services, cron entries, and timers against committed baselines — any diff notifies with content, new listeners page. The deploy-manifest integrity comparison from the integrity lesson runs off-host nightly. Egress: each server has a short expected-destination profile; netflow-level deviation (new sustained destinations, volume jumps) notifies daily, large spikes page.

auditd carries a small ruleset: watches on /etc/passwd, /etc/shadow, sshd_config, the web root, and execution from /tmp and upload paths.

Paging is reserved for signals implying an active intruder (unknown login, new listener, integrity diff without a deploy); everything else lands in the channel for morning triage. Each alert template embeds its evidence and first playbook step, so detection hands off to response without a search.

Practice: Diagnose A Missed Intrusion

Task

Identify each failure and what minimum change would have caught the intrusion in the first day.

Show solution
  • The entry event was invisible: local-only auth logs let the intruder erase their arrival. Off-host shipping would have preserved the login trail, and a shipped-log gap would itself have alarmed.
  • The persistence event was unmonitored: a new cron entry is one of the rarest, highest-signal changes on a server, and nothing diffed it. A daily cron/timer diff against baseline flags this within 24 hours.
  • The execution was visible but unattended: six weeks of elevated CPU sat in graphs nobody was obligated to read. A sustained-CPU-from-unknown-process alert — the classic miner signature — pages instead of decorating a dashboard.
  • The alert channel was dead: 4,000 unread messages mean the channel's real alert rate had already exceeded human capacity long before the incident. An inbox nobody reads is monitoring theater; the fix is fewer, higher-signal alerts routed where the team lives, plus a rule that alert-channel backlog is itself a tracked defect.

Minimum single change with day-one detection: the cron diff — the persistence mechanism was the loudest tripwire available. The complete fix is all four, because they cover entry, persistence, execution, and the human loop respectively.

Practice: Define Monitoring Review Checks
Show solution
  • Log-shipping freshness per host per stream, paging on silence — the monitor most worth having, since every other signal rides on it.
  • Baseline-diff jobs (services, listeners, cron, timers, integrity) each expose a last-ran timestamp that is itself monitored; a dead diff job looks identical to a quiet one otherwise.
  • A monthly planted-signal drill run by automation: create a benign cron entry, an odd listener, and a test login from an unlisted address, and assert each produced its alert within the promised window. Detection systems are tested by detection, not by inspection.

Human, monthly:

  • Alert-channel hygiene: count of alerts, fraction acknowledged, anything unread older than a day. Rising volume with falling acknowledgment predicts the dead-inbox failure while it is still fixable.
  • Review one week of successful-login notifications against the team's memory: every login should have an owner who recognizes it.

Human, quarterly:

  • Tabletop one signal end-to-end — "integrity diff at 03:00, no deploy record" — walking who is paged, what they check first, and where the playbook lives, so the seam between this lesson and compromise response stays exercised.