A PHP dashboard that turns raw WAF logs into actionable intelligence: real-time risk scoring, geographic enrichment, and automated IP banning via firewalld — without any external infrastructure.

Every web application that is exposed to the public internet receives, within minutes of going live, a constant stream of automated requests: vulnerability scanners probing for known exploits, bots harvesting content, brute-force attempts against login forms, and reconnaissance requests mapping the site's structure. The standard response to this reality is to deploy a Web Application FirewallA WAF (Web Application Firewall) is a security layer that sits between the internet and a web application, inspecting HTTP traffic and blocking malicious requests based on configurable rules. — a WAF — which filters traffic according to predefined rules. But a WAF alone generates data, not understanding. The logs accumulate, the storage grows, and in most cases nobody looks at them until something has already gone wrong.

This is the problem I set out to solve. SecureLog is a WAF Dashboard I designed and built to transform raw HTTP log data into actionable intelligence: a system that not only displays what is happening on your web infrastructure, but analyses it, scores it, and — when the evidence warrants it — acts on it autonomously by banning malicious IP addresses at the firewall level.

In this article, I describe the architecture, the reasoning behind the design choices, and how each component works together to create a closed-loop security monitoring system.

The problem: logs without context

A typical LAMP/LEMPLAMP stands for Linux, Apache, MySQL, PHP. LEMP replaces Apache with Nginx. Both are standard server stacks for hosting web applications. web server generates access logs in the Combined Log FormatA standard log format used by Apache and Nginx. Each line records the client IP, timestamp, HTTP method, URI, status code, response size, referrer and user agent string.: one line per request, containing the client IP, timestamp, HTTP method, requested URI, status code, referrer and user agent. On a moderately busy site, this means tens of thousands of lines per day.

The raw data is there, but extracting meaning from it is a different challenge entirely. Which of those 30,000 daily requests are legitimate visitors? Which are search engine crawlers? Which are scanning for /wp-login.php on a site that does not run WordPress? Which are attempting SQL injectionAn attack technique where malicious SQL statements are inserted into input fields or URLs, attempting to manipulate or extract data from the application's database. through query parameters? And crucially — which IP addresses have been doing this persistently enough to warrant blocking at the firewall level?

Answering these questions manually is not sustainable. I needed an automated pipeline that could ingest logs, enrich them with threat analysis and geolocation data, present the results through an intuitive interface, and provide both manual and automated response capabilities.

Architecture overview

SecureLog is built on a PHP backend with a JavaScript frontend, designed to run alongside the web server it monitors. The architecture follows a clear separation of concerns across six components, each implemented as a distinct file with a specific responsibility.

The frontend (index.php) is a single-page application with a sidebar navigation structure, built with vanilla JavaScript and Chart.jsAn open-source JavaScript library for creating responsive, animated charts. SecureLog uses it for traffic trend lines and HTTP status code distribution visualisations.. It communicates asynchronously with the backend through a unified API gateway (api.php) that routes all AJAXAsynchronous JavaScript and XML — a technique for making HTTP requests from the browser without reloading the page, enabling real-time data updates in the dashboard. requests to the appropriate handler function. Authentication and session management are handled by a dedicated security module (auth.php and login.php), which enforces HTTPS, secure cookie parameters, and CSRFCross-Site Request Forgery: an attack that tricks a user's browser into making unwanted requests. SecureLog prevents this by generating and validating a unique token for each session. token validation.

On the backend processing side, a CLI log processor (LogProcessor.php) runs as a scheduled task, reading raw access logs incrementally and feeding each line through the risk analysis engine (RiskAnalyzer.php). Finally, a firewall synchronisation script (SynchronizeFirewall.sh) bridges the gap between the application's decisions and the operating system's firewalldThe default firewall management tool on RHEL/CentOS/AlmaLinux systems. It supports ipsets — named collections of IP addresses that can be used in firewall rules for efficient bulk blocking. configuration, applying bans and unbans in real time.

The risk analysis engine

At the heart of SecureLog is the RiskAnalyzer class — the component that transforms a raw log line into a quantified threat assessment. The design philosophy is straightforward: every request receives a numeric risk score, calculated by evaluating multiple independent signals and summing their weighted contributions.

The analysis proceeds through six sequential checks:

Aggressive scanner detection. The user agent string is matched against a configurable library of known malicious scanner signatures — tools like Nikto, sqlmap, DirBuster, and similar reconnaissance utilities. A positive match contributes a high score component, as the mere presence of these tools indicates deliberate probing.

