A full-stack, AI-powered network intrusion detection system combining rule-based heuristics and machine learning to detect, alert, and block malicious traffic in real time.
- Overview
- Features
- Architecture
- Tech Stack
- Project Structure
- Prerequisites
- Installation & Setup
- Training the ML Model
- Running the Application
- Attack Simulations
- API Reference
- Database Schema
- How Detection Works
- Configuration
Hybrid IDS is a network intrusion detection system that passively sniffs live network traffic and analyzes each packet through two parallel detection engines:
- Rule Engine β Detects known attack patterns (port scans, brute force, DoS floods) using sliding-window heuristics with configurable thresholds.
- ML Engine β Uses a Random Forest classifier trained on the UNSW-NB15 dataset to detect anomalous traffic with a confidence score.
When either engine fires, the source IP is automatically blocked and an alert is recorded. A live React dashboard streams all detections in real time via polling.
| Feature | Description |
|---|---|
| π΄ Live Packet Capture | Sniffs raw network traffic using Scapy on all interfaces |
| π§ ML Anomaly Detection | Random Forest model trained on UNSW-NB15 (~175k samples, 10 features) |
| π Rule-Based Detection | Detects Port Scans, Brute Force, and DoS attacks via sliding time windows |
| π« Auto IP Blocking | Malicious IPs are instantly inserted into a blocked_ips table |
| π Live Dashboard | React UI with traffic chart, attack pie chart, alert feed, and top IPs |
| π§ͺ Attack Simulators | Three Scapy-based scripts to simulate real attacks for testing |
| π REST API | Full FastAPI backend with 8 endpoints for alerts, stats, and IPs |
| ποΈ PostgreSQL Storage | All packets, alerts, blocked IPs, and traffic stats persisted to DB |
| β‘ Threaded Architecture | Sniffer, traffic rate recorder, and API server run concurrently |
| π Auto-Polling Frontend | Dashboard auto-refreshes every 2β5 seconds without page reload |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Network Traffic β
βββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β Raw Packets
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Scapy Packet Sniffer β
β capture/sniffer.py + feature_extractor.py β
ββββββββββββββββ¬βββββββββββββββββββββββββββ¬ββββββββββββββββββββ
β β
βΌ βΌ
ββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββ
β Rule Engine β β ML Engine β
β detection/rule_ β β detection/ml_engine.py β
β engine.py β β Random Forest (UNSW-NB15) β
β β β Confidence threshold: 0.75 β
β β’ Port Scan β ββββββββββββββββ¬βββββββββββββββββββ
β β’ Brute Force β β
β β’ DoS Attack β β
ββββββββββββ¬ββββββββββββ β
β β
ββββββββββββββββ¬βββββββββββββββββ
β insert_alert() + block_ip()
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PostgreSQL Database β
β packets | alerts | blocked_ips | traffic_stats β
βββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FastAPI REST API (port 8000) β
β /api/alerts | /api/stats | /api/ips β
βββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β HTTP polling (2β5s)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β React Dashboard (port 5173) β
β StatCards | TrafficChart | PieChart | AlertTable | TopIPs β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Technology | Version | Purpose |
|---|---|---|
| 18.3 | UI framework | |
| 5.4 | Build tool & dev server | |
| 1.13 | HTTP client | |
| 3.8 | Line chart & pie chart |
| Details | |
|---|---|
| Dataset | UNSW-NB15 β ~175,000 network flow records |
| Model | Random Forest (100 estimators, max_depth=20, balanced class weights) |
| Features used | proto, service, state, dur, sbytes, dbytes, sttl, dttl, sloss, dloss |
| Alert threshold | Attack probability > 75% |
hybrid-ids/
β
βββ backend/
β βββ main.py # FastAPI app + sniffer startup
β βββ requirements.txt
β βββ .env # DB credentials (create this)
β β
β βββ api/
β β βββ routes_alerts.py # GET /alerts, POST /alerts/manual
β β βββ routes_stats.py # GET /stats/summary, /stats/traffic
β β βββ routes_ips.py # GET /ips/top, /ips/blocked, POST /ips/block
β β
β βββ capture/
β β βββ sniffer.py # Scapy sniff loop + traffic rate thread
β β βββ feature_extractor.py # Packet β feature dict + ML vector
β β
β βββ detection/
β β βββ rule_engine.py # Port scan / brute force / DoS rules
β β βββ ml_engine.py # Random Forest inference
β β
β βββ db/
β β βββ connection.py # psycopg2 thread-safe connection pool
β β βββ queries.py # All SQL queries
β β βββ schema.sql # Table definitions + indexes
β β
β βββ models/
β βββ train.py # One-time training script
β βββ UNSW_NB15_training-set.csv # (download separately)
β βββ UNSW_NB15_testing-set.csv # (download separately)
β βββ model.pkl # Generated after training
β βββ model_meta.pkl # Encoders + feature list
β
βββ frontend/
β βββ package.json
β βββ vite.config.js # Proxy /api β localhost:8000
β βββ index.html
β βββ src/
β βββ App.jsx
β βββ main.jsx
β βββ api/client.js # Axios instance
β βββ hooks/
β β βββ useAlerts.js # Polls /api/alerts every 2s
β β βββ useStats.js # Polls /api/stats every 3s
β β βββ useTraffic.js # Polls /api/stats/traffic every 3s
β βββ components/
β β βββ NavBar.jsx
β β βββ StatCard.jsx
β β βββ TrafficChart.jsx
β β βββ AttackPieChart.jsx
β β βββ AlertTable.jsx
β β βββ TopIPsTable.jsx
β βββ pages/
β βββ Dashboard.jsx
β
βββ scripts/
βββ simulate_portscan.py # Sends TCP SYNs to 59 ports
βββ simulate_bruteforce.py # Sends 30 SYNs to port 22
βββ simulate_dos.py # Floods 200 SYNs to port 80
Before you begin, ensure you have the following installed:
- Python 3.10+ β python.org
- Node.js 18+ β nodejs.org
- PostgreSQL 14+ β postgresql.org
- Npcap (Windows) or libpcap (Linux/macOS) β required by Scapy
- Windows: npcap.com
- Linux:
sudo apt install libpcap-dev - macOS:
brew install libpcap
- Admin / root privileges β required for raw packet sniffing
git clone https://github.com/Ayush-2404/Sentinel_IDS.git
cd Sentinel_IDSConnect to PostgreSQL and create a database:
CREATE DATABASE ids_db;Then run the schema file to create all tables and indexes:
psql -U postgres -d ids_db -f backend/db/schema.sqlCreate a .env file inside the backend/ directory:
# backend/.env
DB_HOST=localhost
DB_PORT=5432
DB_NAME=ids_db
DB_USER=postgres
DB_PASSWORD=your_password_herecd backend
# Create and activate virtual environment
python -m venv venv
# Windows
venv\Scripts\activate
# Linux / macOS
source venv/bin/activate
# Install all dependencies
pip install -r requirements.txtcd frontend
npm installThe ML engine requires a trained model before the backend will perform anomaly detection.
Download the CSV files from the official UNSW-NB15 page and place them in backend/models/:
backend/models/UNSW_NB15_training-set.csv
backend/models/UNSW_NB15_testing-set.csv
# From the backend/ directory with venv activated
cd backend
python -m models.trainThis will:
- Load and preprocess ~175k rows using 10 selected features
- Encode categorical columns (
proto,service,state) with LabelEncoder - Train a Random Forest with 100 estimators across all CPU cores
- Print accuracy and a full classification report
- Save
model.pklandmodel_meta.pkltobackend/models/
Training takes approximately 1β2 minutes depending on your hardware.
Expected output:
[Train] Training set: 175341 samples
[Train] Test set: 82332 samples
[Train] Attack ratio in training: 46.5%
[Train] Training Random Forest (this takes ~1-2 mins)...
[Train] === Results ===
Accuracy: 0.9XXX
precision recall f1-score
Normal 0.XX 0.XX 0.XX
Attack 0.XX 0.XX 0.XX
[Train] Saved model β backend/models/model.pkl
[Train] Saved meta β backend/models/model_meta.pkl
You need three terminals open simultaneously.
cd backend
source venv/bin/activate # or venv\Scripts\activate on Windows
# Linux / macOS β root required for raw sockets
sudo venv/bin/python -m uvicorn main:app --host 0.0.0.0 --port 8000 --reload
# Windows β run terminal as Administrator
python -m uvicorn main:app --host 0.0.0.0 --port 8000 --reloadYou should see:
[ML Engine] Model + encoders loaded (UNSW-NB15).
[Main] Sniffer thread started.
[Sniffer] Starting packet capture...
INFO: Uvicorn running on http://0.0.0.0:8000
cd frontend
npm run devYou should see:
VITE v5.x.x ready in XXX ms
β Local: http://localhost:5173/
See the Attack Simulations section below.
Open your browser and navigate to:
http://localhost:5173
The API docs (Swagger UI) are available at:
http://localhost:8000/docs
Three simulation scripts are included to generate test traffic and verify detections. They both send real Scapy packets and call the manual alert API endpoint to guarantee the dashboard shows the correct attack type.
β οΈ Update theTARGETIP in each script to match your machine's actual local IP address (find it withipconfigon Windows orip addron Linux).
# Simulate a Port Scan β probes 59 ports
python scripts/simulate_portscan.py
# Simulate a Brute Force β 30 SYNs to port 22 (SSH)
python scripts/simulate_bruteforce.py
# Simulate a DoS Flood β 200 SYNs to port 80 in ~2 seconds
python scripts/simulate_dos.pyEach script will print a confirmation and the alert will appear in the Live Alert Feed on the dashboard within 2 seconds.
| Script | Fake Source IP | Attack Type | Packets Sent |
|---|---|---|---|
simulate_portscan.py |
10.0.0.99 |
port_scan |
59 TCP SYNs across ports 1β59 |
simulate_bruteforce.py |
10.0.0.101 |
brute_force |
30 TCP SYNs to port 22 |
simulate_dos.py |
10.0.0.100 |
dos_attack |
200 TCP SYNs to port 80 |
Base URL: http://localhost:8000
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/alerts/ |
Get recent alerts (default: last 50) |
GET |
/api/alerts/types |
Get alert counts grouped by attack type |
GET |
/api/alerts/count |
Get total alert count |
POST |
/api/alerts/manual |
Manually create an alert (used by simulators) |
POST /api/alerts/manual body:
{
"src_ip": "10.0.0.99",
"attack_type": "port_scan",
"confidence": 1.0,
"details": "Scanned 59 ports in 1.2s"
}| Method | Endpoint | Description |
|---|---|---|
GET |
/api/stats/summary |
Total packets, alerts, and blocked IPs |
GET |
/api/stats/traffic |
Recent packets/sec readings (default: last 30) |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/ips/top |
Top source IPs by packet count (default: top 10) |
GET |
/api/ips/blocked |
List all blocked IPs |
POST |
/api/ips/block |
Manually block an IP |
-- All captured network packets
packets (id, src_ip, dst_ip, src_port, dst_port, protocol, length, timestamp)
-- Alerts from rule engine and ML engine
alerts (id, src_ip, attack_type, confidence, details, timestamp)
-- IPs blocked automatically or manually
blocked_ips (id, ip_address UNIQUE, reason, blocked_at)
-- Traffic rate samples recorded every 5 seconds
traffic_stats (id, packets_per_sec, recorded_at)Indexes are created on alerts(timestamp DESC), packets(src_ip), and traffic_stats(recorded_at DESC) for fast dashboard queries.
Uses a 15-second sliding window per source IP. Checks three conditions on every incoming packet:
| Attack | Trigger | Threshold |
|---|---|---|
| Port Scan | Distinct destination ports from one IP in 15s | > 5 ports |
| Brute Force | Repeated requests from one IP to the same port in 15s | > 8 requests |
| DoS Attack | Total packets from one IP in 5s | > 30 packets |
Alerts are deduplicated β the same attack type from the same IP won't fire again for 30 seconds. Whitelisted IPs (localhost, your machine's IP) are skipped entirely.
Each packet is mapped to a 10-feature vector approximating the UNSW-NB15 flow schema. The Random Forest model outputs a probability. If P(attack) > 0.75, a ml_anomaly alert is inserted and the IP is blocked.
Feature approximations for single packets:
sbytesβ packet lengthsttlβ IP TTL fielddur,dbytes,dttl,sloss,dlossβ set to 0 (unknown for single packets)stateβ hardcoded to"INT"(in-progress)
PORT_SCAN_THRESHOLD = 5 # distinct ports in WINDOW_SECONDS
BRUTE_FORCE_THRESHOLD = 8 # requests to same port in WINDOW_SECONDS
DOS_THRESHOLD = 30 # total packets in 5s
WINDOW_SECONDS = 15if attack_prob > 0.75: # change this to tune sensitivityself.WHITELIST = {
"127.0.0.1",
"::1",
"192.168.1.7", # replace with your machine's actual IP
}# None = sniff on all interfaces
# Set to e.g. "eth0" or "Wi-Fi" to target a specific interface
start_sniffing(interface=None)This project is licensed under the MIT License.