Setting Up Security Logging and Monitoring Correctly
AI generated
OWASP
0x00
Security · Logging · Monitoring · OWASP
Setting Up Security Logging and Monitoring Correctly
Detection before damage: what, how, and when to log

Discovering a breach weeks after it happened means the decisive advantage is already gone. This article explains which security-relevant events actually need to be logged, how sensitive data like passwords and tokens stays protected while doing so, how central log aggregation makes anomalies visible, and how alert thresholds are set without exhausting the team with constant false positives.

18 min. read Auth Events · Admin Log · SIEM Magento 2.4.8 · Monolog · ELK/Filebeat

1. Why security logging determines detection

Most successful attacks are not discovered within minutes, but only after days or weeks, and often by a third party rather than through the organization's own monitoring. The gap between compromise and detection is exactly the window in which attackers exfiltrate data, plant backdoors, or capture payment data. Security logging closes that gap by capturing the events needed both for later reconstruction and for immediate alerting. A regular application log built for debugging is not enough for this, because it answers a different question: why a request failed, not who tried to gain unauthorized access.

For Magento stores, the admin panel and the checkout process are also favorite attack targets, especially for Magecart-style skimmers that capture payment data directly in the checkout. Without consistent security monitoring, a compromised admin account or a tampered payment module often stays undetected for months. Regulatory requirements such as PCI DSS requirement 10 or the GDPR's 72-hour breach notification duty also assume that reliable logs exist in the first place, ones from which the scope and timing of an incident can actually be reconstructed.

2. Which events are actually security-relevant

Not every event belongs in the security log, otherwise the relevant signal drowns in noise. Core categories are: authentication (successful and failed logins, logouts, password resets, multi-factor events), authorization (denied access to protected resources, attempts to reach admin routes with the wrong role), and administrative actions (configuration changes, creating or deleting user accounts, changes to payment or shipping methods). Each category carries a different attack signal: clustered login failures suggest brute forcing, repeated permission denials suggest privilege escalation attempts.

It's also worth logging input validation failures, which often indicate automated scans or targeted injection attempts, as well as API rate limit violations and unusual file uploads. What matters for every event is the same set of context fields: who acted, what was attempted, when, from which source, and with what outcome. Missing any one of these five building blocks makes the log significantly less useful for incident analysis, because events can no longer be clearly tied back to a cause.


<?php
declare(strict_types=1);

namespace Mironsoft\SecuritySuite\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Psr\Log\LoggerInterface;

/**
 * Logs failed customer login attempts with structured context.
 * Never logs the submitted password, only metadata needed for detection.
 */
class LogFailedLoginObserver implements ObserverInterface
{
    /**
     * @param LoggerInterface $securityLogger Dedicated security channel logger
     */
    public function __construct(private readonly LoggerInterface $securityLogger)
    {
    }

    /**
     * Handles the customer_customer_authenticate_after event on failure.
     *
     * @param Observer $observer Event observer carrying request context
     * @return void
     */
    public function execute(Observer $observer): void
    {
        $email = (string) $observer->getEvent()->getData('email');
        $exception = $observer->getEvent()->getData('exception');

        if ($exception === null) {
            return;
        }

        // Structured, machine-parseable context, no credentials included
        $this->securityLogger->warning('auth.login.failed', [
            'event_type' => 'authentication_failure',
            'actor_email_hash' => hash('sha256', strtolower($email)),
            'source_ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
            'reason' => $exception->getMessage(),
            'timestamp' => (new \DateTimeImmutable())->format(DATE_ATOM),
        ]);
    }
}

3. What must never end up in a log

A log that contains plaintext passwords, full session tokens, API keys, or credit card numbers turns itself into an attack target: whoever gains access to the logs gets the credentials along with them. This pattern is catalogued as CWE-532 (Insertion of Sensitive Information into Log File) and is one of the most common mistakes in home-grown logging solutions. The rule is simple: anything that can be used for authentication, authorization, or payment must be masked, hashed, or omitted entirely, never stored in plain text.

In practice, a central redaction mechanism works best, one that doesn't need to be implemented manually at every single log call site but instead runs automatically before a log entry reaches its handler. A Monolog processor is the right place for this: it recursively walks context and extra data for known sensitive key names and replaces the values with a fixed mask. Correlation is preserved through a hashed or truncated identifier, while the original secret is never persisted at all.


<?php
declare(strict_types=1);

namespace Mironsoft\SecuritySuite\Logger\Processor;

/**
 * Removes sensitive values from log records before they reach any handler.
 * Runs as a Monolog processor, so redaction happens once, centrally, for
 * every channel instead of relying on every call site to sanitize manually.
 */