Attack pattern recognition. The full request URI is scanned for signatures of common attack vectors: SQL injection fragments (UNION SELECT, OR 1=1), path traversalAn attack that uses sequences like ../../ in URLs to escape the web root directory and access arbitrary files on the server, such as /etc/passwd. sequences, XSSCross-Site Scripting: an attack where malicious JavaScript is injected into web pages viewed by other users, typically through URL parameters or form inputs. payloads, and command injection attempts. The signature library is loaded from a centralised configuration file and compiled into optimised regular expressionsPattern-matching expressions used to identify strings that match a specific structure. RiskAnalyzer compiles all signatures for each category into a single regex for performance, using preg_quote() for safe escaping. at startup.

Sensitive file probing. Requests targeting files that should never be accessed externally — configuration files, backup archives, version control directories, database dumps — are flagged with a dedicated score. This catches the reconnaissance phase that typically precedes a targeted attack.

User agent anomaly analysis. Missing or suspiciously short user agent strings receive a score penalty. Legitimate browsers always send a substantial user agent; its absence is a strong indicator of automated tooling. Known passive bots — search engine crawlers, monitoring services — are identified separately and scored at a lower level.

URI length analysis. Requests with URIs exceeding 200 characters receive an additional score component. Abnormally long URIs are characteristic of injection attacks, where the payload is embedded in the query string.

HTTP status code evaluation. Repeated 404 responses from the same IP suggest systematic probing of non-existent paths — a classic enumeration technique.

The six individual scores are summed into a composite risk score, which is then classified into five severity levels: CLEAN, LOW, MEDIUM, HIGH, and CRITICAL. The thresholds for each level are defined in the centralised configuration, making the system tuneable without code changes.

The log processing pipeline

The LogProcessor.php script is the operational backbone of the system. It runs as a CLICommand Line Interface — the script is designed to be executed from the server's terminal or by a cron scheduler, not through a web browser. It explicitly refuses HTTP execution for security. process, typically triggered by cronA Unix/Linux time-based job scheduler. SecureLog's log processor is typically scheduled to run every few minutes, ensuring near-real-time analysis without constant resource consumption. at regular intervals, and performs three distinct phases in sequence.

Phase 1: Incremental log import. The processor reads the access log files for each monitored site, starting from the last known offset position. This is a critical design choice — the system never re-processes data it has already seen, which keeps execution times predictable regardless of log file size. Each log line is parsed using a hybrid approach: the script first attempts a standard regex parser for the Combined Log Format, and falls back to a tab-delimited parser when the format deviates. The parsed data is normalised: IP addresses, user agents, and referrers are resolved to internal IDs through an in-memory cacheA PHP associative array that stores previously resolved database lookups (sites, IPs, user agents, referrers) for the duration of the batch. This eliminates redundant SELECT queries when the same IP or user agent appears thousands of times in a single log file. backed by database lookups, minimising redundant queries. Every line is then passed through the RiskAnalyzer, and both the access record and its risk assessment are inserted into the database using batch operationsInstead of executing one INSERT per log line, the processor accumulates records and executes them in configurable batches. This dramatically reduces the number of database round-trips and improves throughput on large log files. for efficiency.

Phase 2: Geolocation resolution. After the import phase completes, the processor identifies all IP addresses that lack geographic data and resolves them through concurrent API calls to a geolocation service. The results — country, city, ISP, and autonomous system numberAn ASN (Autonomous System Number) identifies the network operator responsible for a block of IP addresses. Knowing the ASN helps distinguish traffic from legitimate hosting providers, cloud services, and known malicious networks. — are stored alongside the IP record. This enrichment is essential for the dashboard's geographic analysis capabilities and for identifying patterns such as concentrated attack traffic from specific regions.

Phase 3: Automated ban processing. The final phase is where analysis becomes action. The BanProcessor class queries the risk event database for IP addresses whose cumulative threat activity exceeds configurable thresholds. The thresholds are differentiated by severity: a single CRITICAL event may be sufficient to trigger a ban, while LOW-severity events require a higher count within a defined lookback windowA configurable time window (default: 24 hours) within which risk events are counted. Events older than the lookback window are excluded from the threshold calculation, preventing stale data from triggering bans on IPs whose behaviour has changed. — typically 24 hours. When an IP crosses the threshold, the processor updates its status to BANNED and sets the firewall synchronisation flag to PENDING, signalling that the change has not yet been applied at the operating system level.

