Skip to main content
Blog

Server

Logging and incident response: the basics for a small team

What to log, how long to keep it, central logs, alerts and what to do in the first hour of an incident. A practical guide to preserving evidence.

· 10 min read

Photo: The U.S. Army, Public domain · Wikimedia Commons

A small team has no separate security department: the developer, the server administrator and the incident responder are often the same people. So when something happens, most of the time goes into answering "what happened?". If there are no logs, they were kept only briefly, or the attacker deleted them, that question may never be answered.

In this article we look at what to log, how to store logs, when to send alerts and how to act in the first hour of an incident. The goal is not a large SOC, but a minimal routine a small team can actually follow.

What the problem is

The usual situation with logs looks like this:

  • web server and application logs exist, but nobody reads them;
  • important events (login, password change, role change) are not recorded at all;
  • logs are rotated away after a few days;
  • logs live only on the same server — if it is compromised, the attacker can edit or delete them;
  • conversely, passwords, tokens and personal data end up in the logs.

The last case is dangerous too: the log file itself becomes a source of data leakage.

Why it matters for the expertise

The OWASP Top 10 (2021) has a dedicated category: A09 "Security Logging and Monitoring Failures". OWASP ASVS also contains requirements for logging security events, keeping secrets out of logs and protecting logs. In the expertise this shows up from two sides:

  1. Information disclosure. Publicly reachable storage/logs/laravel.log, error_log or debug.log files and stack traces on error pages are all recorded as findings. More on this: debug mode and information disclosure.
  2. No monitoring. If password guessing and unauthorised access attempts are neither detected nor recorded, an attack goes unnoticed.

Principle: the log is the only witness that answers questions after an incident. It must be complete, protected and contain no secrets.

What to log

At the application level

Every entry should include at least: time (UTC or with an explicit time zone), user identifier, IP address, event type, outcome (success or failure) and a request identifier.

Events that should be logged:

  • successful and failed logins, logouts, lockouts;
  • changes of password, email or phone number, password resets;
  • changes of roles and permissions, a new administrator being added;
  • denied requests (403) — especially attempts to access other users' objects;
  • important business operations: payments, document signing, data exports;
  • repeated validation and CSRF errors.

What not to log

Passwords, tokens, session identifiers, full card numbers, one-time codes, full personal data. Logging entire request bodies often pulls such data into the log.

At the server level

  • web server access and error logs (nginx access.log, error.log);
  • PHP-FPM and application errors;
  • SSH logins (/var/log/auth.log or journalctl -u ssh);
  • control panel logins;
  • connections rejected by the firewall;
  • package installations and system changes.

Examples for Laravel and Node

In Laravel it is convenient to create a separate channel for security events (config/logging.php):

'security' => [
    'driver' => 'daily',
    'path' => storage_path('logs/security.log'),
    'level' => 'info',
    'days' => 90,
],

Listening to login events:

Event::listen(\Illuminate\Auth\Events\Failed::class, function ($e) {
    Log::channel('security')->warning('login.failed', [
        'login' => $e->credentials['email'] ?? null,
        'ip' => request()->ip(),
        'ua' => substr((string) request()->userAgent(), 0, 200),
    ]);
});

Note that only the login is written, not the whole credentials array — the password never reaches the log.

A Node.js (pino) example that automatically redacts sensitive fields:

const logger = require('pino')({
  redact: ['req.headers.authorization', 'req.headers.cookie', '*.password', '*.token'],
});
logger.info({ userId: user.id, ip: req.ip, event: 'login.success' });

Structured (JSON) logs make later searching and analysis much easier.

Check it yourself

Make sure logs are not reachable from outside (only on your own site):

for p in storage/logs/laravel.log error_log debug.log wp-content/debug.log logs/error.log; do
  printf "%s " "$p"; curl -s -o /dev/null -w "%{http_code}\n" "https://example.uz/$p"
done

Search the logs for secrets:

grep -riE "password|passwd|authorization: bearer|api_key|secret" /var/www/app/storage/logs | head