class SensitiveDataProcessor
{
    /** @var string[] Field names that must never appear in plain text */
    private const REDACTED_KEYS = [
        'password', 'passwd', 'pwd', 'token', 'access_token', 'refresh_token',
        'authorization', 'secret', 'api_key', 'credit_card', 'cvv',
    ];

    private const MASK = '***REDACTED***';

    /**
     * Processes a single log record and redacts sensitive keys recursively.
     *
     * @param array $record Monolog log record (message, context, extra, ...)
     * @return array Sanitized log record safe to persist or ship
     */
    public function __invoke(array $record): array
    {
        $record['context'] = $this->redact($record['context'] ?? []);
        $record['extra'] = $this->redact($record['extra'] ?? []);

        return $record;
    }

    /**
     * Walks an array recursively and masks values whose key looks sensitive.
     *
     * @param array $data Arbitrary context data attached to a log entry
     * @return array Data with sensitive values replaced by a fixed mask
     */
    private function redact(array $data): array
    {
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $data[$key] = $this->redact($value);
                continue;
            }

            $normalizedKey = strtolower((string) $key);
            foreach (self::REDACTED_KEYS as $sensitiveKey) {
                if (str_contains($normalizedKey, $sensitiveKey)) {
                    $data[$key] = self::MASK;
                    continue 2;
                }
            }
        }

        return $data;
    }
}

4. Structured logging: format and context

Free-text log lines like "Login failed for user X" are readable by humans but hard to process programmatically once millions of lines pile up per day. Structured logging in JSON format solves this, because every field can be queried, filtered, and correlated directly, without building fragile regex parsers. A consistent schema with fixed fields like timestamp, event_type, severity, actor, source_ip, and outcome makes log entries comparable across different systems, whether they come from the PHP backend, the web server, or a payment integration.

Just as important as the individual fields is a consistent correlation ID or trace ID, generated when a request comes in and passed through every downstream log entry, service, and asynchronous job. Only with that can a complete request, from a failed login through a subsequent access attempt on a protected resource to the eventual account lockout, be reconstructed as a coherent chain. Without a correlation ID, distributed systems remain a puzzle of isolated individual events during forensic analysis.


{
  "timestamp": "2026-07-12T14:32:07Z",
  "event_type": "authentication_failure",
  "severity": "warning",
  "actor": {
    "type": "customer",
    "id_hash": "9f3a1c2b7e4d5f6a8b9c0d1e2f3a4b5c",
    "email_hash": "c1a5e2f8b3d4a9e7f6b2c1d0e9f8a7b6"
  },
  "source": {
    "ip": "203.0.113.42",
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
  },
  "context": {
    "reason": "invalid_credentials",
    "attempt_count": 3,
    "account_locked": false
  },
  "trace_id": "b7e1f6a2-4c3d-4e5f-9a1b-2c3d4e5f6a7b",
  "application": "magento",
  "environment": "production"
}

5. Magento admin activity logging

Since Magento 2.4, the Magento_AdminActionsLog module ships a built-in log for admin activity, visible under System > Admin Actions Log. It captures logins, logouts, and basic configuration changes along with the admin user and timestamp. For many stores this is a solid baseline, but it logs generic framework events, not necessarily business-level actions such as changing a product price, creating a new admin account with the "Administrator" role, or disabling a payment method. Reaching that granular level requires custom plugins.

A plugin wrapping the controller's execute() method is the most reliable approach, because it works regardless of whether a module follows Magento's standard admin grid conventions or ships its own controllers. It's important to filter the logged request parameters before writing them, so no password fields or tokens end up there either. Combined with the admin user's ID, the executed action, and the source IP, this produces an audit trail that shows exactly what changed and when in the event of a compromised admin account.


<?php
declare(strict_types=1);

namespace Mironsoft\SecuritySuite\Plugin;

use Magento\Backend\App\Action;
use Magento\Backend\Model\Auth\Session;
use Psr\Log\LoggerInterface;

/**
 * Logs every executed backend controller action for audit purposes.
 * Wraps execute() instead of relying only on Magento's built-in admin log,
 * so custom controllers (e.g. Mironsoft modules) are covered too.
 */
class LogAdminActionPlugin
{
    /**
     * @param LoggerInterface $securityLogger Dedicated security channel logger
     * @param Session $authSession Backend auth session for the current admin user
     */
    public function __construct(
        private readonly LoggerInterface $securityLogger,
        private readonly Session $authSession
    ) {
    }