The entire execution is protected by a lock file mechanismA mutual exclusion technique: the script creates a temporary file at startup and removes it on completion. If the lock file already exists, the script aborts, preventing two instances from processing the same data simultaneously. that prevents concurrent instances from overlapping — an essential safeguard when processing is scheduled at frequent intervals.

Firewall synchronisation

The bridge between SecureLog's decisions and the actual network-level enforcement is SynchronizeFirewall.sh — a Bash script that reads the database state and applies changes to the server's firewalld configuration through ipsetsNamed collections of IP addresses managed by firewalld. SecureLog maintains two ipsets: bad_ips for IPv4 addresses and bad_ipv6s for IPv6 addresses. Membership in these sets triggers firewall rules that block all traffic from the listed IPs..

The synchronisation operates in four phases, handling IPv4 and IPv6 separately to accommodate the different ipset families required by firewalld. For each protocol version, the script processes two operations: addition (applying new bans) and removal (lifting bans for IPs whose status has changed to ALLOWED or WHITELISTED). In every case, the script queries exclusively for records where firewall_status = 'PENDING', processes only those changes, and updates the status to APPLIED upon successful execution.

This design — separating the decision (what to ban) from the enforcement (how to ban) — provides several advantages. The PHP application never needs root privileges; it only writes to the database. The Bash script, which does require elevated permissions to modify firewalld, operates on a minimal, well-defined interface: a single database column that acts as a message queue. A safety mechanism ensures that a predefined "safe IP" — typically the administrator's own address — is never added to a block list, regardless of what the risk analysis produces.

After processing all pending changes, the script triggers a firewall-cmd --reload only if at least one modification was made, and outputs a summary report of all operations performed.

The dashboard interface

The frontend is a single-page application structured around five functional tabs, accessible through a dark sidebar navigation. The entire data flow is asynchronous: every view populates itself through API calls to api.php, and the interface supports deep linking and browser history navigation through a custom URL routing systemThe dashboard uses the HTML5 History API (pushState/popState) to maintain bookmarkable URLs for each tab and filter state. Navigating to /dashboard/?tab=logs&ip=1.2.3.4 restores the exact view..

Overview. The landing tab presents eight key performance indicators in a card grid: total requests, critical events in the last seven days, banned IPs, unique IPs, unique countries, useful traffic (seven-day and total), and total threats. Below the KPIs, three visualisation panels show the traffic trend over the past week as a line chart, the distribution of HTTP status codes as a doughnut chart, and a ranked list of the ten most visited pages. A fourth panel — the risk breakdown table — displays the count of events for each threat category (aggressive scanners, attack patterns, sensitive file probing), providing a direct view into the nature of the threats being detected.

Traffic & Logs. A paginated, filterable table showing individual access log records with their associated risk scores. Each row can be expanded to view full details, and IP addresses are clickable: selecting one filters the entire view to show all requests from that address. From this tab, individual IPs can be banned or unbanned with a single action, and bulk operations allow banning or unbanning all visible IPs on the current page or across the entire filtered result set.

Ban Management. A dedicated view for monitoring and managing the complete IP inventory. Each entry shows the IP address, its geographic origin, the average risk score calculated from the risk_analysis_events table, and the current status (BANNED, ALLOWED, or WHITELISTED). Filters allow isolating IPs by status, country, or risk level. From any entry, the administrator can navigate directly to the full log history for that IP.

Blacklist. A manual ban interface for adding IP addresses to the block list proactively — before any automated analysis detects them. Useful for integrating threat intelligence from external sources or blocking known bad actors immediately.

Whitelist. The inverse: a protection mechanism that ensures specific IP addresses — monitoring services, partner systems, the administrator's own addresses — are never subject to automated banning, regardless of their traffic patterns.

The entire interface supports multi-site filtering: a dropdown selector at the top of the page allows switching between aggregated data for all monitored sites and filtered views for individual domains. This makes SecureLog suitable for environments hosting multiple web properties on the same infrastructure.

Security design

A security monitoring tool that is itself insecure would be worse than useless — it would be a liability. SecureLog's authentication layer implements several defensive measures that reflect this awareness.