Check how long logs are kept: the settings in /etc/logrotate.d/ and journalctl --disk-usage. And a simple question: could you find out within five minutes who logged into the admin panel yesterday at 03:00?

Retention and protection

Retention depends on your organisation's requirements, but a practical approach for a small team is: at least several weeks for quick searching, and several months in an archive. Some sectors may have regulatory requirements — confirm them with your lawyer.

A logrotate example for nginx logs:

/var/log/nginx/*.log {
    daily
    rotate 90
    compress
    delaycompress
    missingok
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        [ -f /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid)
    endscript
}

Log files should be readable only by the users who need them (0640) and live outside the web root.

Centralising logs

If logs exist only on the server itself, whoever compromises it can erase their tracks. So logs should be shipped elsewhere in real time. Options for a small team:

  • forwarding to a separate log server with rsyslog:
# /etc/rsyslog.d/90-remote.conf
*.* @@logs.internal.example:514
  • lightweight tools such as Grafana Loki + Promtail or Vector;
  • your cloud provider's logging service.

Important: the main server should only be able to write to the log server, not delete from it. Synchronise server time with NTP (timedatectl, chrony) — otherwise events from different sources cannot be correlated.

Alerts

Alerting on everything is the same as alerting on nothing: the team quickly stops paying attention. Start with a few precise rules:

  • many failed logins from one IP or against one account in a short time;
  • a new administrator being added or a role being changed;
  • an admin panel login from a new country or an unknown IP;
  • a sharp increase in 5xx errors;
  • a new .php file appearing in the web root (especially inside uploads);
  • a failed backup run.

Alerts should go to one channel (a Telegram group, email), and everyone should know who is on duty. For SSH, fail2ban automatically blocks repeated attempts.

The first hour of an incident

You have found a sign of an incident: an unknown administrator, a strange file, unexpected requests in the logs. The most common mistake is to hastily shut the server down or delete files — the evidence is lost. A rough order:

  1. Record. Who saw what and when — write it down with timestamps. This becomes the basis of the report.
  2. Preserve evidence. Copy logs, suspicious files and configuration to another location and record their hashes:
tar -czf /root/ir/evidence_$(date +%F_%H%M).tar.gz /var/log/nginx /var/www/app/storage/logs
sha256sum /root/ir/evidence_*.tar.gz > /root/ir/evidence.sha256
ps auxf > /root/ir/processes.txt
ss -tunap > /root/ir/connections.txt
find /var/www -type f -mtime -7 -printf "%TY-%Tm-%Td %TH:%TM %p\n" | sort > /root/ir/recent_files.txt
  1. Contain, but do not destroy. Block the suspicious account, close the backdoor path at the web server level and, if needed, put the site into maintenance mode. Do not delete suspicious files — move them to quarantine.
  2. Rotate keys. Database password, API tokens and application secrets, administrator passwords — change them from a clean environment.
  3. Find the entry point. An outdated plugin, a vulnerable library, a leaked password? Until the entry point is closed, any cleanup is temporary.
  4. Notify. Management, and where necessary customers and the competent authorities. For government systems and serious incidents, notifying the national cybersecurity centre may be required.

You can see a real example in the article anatomy of a WordPress backdoor: evidence was preserved first, then malicious files were quarantined and core files were compared against the official checksums.

Short checklist

  • Logins, password changes, role changes and 403 events are logged
  • Logs contain no passwords, tokens or full personal data
  • Log files live outside the web root and are not reachable from outside
  • A retention period is defined and logrotate is configured
  • Logs are shipped elsewhere in real time
  • Server time is synchronised with NTP
  • There are a few precise alert rules and someone on duty
  • An incident response procedure is written down and evidence collection commands are ready

Summary

Good logging does not prevent an incident, but it lets you notice it quickly and respond correctly. In our process, the Claude AI agent reviews logging points in the application code, the log configuration and any secrets leaking into logs, and the expert sets up retention, centralisation and alert rules.

If you want to check whether your logging and incident response are ready for the expertise, request a security scan. We will help you close the gaps before the official check.