    /**
     * Logs the admin action before it is executed.
     *
     * @param Action $subject Backend controller being invoked
     * @param callable $proceed Original execute() call
     * @return mixed Result of the wrapped controller action
     */
    public function aroundExecute(Action $subject, callable $proceed)
    {
        $adminUser = $this->authSession->getUser();
        $request = $subject->getRequest();

        $this->securityLogger->info('admin.action.executed', [
            'event_type' => 'admin_action',
            'admin_user_id' => $adminUser?->getId(),
            'admin_username' => $adminUser?->getUsername(),
            'action' => $request->getFullActionName(),
            'params' => $this->filterParams($request->getParams()),
            'source_ip' => $request->getClientIp(),
        ]);

        return $proceed();
    }

    /**
     * Removes sensitive request parameters before they are logged.
     *
     * @param array $params Raw request parameters
     * @return array Parameters with sensitive keys removed
     */
    private function filterParams(array $params): array
    {
        unset($params['password'], $params['current_password'], $params['token']);

        return $params;
    }
}

6. Central log aggregation for anomaly detection

A single server log is nearly useless for anomaly detection, because attacks rarely stay confined to one source. Only once PHP application logs, web server access logs, database audit logs, and WAF events flow into one place do patterns become visible that span multiple systems, for example a failed login followed by a successful login from a different IP address minutes later. Central log aggregation with tools like Filebeat, Fluentd, or Logstash collects logs from all sources, normalizes them, and pushes them into a searchable backend such as Elasticsearch, Loki, or a SIEM solution.

For Magento setups, this means that besides the PHP logs under var/log/, Nginx or Varnish access logs, MySQL slow query logs, and, where applicable, the logs of an upstream web application firewall should also be included. A log shipper like Filebeat reads files incrementally, detects rotation automatically, and tags every entry with metadata like hostname and environment before forwarding it. An encrypted connection to the central backend is essential, so the transport layer itself doesn't become the weak point.


# filebeat.yml - forward Magento security-relevant logs to Logstash
filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /var/www/magento/var/log/security.log
      - /var/www/magento/var/log/exception.log
      - /var/www/magento/var/log/system.log
    fields:
      app: magento
      environment: production
    json.keys_under_root: true
    json.add_error_key: true
    multiline.pattern: '^\['
    multiline.negate: true
    multiline.match: after

output.logstash:
  hosts: ["logstash.internal:5044"]
  ssl.certificate_authorities: ["/etc/filebeat/ca.crt"]

processors:
  - drop_fields:
      fields: ["agent.ephemeral_id", "ecs.version"]
  - add_host_metadata: {}

7. Detecting anomalies: patterns and correlation

Static rules such as "more than five failed logins" catch obvious brute force attempts, but fall short against slow, distributed attacks where an attacker spreads only one or two attempts per account across many IP addresses. What's needed in addition is a statistical baseline: how many logins per hour are normal for this store, which countries traffic typically comes from, at what times admin users usually work. Deviations from that baseline, such as an admin login from a country never seen before, are often more telling than any fixed threshold.

Correlating several weak signals into one strong one is especially effective: an "impossible travel" rule detects when the same user logs in from geographically distant locations within minutes, which is physically impossible and suggests a stolen session token or a compromised password. SIEM systems like Elastic Security or Wazuh ship pre-built correlation rules for such patterns, which can be combined with external signals like threat intelligence feeds for known malicious IP addresses.

8. Alerting thresholds without alert fatigue

Overly sensitive alerts lead a security team to reflexively dismiss every notification after a short time without even checking it, including the real incidents. This phenomenon is called alert fatigue and is one of the most common reasons attacks go undetected despite monitoring being in place. The fix lies in tiered severity levels instead of a single alarm state: informational events land on a dashboard, moderate anomalies create a ticket, and only clearly critical patterns, such as a successful login after ten failed attempts from a new IP, trigger an immediate notification with escalation.

Time windows and aggregation are the second lever: instead of alerting on every single failed login, counts are tracked over a rolling window, for example five failed attempts within 15 minutes per account or IP, and duplicates within a period are collapsed into a single alert. Thresholds should also be tuned regularly based on real incidents and false positives, rather than set once and never touched again. An alert without a clearly defined, documented response is just noise anyway, and belongs either sharpened or removed.

9. Retention, access control, and compliance

How long security logs must be retained is a trade-off between forensic usefulness and data protection: PCI DSS requires at least one year of retention for payment-relevant systems, with three months immediately available for analysis, while the GDPR's data minimization principle requires that personal data not be stored any longer than necessary. In practice this often means a tiered strategy: complete, searchable logs kept short-term for active investigations, aggregated or anonymized data kept long-term for trend analysis.