Session cookies are configured with secure, httponly, and SameSite=Lax flags. The httponlyA cookie attribute that prevents client-side JavaScript from reading the cookie value via document.cookie. This is a critical defence against session hijacking through XSS vulnerabilities. attribute prevents JavaScript from reading the session cookie, mitigating session hijacking via XSS. The SameSite attribute provides baseline protection against CSRF. All traffic is forced to HTTPS through a server-side redirect, and the login form validates a CSRF token on every submission. Failed authentication attempts are logged with the client IP for audit purposes, and a deliberate delay is introduced after failed attempts to slow down brute-force attacks.

On the API side, every request is validated for authentication before any data operation is performed. The API gateway returns structured JSONJavaScript Object Notation — a lightweight data interchange format. All communication between the dashboard frontend and the api.php backend uses JSON, both for request bodies (via POST) and for responses. responses with appropriate HTTP status codes, and database errors in production are logged server-side without exposing internal details to the client.

The CLI components enforce strict execution context: LogProcessor.php refuses to run if accessed via HTTP, and RiskAnalyzer.php blocks direct file access through the browser. These are simple but essential guards against accidental exposure.

The data model

SecureLog's database design follows a normalised structure centred on a few core tables. The ip_addresses table stores unique IPs with their geographic metadata. The ip_management table tracks the security status of each IP (BANNED, ALLOWED, WHITELISTED) alongside its firewall_status flag (PENDING or APPLIED) — the crucial column that drives the synchronisation loop. Access logs are stored with foreign keys to normalised lookup tables for sites, IP addresses, referrers, and user agents, keeping the main log table compact and query-efficient. Risk analysis events are recorded in a separate table linked to the access log, enabling aggregate queries by severity, time range, and IP without scanning the full log.

The separation between status (the desired state) and firewall_status (the applied state) is the architectural keystone of the entire system. All write operations in the PHP application — whether triggered by the automated ban processor, by manual actions through the dashboard, or by whitelist/blacklist management — set firewall_status to PENDING. The Bash synchronisation script then consumes these pending records and applies them to firewalld. This pattern is essentially a lightweight event-driven architectureA design pattern where state changes are recorded as events in a shared store, and downstream consumers process them independently. In SecureLog, the database column acts as the event queue, the PHP application acts as the producer, and the Bash script acts as the consumer., implemented without the overhead of a dedicated message queue.

Why I built this

In my work managing web infrastructure, I found myself repeatedly in the same situation: the WAF was doing its job filtering traffic, the logs were accumulating, but the gap between raw data and informed decision-making remained wide. Commercial SIEMSecurity Information and Event Management — enterprise platforms (such as Splunk, IBM QRadar, or Microsoft Sentinel) that aggregate security data from multiple sources, correlate events, and provide alerting. Effective but typically expensive and complex to deploy. solutions exist, but they are typically designed for enterprise-scale environments and carry licensing costs that are disproportionate for small to mid-size deployments. Open-source alternatives like the ELK stackElasticsearch, Logstash, and Kibana — an open-source log aggregation and visualisation platform. Powerful and flexible, but requires dedicated infrastructure (typically 8+ GB RAM for Elasticsearch alone) and ongoing maintenance. are powerful but require significant infrastructure and operational expertise to maintain.

I wanted something purpose-built: a tool that could be deployed alongside an existing web server, that required no additional infrastructure beyond a MySQL database, and that could close the loop from log analysis to firewall enforcement without manual intervention. SecureLog is the result — not a generic log aggregator, but a specialised system designed for a single, well-defined mission: understand the threat landscape of your web applications and respond to it automatically.

The code is structured to be maintainable by a single developer or a small team. Every file includes extensive inline documentation — not just comments explaining what the code does, but why specific choices were made, including dated notes on bug fixes and design corrections. This reflects a principle I apply in all my work: code that cannot be understood six months later by someone other than its author is a technical debt, not an asset.

Conclusions

SecureLog demonstrates that effective web security monitoring does not require a complex, multi-layered enterprise stack. A well-designed PHP application, a Bash script with the right privileges, a database acting as the coordination layer, and a clear separation between analysis, decision, and enforcement can produce a system that is simultaneously transparent, responsive, and maintainable.

The key architectural insight is the closed loop: logs enter the system, are analysed and scored, the scores drive automated decisions, the decisions are synchronised to the firewall, and the results are visible in real time through the dashboard. At every stage, the administrator retains full control — automated bans can be overridden, whitelists protect trusted addresses, and the complete history of every decision is auditable.

If you are managing web infrastructure and facing similar challenges — or if you are interested in discussing the technical details of the implementation — feel free to get in touch.