August 8, 2026
DESIGN OF A CENTRALIZED MONITORING DASHBOARD FOR WAF AND RATE LIMITING IN CYBER SECURITY SYSTEMS
Introduction
By BRILLIANT MUHAMMAD ILHAM
8 min read
Introduction
Web applications are constantly exposed to different types of attacks, including SQL Injection, Cross-Site Scripting (XSS), and HTTP Flood. In this project, I wanted to understand how these attacks could be prevented and, at the same time, how security events could be monitored from a single interface.
To explore this, I built a small virtual security lab using Oracle VirtualBox, DVWA, NGINX, Open-appsec WAF, NGINX Rate Limiting, Python, Pandas, and Streamlit.
The main idea was to place NGINX in front of a vulnerable web application and use it as the central security layer.
The final architecture was:
Client
│
▼
┌──────────────────┐
│ NGINX │
│ Reverse Proxy │
└────────┬─────────┘
│
┌────────┴─────────┐
│ │
Rate Limiting Open-appsec
│ │
└────────┬─────────┘
│
▼
DVWA
│
▼
Security Logs
│
▼
Python Log Parser
│
▼
Streamlit Dashboard Client
│
▼
┌──────────────────┐
│ NGINX │
│ Reverse Proxy │
└────────┬─────────┘
│
┌────────┴─────────┐
│ │
Rate Limiting Open-appsec
│ │
└────────┬─────────┘
│
▼
DVWA
│
▼
Security Logs
│
▼
Python Log Parser
│
▼
Streamlit DashboardThe project focused on three main attack scenarios:
- SQL Injection
- Cross-Site Scripting (XSS)
- HTTP Flood
The system was designed so that incoming requests were inspected and controlled before being forwarded to the vulnerable application. This follows the architecture described in the final project, where NGINX acts as the Reverse Proxy integrated with Open-appsec and Rate Limiting.
1. Setting Up DVWA
I started by preparing Damn Vulnerable Web Application (DVWA) as the target application.
DVWA is intentionally designed with web vulnerabilities, making it useful as a controlled environment for testing web security mechanisms. In this project, it was mainly used as the target for SQL Injection and XSS testing.
Installing Apache
First, I updated the Ubuntu package repository and installed Apache:
sudo apt update
sudo apt install apache2 -ysudo apt update
sudo apt install apache2 -yApache was used to host the DVWA application.
Downloading DVWA
I then cloned the DVWA repository into the web server directory:
cd /var/www/html
sudo git clone https://github.com/digininja/DVWA.git dvwacd /var/www/html
sudo git clone https://github.com/digininja/DVWA.git dvwaFor the initial lab setup, I configured the required permissions:
sudo chmod -R 777 dvwasudo chmod -R 777 dvwaConfiguring DVWA
The default configuration file was copied to the active configuration file:
cd /var/www/html/dvwa/config
sudo cp config.inc.php.dist config.inc.phpcd /var/www/html/dvwa/config
sudo cp config.inc.php.dist config.inc.phpThe database configuration was then adjusted to match the MySQL database and credentials used by the application.
Installing PHP and MySQL
DVWA requires PHP and MySQL to operate. I installed the required PHP packages and MySQL components, then created the DVWA database and user.
After completing the configuration, I accessed DVWA through the browser and verified that the application was working correctly.
At this stage, the vulnerable application was ready. The next step was to place a security layer in front of it.
2. Installing NGINX
The next component was NGINX.
NGINX would become the main entry point for HTTP traffic and would later handle two important functions:
- Reverse Proxy
- Rate Limiting
I installed NGINX using:
sudo apt install nginx -ysudo apt install nginx -yThen I verified that the service was running:
sudo systemctl status nginxsudo systemctl status nginxThe expected result was:
Active: active (running)Active: active (running)I also accessed the default NGINX page from a browser to make sure the installation was working before modifying the configuration.
3. Configuring NGINX as a Reverse Proxy
With NGINX working, I configured it as a Reverse Proxy.
Instead of allowing the client to access DVWA directly, all requests would first go through NGINX.
The traffic flow became:
Client
│
▼
NGINX
│
▼
DVWAClient
│
▼
NGINX
│
▼
DVWAThe important part of the configuration was the proxy_pass directive.
In the final configuration, NGINX listened on port 8080 and forwarded requests to the internal DVWA server:
server {
listen 8080;
server_name 192.168.1.18;
location /DVWA/ {
proxy_pass http://192.168.56.101/DVWA/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
}
}
server {
listen 8080;
server_name 192.168.1.18;
location /DVWA/ {
proxy_pass http://192.168.56.101/DVWA/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
}
}
The forwarding headers were included so that information about the original client request could be preserved when the request was forwarded to the backend.
The final project configuration confirms that NGINX forwarded traffic to the DVWA server through proxy_pass.
After modifying the configuration, I checked the syntax:
sudo nginx -tsudo nginx -tIf the configuration was valid:
syntax is ok
test is successfulsyntax is ok
test is successfulI then restarted NGINX:
sudo systemctl restart nginxsudo systemctl restart nginx
4. Configuring NGINX Rate Limiting
The next layer was Rate Limiting.
The purpose of Rate Limiting was different from the WAF.
While the WAF focused on malicious application-layer requests, Rate Limiting was used to control excessive HTTP requests, particularly during the HTTP Flood scenario.
The final NGINX configuration used:
limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
server {
listen 8080;
server_name 192.168.1.18;
location /DVWA/ {
proxy_pass http://192.168.56.101/DVWA/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
limit_req zone=one burst=20 nodelay;
limit_req_status 429;
}
}limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
server {
listen 8080;
server_name 192.168.1.18;
location /DVWA/ {
proxy_pass http://192.168.56.101/DVWA/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
limit_req zone=one burst=20 nodelay;
limit_req_status 429;
}
}The important directives were:
rate=5r/s— defines the request rate.burst=20— allows a temporary burst of requests.nodelay— controls how burst requests are handled.limit_req_status 429— returns HTTP 429 when requests are rejected by Rate Limiting.
This configuration was later tested using HTTP Flood traffic generated with HTTPerf.
5. Solving an NGINX Compatibility Issue
The WAF installation introduced an unexpected problem.
The initial NGINX version installed on the system was 1.18.0, but the Open-appsec installer reported that this version was unsupported.
I verified the installed version using:
nginx -vnginx -vThe result was:
nginx version: nginx/1.18.0nginx version: nginx/1.18.0To solve this problem, I updated the NGINX installation to a supported version.
The environment was eventually upgraded to:
nginx/1.26.2nginx/1.26.2This step was important because the WAF installation depended on NGINX compatibility.
The final project documentation records the upgrade from NGINX 1.18.0 to 1.26.2 before installing Open-appsec.
This was also one of the useful lessons from the project: implementing security tools is not always about following installation commands. Compatibility between components can become a problem that needs to be diagnosed first.
6. Installing Open-appsec WAF
Once NGINX was running on the supported version, I installed Open-appsec WAF.
The installer was executed in Prevent mode:
./open-appsec-install --auto --prevent./open-appsec-install --auto --preventAfter installation, I checked the status of the Open-appsec components:
open-appsec-ctl --statusopen-appsec-ctl --statusThe Nano Agent, Orchestration Service, and HTTP Transaction Handler were running successfully.
The WAF was now positioned in the HTTP request path and could inspect incoming requests before they reached DVWA.
The resulting architecture became:
Client
│
▼
NGINX
│
├── Rate Limiting
│
└── Open-appsec WAF
│
▼
DVWAClient
│
▼
NGINX
│
├── Rate Limiting
│
└── Open-appsec WAF
│
▼
DVWAThe project documentation confirms the Open-appsec installation using --auto --prevent and the verification of its running components.
7. Testing the WAF
After configuring the WAF, I moved to the first security tests.
The goal was to determine whether malicious application-layer requests could be blocked before reaching DVWA.
The two main scenarios were:
- SQL Injection
- Cross-Site Scripting
SQL Injection
I sent a SQL Injection payload toward the DVWA application.
The request was inspected by Open-appsec and blocked.
The response was:
HTTP 403 ForbiddenHTTP 403 ForbiddenThe event was also recorded in the Open-appsec security log.
This demonstrated the complete flow:
SQL Injection
↓
Open-appsec Inspection
↓
Attack Detected
↓
Request Blocked
↓
Security Log GeneratedSQL Injection
↓
Open-appsec Inspection
↓
Attack Detected
↓
Request Blocked
↓
Security Log Generated
Cross-Site Scripting
I then performed an XSS test using the same security path.
Open-appsec detected the malicious request and blocked it with:
HTTP 403 ForbiddenHTTP 403 ForbiddenThe event was recorded in the WAF log and later became visible in the monitoring dashboard.
These tests demonstrated that the WAF was not only installed but was actively participating in the request-processing path.
8. Testing Rate Limiting with HTTP Flood
The next test focused on a different type of traffic: HTTP Flood.
For this scenario, I used HTTPerf to generate a large volume of HTTP requests.
The purpose was not to simulate a sophisticated distributed attack, but to evaluate whether NGINX could limit excessive request rates in the controlled virtual lab.
The test plan included traffic volumes of up to 150,000 connections over a fixed test duration.
When the request rate exceeded the configured limit, NGINX returned:
HTTP 429 Too Many RequestsHTTP 429 Too Many RequestsThe resulting traffic flow was:
HTTP Flood
↓
NGINX Rate Limiting
↓
Request Threshold Exceeded
↓
HTTP 429
↓
NGINX Log
↓
DashboardHTTP Flood
↓
NGINX Rate Limiting
↓
Request Threshold Exceeded
↓
HTTP 429
↓
NGINX Log
↓
Dashboard
9. Building the Log Processing Pipeline
After implementing the security mechanisms, I needed a way to monitor the events they generated.
The system used two main log sources:
Open-appsec
│
└── WAF Security Log
NGINX
│
└── Error Log / Rate Limiting LogOpen-appsec
│
└── WAF Security Log
NGINX
│
└── Error Log / Rate Limiting LogInstead of manually reading these files, I developed a Python-based log processing pipeline.
The process was:
Raw Logs
↓
Log Parsing
↓
Data Extraction
↓
Normalization
↓
Pandas DataFrame
↓
Visualization
↓
Streamlit DashboardRaw Logs
↓
Log Parsing
↓
Data Extraction
↓
Normalization
↓
Pandas DataFrame
↓
Visualization
↓
Streamlit DashboardThe dashboard reads the Open-appsec log and NGINX error log dynamically. The final project used:
/var/log/nano_agent/cp-nano-http-transaction-handler.log1/var/log/nano_agent/cp-nano-http-transaction-handler.log1and:
/var/log/nginx/error.log/var/log/nginx/error.logas the main log sources.
For Open-appsec, the parser reads each JSON log entry and extracts fields such as:
- Timestamp
- Source IP
- Attack type
- Severity
- HTTP method
- URI
- Security action
- Threat level
- Matched parameter
The parser then categorizes relevant events into SQL Injection and XSS.
10. Building the Streamlit Dashboard
The final part of the project was building a centralized monitoring dashboard.
I used:
- Python for the processing logic
- Pandas for structured data processing
- Streamlit for the web interface
- Plotly for interactive visualization
The dashboard converts the raw logs into structured information that can be viewed through a single interface.
The dashboard provides:
- Total security events
- Attack categories
- Severity
- Source IP
- Event timestamps
- Rate Limiting events
- Detailed logs
- Filtering
- Pagination
- Auto-refresh
The final system integrates two different log formats — JSON logs from Open-appsec and plain-text logs from NGINX — into one centralized monitoring interface.
11. Monitoring Security Events
One of the most useful parts of the project was being able to see the relationship between an attack and the resulting security event.
For example:
SQL Injection
↓
Open-appsec
↓
Blocked
↓
WAF Log
↓
Python Parser
↓
DashboardSQL Injection
↓
Open-appsec
↓
Blocked
↓
WAF Log
↓
Python Parser
↓
DashboardThe same concept applied to HTTP Flood:
HTTP Flood
↓
NGINX Rate Limiting
↓
HTTP 429
↓
NGINX Log
↓
Python Parser
↓
DashboardHTTP Flood
↓
NGINX Rate Limiting
↓
HTTP 429
↓
NGINX Log
↓
Python Parser
↓
DashboardThis meant that I did not need to manually inspect the raw log files every time an event occurred.
The dashboard presented information such as attack category, severity, source IP, event time, and detected payload information.
Press enter or click to view image in full size
12. Results
After completing the implementation and testing, the system successfully demonstrated the intended security and monitoring workflow.
The main results were:
Test Security Mechanism Result SQL Injection Open-appsec WAF HTTP 403 XSS Open-appsec WAFH TTP 403 HTTP Flood NGINX Rate Limiting HTTP 429
The dashboard successfully integrated security logs from both Open-appsec and NGINX into a centralized interface.
During the testing process, the system processed up to 148,649 logs without data loss. Processing latency ranged from approximately 1.07 to 6.49 seconds, depending on the traffic volume.
The testing also included dashboard functionality such as filtering, pagination, navigation, and auto-refresh.
13. What I Learned
This project gave me hands-on experience across several areas of cybersecurity and infrastructure.
Technically, I worked with:
- Linux server administration
- Virtual networking
- NGINX
- Reverse Proxy
- Web Application Firewall
- Rate Limiting
- Web security testing
- Security logs
- Python
- JSON parsing
- Pandas
- Streamlit
- Security monitoring
However, the most important lesson was that security is not only about blocking attacks.
A security mechanism also needs to provide visibility.
In this project, I connected the entire process:
Incoming Traffic
↓
Security Inspection
↓
Attack Mitigation
↓
Log Generation
↓
Log Parsing
↓
Centralized MonitoringIncoming Traffic
↓
Security Inspection
↓
Attack Mitigation
↓
Log Generation
↓
Log Parsing
↓
Centralized Monitoring