Access to security logs themselves must be protected under the principle of least privilege, since whoever can alter or delete logs can erase the traces of an attack. Immutable or WORM storage (Write Once, Read Many) prevents subsequent modification, and separating access rights between the teams that generate logs and those that analyze them reduces the risk of an insider attack. Anyone aggregating logs centrally should therefore treat the aggregation layer itself as a critical system, with its own audit log for access to the logs.

Logging security-relevant events in a structured way is one half of the task, reliably keeping sensitive data out of those same logs is the other. The table below compares naive logging practice against the recommended, secure approach.

Data type / event Wrong approach Correct approach Risk if mishandled
Login password Plaintext in error/debug log Log only outcome and hashed identifier Full credential leak on log access
Session/API token Logging the full token Mask it or log only a prefix Session hijacking on log compromise
Payment data Card number in a debug log Never log it, only the transaction ID PCI DSS violation, fines, loss of trust
Failed logins Only a counter, no context Structured with IP, user agent, timestamp Attack patterns undetectable without context
Admin actions Config changes stay unlogged Log admin ID, action, and entity in structure No audit trail during incident response

Mironsoft

Security audits, logging architecture, and monitoring setup for Magento stores

Ready for security monitoring that actually catches attacks?

We build structured security logging, central log aggregation, and well-tuned alert thresholds for your Magento store, including admin activity logging and a redaction strategy that reliably keeps sensitive data out of every log.

Logging audit

Review existing logs for security gaps and sensitive data exposure

Aggregation setup

Build Filebeat/Fluentd pipelines and a central log backend

Alerting tuning

Define thresholds and reduce alert fatigue across the team

10. Summary

Security logging and monitoring address a single core problem: keeping the time between compromise and detection as short as possible. Authentication events, authorization violations, and administrative actions form the core of what must be logged, each with full context on actor, source, timing, and outcome. Passwords, tokens, and payment data must never appear in plaintext in a log; a central redaction mechanism such as a Monolog processor enforces that rule automatically, rather than relying on manual discipline at every log call site.

Only central log aggregation makes patterns visible across multiple systems, from impossible-travel logins to distributed brute force attempts. For this signal not to drown in noise, it needs tiered alert levels, time-window aggregation, and regularly retuned thresholds; otherwise alert fatigue causes real incidents to get lost in the flood of notifications. Magento's built-in Admin Actions Log is a solid starting point, but it doesn't replace a dedicated, fine-grained audit trail for critical business actions.

Security Logging and Monitoring - The Essentials at a Glance

What to log

Auth events, permission denials, and admin actions, captured in structure with actor, source, timing, and outcome.

What not to log

Passwords, full tokens, and payment data never in plaintext, masked centrally via a Monolog processor.

Central aggregation

Filebeat/Fluentd collect PHP, web server, and database logs for cross-system correlation.

Alerting without fatigue

Tiered severity levels, time-window aggregation, and thresholds retuned on a regular basis.

11. FAQ: Security Logging and Monitoring

1Which events must be logged in a Magento store?
Authentication events, denied access attempts, and administrative actions such as configuration changes or new admin accounts form the core of the security log.
2Why shouldn't I log passwords, don't I need them for debugging?
A log with plaintext passwords becomes an attack target itself. The login outcome with a hashed identifier is entirely sufficient for debugging; the password itself is never needed.
3What's the difference between application logging and security logging?
Application logging clarifies technical failure causes. Security logging clarifies who attempted what, whether authorized, and with what outcome, focused on accountability, not debugging.
4How does the Magento Admin Actions Log work?
Magento_AdminActionsLog has logged logins and basic config changes since 2.4. Granular business actions like price changes need custom plugins on top.
5What is central log aggregation and why isn't a single server log enough?
Collects logs from the app, web server, database, and WAF in one place. Attacks span multiple systems, so patterns only become visible through correlation.
6How do I avoid alert fatigue in the monitoring team?
Tiered severity levels, time-window aggregation against alert floods, and regularly retuning thresholds based on real incidents.
7How long must security logs be retained?
PCI DSS requires at least one year, three months immediately searchable. GDPR requires data minimization, so use a tiered retention strategy.
8What is a correlation ID / trace ID and what is it used for?
Generated on request arrival and passed through every log entry. Allows a request to be reconstructed across multiple systems as a coherent chain.
9Which tools are suitable for central log aggregation with Magento?
Filebeat/Fluentd for collection, Logstash for processing, Elasticsearch/Loki, or a SIEM solution like Wazuh as a searchable backend.
10Are security logs themselves an attack target?
Yes. Whoever can alter logs can erase attack traces. Least-privilege access control and immutable/WORM storage are essential.