beskar 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +143 -0
  3. data/README.md +298 -110
  4. data/app/controllers/beskar/application_controller.rb +170 -0
  5. data/app/controllers/beskar/banned_ips_controller.rb +280 -0
  6. data/app/controllers/beskar/dashboard_controller.rb +70 -0
  7. data/app/controllers/beskar/security_events_controller.rb +182 -0
  8. data/app/controllers/concerns/beskar/controllers/security_tracking.rb +6 -6
  9. data/app/models/beskar/banned_ip.rb +68 -27
  10. data/app/models/beskar/security_event.rb +14 -0
  11. data/app/services/beskar/banned_ip_manager.rb +78 -0
  12. data/app/views/beskar/banned_ips/edit.html.erb +259 -0
  13. data/app/views/beskar/banned_ips/index.html.erb +361 -0
  14. data/app/views/beskar/banned_ips/new.html.erb +310 -0
  15. data/app/views/beskar/banned_ips/show.html.erb +310 -0
  16. data/app/views/beskar/dashboard/index.html.erb +280 -0
  17. data/app/views/beskar/security_events/index.html.erb +309 -0
  18. data/app/views/beskar/security_events/show.html.erb +307 -0
  19. data/app/views/layouts/beskar/application.html.erb +647 -5
  20. data/config/routes.rb +41 -0
  21. data/lib/beskar/configuration.rb +24 -10
  22. data/lib/beskar/engine.rb +4 -4
  23. data/lib/beskar/logger.rb +293 -0
  24. data/lib/beskar/middleware/request_analyzer.rb +128 -53
  25. data/lib/beskar/models/security_trackable_authenticable.rb +11 -11
  26. data/lib/beskar/models/security_trackable_devise.rb +5 -5
  27. data/lib/beskar/models/security_trackable_generic.rb +12 -12
  28. data/lib/beskar/services/account_locker.rb +12 -12
  29. data/lib/beskar/services/geolocation_service.rb +8 -8
  30. data/lib/beskar/services/ip_whitelist.rb +2 -2
  31. data/lib/beskar/services/waf.rb +307 -78
  32. data/lib/beskar/version.rb +1 -1
  33. data/lib/beskar.rb +1 -0
  34. data/lib/generators/beskar/install/install_generator.rb +158 -0
  35. data/lib/generators/beskar/install/templates/initializer.rb.tt +177 -0
  36. data/lib/tasks/beskar_tasks.rake +11 -2
  37. metadata +35 -6
  38. data/lib/beskar/templates/beskar_initializer.rb +0 -107
data/README.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  **Beskar** is a comprehensive, Rails-native security engine designed to provide multi-layered, proactive protection for modern web applications. It defends against common threats, bot activity, and account takeovers without requiring external dependencies, integrating seamlessly into your application as a natural extension of the framework.
4
4
 
5
+ ## Table of Contents
6
+
7
+ - [Features](#features)
8
+ - [Installation](#installation)
9
+ - [Quick Start](#quick-start)
10
+ - [Dashboard Authentication (REQUIRED)](#dashboard-authentication-required)
11
+ - [Add to Your User Model](#add-to-your-user-model)
12
+ - [Configuration](#configuration)
13
+ - [Usage](#usage)
14
+ - [Risk-Based Account Locking](#risk-based-account-locking-with-devise-lockable)
15
+ - [Rate Limiting](#rate-limiting)
16
+ - [IP Whitelisting](#ip-whitelisting)
17
+ - [Web Application Firewall (WAF)](#web-application-firewall-waf)
18
+ - [IP Blocking and Banning](#ip-blocking-and-banning)
19
+ - [Security Events](#security-events)
20
+ - [Middleware Integration](#middleware-integration)
21
+ - [WAF Pattern Reference](#waf-pattern-reference)
22
+ - [Security Best Practices](#security-best-practices)
23
+ - [Troubleshooting](#troubleshooting)
24
+ - [Development](#development)
25
+ - [Contributing](#contributing)
26
+ - [License](#license)
27
+
5
28
  ## Features
6
29
 
7
30
  - **Devise Integration:** Seamless integration with Devise authentication for automatic login tracking and security analysis.
@@ -10,14 +33,14 @@
10
33
  - **Brute Force Detection:** Advanced pattern recognition to detect single account attacks vs credential stuffing attempts, with automatic IP banning.
11
34
  - **IP Whitelisting:** Allow trusted IPs (office networks, partners, security scanners) to bypass blocking while maintaining full audit logs. Supports individual IPs and CIDR notation.
12
35
  - **Persistent IP Blocking:** Hybrid cache + database blocking system that survives application restarts. Auto-bans IPs after authentication abuse or excessive rate limiting violations.
13
- - **Web Application Firewall (WAF):** Real-time detection and blocking of vulnerability scanning attempts across 7 attack categories (WordPress, PHP admin panels, config files, path traversal, framework debug, CMS detection, common exploits). Includes escalating ban durations and monitor-only mode.
36
+ - **Web Application Firewall (WAF):** Real-time detection and blocking of vulnerability scanning attempts across 12 attack categories including Rails exception analysis (WordPress scans, WordPress static files, PHP admin panels, config files, path traversal, framework debug, CMS detection, common exploits, UnknownFormat, IP spoofing, InvalidType, RecordNotFound enumeration). Includes escalating ban durations, monitor-only mode, and configurable exclusion patterns.
14
37
  - **Security Event Tracking:** Comprehensive logging of authentication events with risk scoring and metadata extraction.
15
38
  - **IP Geolocation:** MaxMind GeoLite2-City database integration for country/city location, coordinates, timezone, and enhanced risk scoring (configurable, database not included due to licensing).
16
39
  - **Geographic Anomaly Detection:** Haversine-based impossible travel detection and location-based risk assessment.
17
40
  - **Advanced Bot Detection:** Multi-layered defense using JavaScript challenges and invisible honeypots to filter out malicious bots while allowing legitimate ones.
18
41
  - **Modular Architecture:** Devise-specific code is isolated in separate services for maintainability and extensibility.
19
42
  - **Rails-Native Architecture:** Built as a mountable `Rails::Engine`, it leverages `ActiveJob` and `Rails.cache` for high performance and low overhead.
20
- - **Real-Time Dashboard (Coming Soon):** A mountable dashboard to visualize security events and monitor threats as they happen.
43
+ - **Security Dashboard:** A mountable web interface for monitoring security events, managing IP bans, and viewing statistics. Features configurable authentication, real-time filtering, and export capabilities. See [Dashboard Authentication](#dashboard-authentication) section below.
21
44
 
22
45
  ## Installation
23
46
 
@@ -52,22 +75,98 @@ bin/rails db:migrate
52
75
 
53
76
  ### Quick Start
54
77
 
78
+ **1. Configure Dashboard Authentication (Required)**
79
+
80
+ Before using Beskar, you must configure authentication for the dashboard. See the [Dashboard Authentication](#dashboard-authentication) section below for details and examples.
81
+
82
+ **2. Enable WAF Monitoring**
83
+
55
84
  By default, Beskar enables the **Web Application Firewall (WAF) in monitor-only mode**. This means:
56
85
  - ✅ Vulnerability scans are detected and logged
57
- - ✅ Security events are created for analysis
86
+ - ✅ Security events are created for analysis
58
87
  - ⚠️ No requests are blocked yet (safe to enable in production)
59
88
 
60
89
  After monitoring for 24-48 hours, review the logs and disable monitor-only mode to enable active blocking:
61
90
 
62
91
  ```ruby
63
92
  # config/initializers/beskar.rb
64
- config.waf = {
65
- enabled: true,
66
- monitor_only: false, # Change this to enable blocking
93
+ Beskar.configure do |config|
94
+ config.monitor_only = true # Change this to false to enable blocking
95
+ config.waf[:enabled] = true
67
96
  # ... rest of configuration
68
- }
97
+ end
98
+ ```
99
+
100
+ ### Dashboard Authentication (REQUIRED)
101
+
102
+ **⚠️ IMPORTANT: Dashboard authentication must be configured for all environments.**
103
+
104
+ The Beskar dashboard requires authentication to prevent unauthorized access. You must configure how users authenticate to access the dashboard by setting up the `authenticate_admin` callback:
105
+
106
+ ```ruby
107
+ # config/initializers/beskar.rb
108
+ Beskar.configure do |config|
109
+ # REQUIRED: Configure dashboard authentication
110
+ # The block is executed in the controller context and receives the request object.
111
+ # You have access to all controller methods (cookies, session, etc.) and helpers.
112
+ config.authenticate_admin = ->(request) do
113
+ # Return truthy to allow access, falsey to deny
114
+
115
+ # Example 1: Devise with admin role (recommended for production)
116
+ user = request.env['warden']&.authenticate(scope: :user)
117
+ user&.admin?
118
+ end
119
+ end
69
120
  ```
70
121
 
122
+ **Why this is required:** Previous versions allowed unauthenticated access in development/test environments, which could lead to production security issues. Now, authentication must be explicitly configured for all environments to prevent accidental exposure.
123
+
124
+ **Other Authentication Strategies:**
125
+
126
+ ```ruby
127
+ # Token-based authentication
128
+ config.authenticate_admin = ->(request) do
129
+ request.headers['Authorization'] == "Bearer #{ENV['BESKAR_ADMIN_TOKEN']}"
130
+ end
131
+
132
+ # HTTP Basic Auth (uses controller method)
133
+ config.authenticate_admin = ->(request) do
134
+ authenticate_or_request_with_http_basic do |username, password|
135
+ username == ENV['BESKAR_USERNAME'] && password == ENV['BESKAR_PASSWORD']
136
+ end
137
+ end
138
+
139
+ # Cookie-based authentication (uses controller cookies)
140
+ config.authenticate_admin = ->(request) do
141
+ cookies.signed[:admin_token] == ENV['BESKAR_ADMIN_TOKEN']
142
+ end
143
+
144
+ # Development/Testing bypass (use with caution!)
145
+ config.authenticate_admin = ->(request) do
146
+ Rails.env.development? || Rails.env.test?
147
+ end
148
+ ```
149
+
150
+ **Accessing the Dashboard:**
151
+
152
+ After configuring authentication, mount the engine in your routes:
153
+
154
+ ```ruby
155
+ # config/routes.rb
156
+ Rails.application.routes.draw do
157
+ mount Beskar::Engine => "/beskar"
158
+ end
159
+ ```
160
+
161
+ Then visit `http://localhost:3000/beskar` to access the dashboard.
162
+
163
+ **Dashboard Features:**
164
+ - 📊 Security event monitoring with filtering and search
165
+ - 🚫 IP ban management (view, extend, unban)
166
+ - 📈 Statistics and risk distribution analysis
167
+ - 📥 Export capabilities (CSV/JSON)
168
+ - 🔒 CSRF protection and secure by default
169
+
71
170
  ### Add to Your User Model
72
171
 
73
172
  Include the `SecurityTrackable` concern in your Devise user model:
@@ -75,8 +174,8 @@ Include the `SecurityTrackable` concern in your Devise user model:
75
174
  ```ruby
76
175
  # app/models/user.rb
77
176
  class User < ApplicationRecord
78
- include Beskar::SecurityTrackable
79
-
177
+ include Beskar::Models::SecurityTrackable
178
+
80
179
  devise :database_authenticatable, :registerable,
81
180
  :recoverable, :rememberable, :validatable
82
181
  # ... other Devise modules
@@ -85,11 +184,20 @@ end
85
184
 
86
185
  ## Configuration
87
186
 
88
- You can configure Beskar in the initializer file created by the installer:
187
+ You can configure Beskar in the initializer file created by the installer.
188
+
189
+ > **Note:** Dashboard authentication setup is covered in the [Dashboard Authentication](#dashboard-authentication-required) section above.
89
190
 
90
191
  ```ruby
91
192
  # config/initializers/beskar.rb
92
193
  Beskar.configure do |config|
194
+ # === Dashboard Authentication (REQUIRED) ===
195
+ # See "Dashboard Authentication" section above for examples and details
196
+ config.authenticate_admin = ->(request) do
197
+ user = request.env['warden']&.authenticate(scope: :user)
198
+ user&.admin?
199
+ end
200
+
93
201
  # === Security Tracking ===
94
202
  # Controls what security events are tracked and analyzed
95
203
  config.security_tracking = {
@@ -119,27 +227,28 @@ Beskar.configure do |config|
119
227
  }
120
228
 
121
229
  # === IP Whitelisting ===
122
- # Allow trusted IPs to bypass all blocking (bans, rate limits, WAF)
123
- # while still logging all activity for audit purposes
124
- config.ip_whitelist = [
125
- "192.168.1.100", # Single IP address
126
- "10.0.0.0/24", # CIDR notation - entire subnet
127
- "172.16.0.0/16", # Larger CIDR range
128
- "2001:db8::/32" # IPv6 support
129
- ]
230
+ # See "IP Whitelisting" section below for detailed examples
231
+ config.ip_whitelist = [] # Add trusted IPs here (supports CIDR notation)
130
232
 
131
233
  # === Web Application Firewall (WAF) ===
132
- # Real-time detection and blocking of vulnerability scanning attempts
133
- config.waf = {
134
- enabled: true, # Master switch for WAF
135
- auto_block: true, # Automatically ban IPs after threshold
136
- block_threshold: 3, # Number of violations before blocking
137
- violation_window: 1.hour, # Time window for counting violations
138
- block_durations: [1.hour, 6.hours, 24.hours, 7.days], # Escalating ban durations
139
- permanent_block_after: 5, # Permanent ban after N violations
140
- create_security_events: true, # Log WAF violations to SecurityEvent table
141
- monitor_only: false # If true, log violations but never block
142
- }
234
+ # See "Web Application Firewall" section below for production examples
235
+ # Defaults shown here - use [:key] syntax to preserve other defaults
236
+ config.waf[:enabled] = true # Master switch for WAF
237
+ # config.waf[:auto_block] = true # Default: true
238
+ # config.waf[:score_threshold] = 150 # Default: 150 (cumulative risk score before blocking)
239
+ # config.waf[:violation_window] = 6.hours # Default: 6 hours (max time to track violations)
240
+ # config.waf[:block_durations] = [1.hour, 6.hours, 24.hours, 7.days] # Escalating bans
241
+ # config.waf[:permanent_block_after] = 500 # Permanent after cumulative score reaches 500
242
+ # config.waf[:create_security_events] = true # Log to SecurityEvent table
243
+ # config.waf[:record_not_found_exclusions] = [] # Regex patterns for false positives
244
+ # config.waf[:decay_enabled] = true # Enable exponential decay of violation scores
245
+ # config.waf[:decay_rates] = { # Decay rates by severity (half-life in minutes)
246
+ # critical: 360, # 6 hour half-life
247
+ # high: 120, # 2 hour half-life
248
+ # medium: 45, # 45 minute half-life
249
+ # low: 15 # 15 minute half-life
250
+ # }
251
+ # config.waf[:max_violations_tracked] = 50 # Maximum violations to track per IP
143
252
 
144
253
  # === Risk-Based Account Locking ===
145
254
  # Automatically lock accounts when authentication risk score exceeds threshold
@@ -163,32 +272,11 @@ Beskar.configure do |config|
163
272
  }
164
273
  end
165
274
 
166
- # Security Tracking Configuration Details
167
- The security tracking system respects all configuration settings:
168
-
169
- - **`enabled: false`** - Completely disables all security event tracking
170
- - **`track_successful_logins: false`** - Stops tracking successful login events
171
- - **`track_failed_logins: false`** - Stops tracking failed login attempts
172
- - **`auto_analyze_patterns: false`** - Disables automatic threat pattern analysis
173
-
174
- When tracking is disabled via configuration, no `SecurityEvent` records are created and no background analysis jobs are queued.
175
275
  ```
176
276
 
177
277
  ## Usage
178
278
 
179
- ### Basic Setup
180
-
181
- Once installed and configured, Beskar works automatically with Devise. Add the `SecurityTrackable` module to your User model:
182
-
183
- ```ruby
184
- class User < ApplicationRecord
185
- devise :database_authenticatable, :registerable,
186
- :recoverable, :rememberable, :validatable
187
-
188
- # Add Beskar security tracking
189
- include Beskar::Models::SecurityTrackable
190
- end
191
- ```
279
+ > **Note:** If you haven't already, see the [Add to Your User Model](#add-to-your-user-model) section in Quick Start for setting up `SecurityTrackable`.
192
280
 
193
281
  ### Risk-Based Account Locking (with Devise Lockable)
194
282
 
@@ -203,7 +291,7 @@ class User < ApplicationRecord
203
291
  devise :database_authenticatable, :registerable,
204
292
  :recoverable, :rememberable, :validatable,
205
293
  :lockable # Add this for risk-based locking
206
-
294
+
207
295
  include Beskar::Models::SecurityTrackable
208
296
  end
209
297
  ```
@@ -351,7 +439,7 @@ attack_type = rate_limiter.attack_pattern_type
351
439
  case attack_type
352
440
  when :brute_force_single_account
353
441
  # Single IP attacking one account
354
- when :distributed_single_account
442
+ when :distributed_single_account
355
443
  # Multiple IPs attacking one account
356
444
  when :single_ip_multiple_accounts
357
445
  # One IP attacking multiple accounts (credential stuffing)
@@ -394,56 +482,156 @@ Beskar::Services::IpWhitelist.clear_cache!
394
482
 
395
483
  ### Web Application Firewall (WAF)
396
484
 
397
- Beskar's WAF detects and blocks vulnerability scanning attempts across 7 attack categories:
485
+ Beskar's WAF uses a **score-based blocking system with exponential decay** to intelligently detect and block vulnerability scanning attempts across 12 attack categories:
398
486
 
399
487
  **Attack Categories Detected:**
400
- 1. **WordPress Scans** (High Severity) - `/wp-admin`, `/wp-login.php`, `/xmlrpc.php`
401
- 2. **PHP Admin Panels** (High Severity) - `/phpmyadmin`, `/admin.php`, `/phpinfo.php`
402
- 3. **Config Files** (Critical Severity) - `/.env`, `/.git`, `/database.yml`
403
- 4. **Path Traversal** (Critical Severity) - `/../../../etc/passwd`, URL encoded variants
404
- 5. **Framework Debug** (Medium Severity) - `/rails/info/routes`, `/__debug__`, `/telescope`
405
- 6. **CMS Detection** (Medium Severity) - `/joomla`, `/drupal`, `/magento`
406
- 7. **Common Exploits** (Critical Severity) - `/shell.php`, `/c99.php`, `/webshell`
488
+ 1. **WordPress Scans** (High: 80 points) - `/wp-admin`, `/wp-login.php`, `/wp-content/*.php`, `/xmlrpc.php`
489
+ 2. **WordPress Static Files** (Low: 30 points) - `/wp-content/*.css`, `/wp-content/*.js`, `/wp-content/*.jpg` (broken links, not attacks)
490
+ 3. **PHP Admin Panels** (High: 80 points) - `/phpmyadmin`, `/admin.php`, `/phpinfo.php`
491
+ 4. **Config Files** (Critical: 95 points) - `/.env`, `/.git`, `/database.yml`
492
+ 5. **Path Traversal** (Critical: 95 points) - `/../../../etc/passwd`, URL encoded variants
493
+ 6. **Framework Debug** (Medium: 60 points) - `/rails/info/routes`, `/__debug__`, `/telescope`
494
+ 7. **CMS Detection** (Medium: 60 points) - `/joomla`, `/drupal`, `/magento`
495
+ 8. **Common Exploits** (Critical: 95 points) - `/shell.php`, `/c99.php`, `/webshell`
496
+ 9. **ActionController::UnknownFormat** (Medium: 60 points) - Detects requests for unusual formats like `/users/1.exe`, `/api/data.bat` that trigger Rails format exceptions, indicating potential scanning
497
+ 10. **ActionDispatch::RemoteIp::IpSpoofAttackError** (Critical: 95 points) - Detects IP spoofing attempts when conflicting IP headers are present
498
+ 11. **ActionDispatch::Http::MimeNegotiation::InvalidType** (Medium: 60 points) - Detects invalid MIME type requests like `GET "../../../../../../../../etc/passwd{{"` that indicate scanner activity
499
+ 12. **ActiveRecord::RecordNotFound** (Low: 30 points) - Detects potential record enumeration scans like `/admin/users/999999`, with configurable exclusions to prevent false positives
500
+
501
+ **How Score-Based Blocking Works:**
502
+
503
+ Instead of counting violations (1, 2, 3...), Beskar tracks a **cumulative risk score** that decays over time:
504
+
505
+ - Each violation adds points based on severity (Critical=95, High=80, Medium=60, Low=30)
506
+ - Violations **decay exponentially** based on severity (critical threats persist longer)
507
+ - IP is blocked when cumulative score reaches threshold (default: 150 points)
508
+ - Lower-severity violations decay faster, reducing false positives from legitimate 404s
509
+
510
+ **Example Scenarios:**
511
+ ```ruby
512
+ # Scenario 1: Legitimate user hitting 404s
513
+ 10 × RecordNotFound (30 points each) = 300 cumulative
514
+ BUT: Low severity decays with 15-minute half-life
515
+ → Score drops quickly, no ban triggered
516
+
517
+ # Scenario 2: Attacker scanning config files
518
+ 2 × /.env access (95 points each) = 190 points
519
+ → Exceeds threshold (150) → Immediate ban
520
+ → Critical severity persists for 6 hours
521
+
522
+ # Scenario 3: Mixed attack pattern
523
+ 1 × WordPress scan (80) + 1 × Config access (95) = 175
524
+ → Exceeds threshold → Ban triggered
525
+ → Different decay rates for each violation type
526
+ ```
407
527
 
408
- **Configuration Examples:**
528
+ **Configuration Profiles:**
409
529
 
410
530
  ```ruby
411
- # Production - Aggressive protection
531
+ # 🔥 STRICT - High-security environment (financial, healthcare)
412
532
  Beskar.configure do |config|
413
- config.waf = {
414
- enabled: true,
415
- auto_block: true,
416
- block_threshold: 2, # Block after just 2 violations
417
- violation_window: 30.minutes,
418
- block_durations: [6.hours, 24.hours, 7.days, 30.days],
419
- permanent_block_after: 4,
420
- create_security_events: true
533
+ config.waf[:enabled] = true
534
+ config.waf[:auto_block] = true
535
+ config.waf[:score_threshold] = 100 # Lower threshold = faster blocking
536
+ config.waf[:violation_window] = 12.hours # Longer memory
537
+ config.waf[:permanent_block_after] = 300 # Permanent ban at 300 cumulative score
538
+ config.waf[:block_durations] = [6.hours, 24.hours, 7.days, 30.days]
539
+
540
+ # Slower decay = violations persist longer
541
+ config.waf[:decay_rates] = {
542
+ critical: 720, # 12 hour half-life (very persistent)
543
+ high: 360, # 6 hour half-life
544
+ medium: 120, # 2 hour half-life
545
+ low: 30 # 30 minute half-life
421
546
  }
547
+
548
+ # Exclude legitimate 404-prone paths
549
+ config.waf[:record_not_found_exclusions] = [
550
+ %r{/posts/.*}, %r{/articles/\d+}, %r{/public/.*}
551
+ ]
552
+ end
553
+
554
+ # ⚖️ BALANCED - Default production (recommended for most apps)
555
+ Beskar.configure do |config|
556
+ config.waf[:enabled] = true
557
+ config.waf[:auto_block] = true
558
+ config.waf[:score_threshold] = 150 # Default threshold
559
+ config.waf[:violation_window] = 6.hours # Standard window
560
+ config.waf[:permanent_block_after] = 500 # Permanent at 500 cumulative
561
+ config.waf[:decay_enabled] = true
562
+ # Uses default decay rates (critical: 360, high: 120, medium: 45, low: 15)
563
+
564
+ config.waf[:record_not_found_exclusions] = [
565
+ %r{/posts/.*}, %r{/products/[\\w-]+}
566
+ ]
422
567
  end
423
568
 
424
- # Development - Monitor only
569
+ # 🧪 PERMISSIVE - High-traffic public site with many 404s
425
570
  Beskar.configure do |config|
426
- config.waf = {
427
- enabled: true,
428
- monitor_only: true, # Log but never block
429
- create_security_events: true
571
+ config.waf[:enabled] = true
572
+ config.waf[:auto_block] = true
573
+ config.waf[:score_threshold] = 200 # Higher tolerance
574
+ config.waf[:violation_window] = 3.hours # Shorter memory
575
+ config.waf[:permanent_block_after] = 800 # Rare permanent bans
576
+
577
+ # Faster decay = violations forgotten quickly
578
+ config.waf[:decay_rates] = {
579
+ critical: 180, # 3 hour half-life
580
+ high: 60, # 1 hour half-life
581
+ medium: 20, # 20 minute half-life
582
+ low: 5 # 5 minute half-life (very forgiving)
430
583
  }
431
-
432
- config.ip_whitelist = ["127.0.0.1", "::1"] # Whitelist localhost
584
+
585
+ # Extensive exclusions for public content
586
+ config.waf[:record_not_found_exclusions] = [
587
+ %r{/posts/.*}, %r{/articles/.*}, %r{/tags/.*},
588
+ %r{/search/.*}, %r{/public/.*}, %r{/assets/.*}
589
+ ]
590
+ end
591
+
592
+ # 🔍 MONITOR ONLY - Testing/staging (recommended before going live)
593
+ Beskar.configure do |config|
594
+ config.monitor_only = true # Log violations but NEVER block
595
+ config.waf[:enabled] = true
596
+ config.waf[:create_security_events] = true
597
+ config.ip_whitelist = ["127.0.0.1", "::1"] # Whitelist localhost
433
598
  end
434
599
  ```
435
600
 
436
601
  **Blocking Behavior:**
437
- - **First violation**: Logged, violation count incremented
438
- - **Threshold reached** (default 3): IP automatically banned for 1 hour
439
- - **Repeat violations**: Ban duration escalates: 1h 6h → 24h → 7d → **permanent**
440
- - **Permanent block**: After 5 violations (configurable), IP is permanently banned
441
- - **Monitor mode**: Logs all violations but never blocks (useful for tuning)
602
+
603
+ With **default settings** (score_threshold: 150):
604
+ - **Violations accumulate**: Each violation adds points based on severity
605
+ - **Score threshold reached**: IP automatically banned when cumulative score 150
606
+ - **Exponential decay**: Violations lose impact over time based on severity
607
+ - **Ban duration escalates**: Based on total score accumulated:
608
+ - 150-300 points → 1 hour ban
609
+ - 300-450 points → 6 hour ban
610
+ - 450-600 points → 24 hour ban
611
+ - 600+ points → 7 day ban
612
+ - 500+ cumulative score → **permanent ban**
613
+
614
+ **Key Advantages:**
615
+ - **Fewer false positives**: Low-severity violations (404s) decay quickly
616
+ - **Faster response to serious threats**: Critical violations persist longer
617
+ - **Adaptive blocking**: Mixed attack patterns properly weighted
618
+ - **Monitor mode compatible**: Set `config.monitor_only = true` to log without blocking
619
+
620
+ > **Production Tip:** Start with monitor mode for 24-48 hours to observe your traffic patterns, then adjust thresholds and exclusions before enabling blocking.
442
621
 
443
622
  **Check WAF status:**
444
623
  ```ruby
445
- # Check if WAF detected threats
624
+ # Get current risk score (with decay applied)
625
+ current_score = Beskar::Services::Waf.get_current_score(ip_address)
626
+ # => 145.3 (below threshold, not blocked)
627
+
628
+ # Get number of violations tracked
446
629
  violation_count = Beskar::Services::Waf.get_violation_count(ip_address)
630
+ # => 3 (number of violations being tracked)
631
+
632
+ # Get detailed violation records
633
+ violations = Beskar::Services::Waf.get_violations(ip_address)
634
+ # => [{timestamp: ..., score: 95, severity: :critical, category: :config_files}, ...]
447
635
 
448
636
  # Reset violations (admin action)
449
637
  Beskar::Services::Waf.reset_violations(ip_address)
@@ -461,12 +649,15 @@ end
461
649
 
462
650
  Beskar uses a hybrid cache + database blocking system that persists across application restarts.
463
651
 
464
- **Automatic IP Banning:**
652
+ **Automatic IP Banning Thresholds:**
653
+
654
+ | Trigger | Threshold | Time Window | Ban Duration | Configurable |
655
+ |---------|-----------|-------------|--------------|--------------|
656
+ | **Failed Authentication** | 10 attempts | 1 hour | 1 hour (escalating) | Via rate_limiting config |
657
+ | **Rate Limit Violations** | 5 violations | 1 hour | 1 hour (escalating) | Via rate_limiting config |
658
+ | **WAF Violations** | 3 violations | 1 hour | 1 hour (escalating) | Via waf[:block_threshold] |
465
659
 
466
- IPs are automatically banned for:
467
- 1. **Authentication abuse** - 10+ failed login attempts in 1 hour
468
- 2. **Rate limit violations** - 5+ rate limit violations in 1 hour
469
- 3. **WAF violations** - 3+ vulnerability scan attempts (configurable)
660
+ > **Note:** All ban durations escalate on repeat offenses: 1h → 6h → 24h → 7d → permanent
470
661
 
471
662
  **Manual IP Management:**
472
663
 
@@ -574,10 +765,10 @@ class SecurityCleanupJob < ApplicationJob
574
765
  # Remove expired bans from database
575
766
  removed = Beskar::BannedIp.cleanup_expired!
576
767
  Rails.logger.info "Cleaned up #{removed} expired IP bans"
577
-
768
+
578
769
  # Archive old security events (optional)
579
770
  Beskar::SecurityEvent.where('created_at < ?', 90.days.ago).delete_all
580
-
771
+
581
772
  # Generate security report (example)
582
773
  report = {
583
774
  active_bans: Beskar::BannedIp.active.count,
@@ -587,7 +778,7 @@ class SecurityCleanupJob < ApplicationJob
587
778
  created_at: 24.hours.ago..Time.current
588
779
  ).count
589
780
  }
590
-
781
+
591
782
  # Send to monitoring service
592
783
  Rails.logger.info "Security Report: #{report}"
593
784
  end
@@ -611,10 +802,7 @@ Every request passes through these security checks in order:
611
802
  **Features:**
612
803
  - **Early exit** - Banned IPs are blocked immediately for performance
613
804
  - **Whitelist bypass** - Trusted IPs bypass all blocking but activity is logged
614
- - **Auto-blocking** - Automatic IP banning after:
615
- - 10+ failed authentication attempts (authentication abuse)
616
- - 5+ rate limit violations in 1 hour (rate limit abuse)
617
- - 3+ WAF violations (configurable, vulnerability scanning)
805
+ - **Auto-blocking** - See [Automatic IP Banning Thresholds](#ip-blocking-and-banning) section for details
618
806
  - **Custom error pages** - Returns helpful 403/429 error responses
619
807
  - **Response headers** - Adds `X-Beskar-Blocked` and `X-Beskar-Rate-Limited` headers
620
808
  - **Graceful degradation** - Continues working if cache is unavailable
@@ -637,13 +825,18 @@ Security events are logged to the `beskar_security_events` table for analysis an
637
825
 
638
826
  | Category | Severity | Example Patterns | Risk Score |
639
827
  |----------|----------|------------------|------------|
640
- | WordPress Scans | High | `/wp-admin`, `/wp-login.php`, `/xmlrpc.php` | 80 |
828
+ | WordPress Scans | High | `/wp-admin`, `/wp-login.php`, `/wp-content/*.php` | 80 |
829
+ | WordPress Static Files | Low | `/wp-content/*.css`, `/wp-content/*.jpg` | 30 |
641
830
  | PHP Admin Panels | High | `/phpmyadmin`, `/admin.php`, `/phpinfo.php` | 80 |
642
831
  | Config Files | **Critical** | `/.env`, `/.git`, `/database.yml`, `/config.php` | **95** |
643
832
  | Path Traversal | **Critical** | `/../../../etc/passwd`, `%2e%2e/` | **95** |
644
833
  | Framework Debug | Medium | `/rails/info/routes`, `/__debug__`, `/telescope` | 60 |
645
834
  | CMS Detection | Medium | `/joomla`, `/drupal`, `/magento` | 60 |
646
835
  | Common Exploits | **Critical** | `/shell.php`, `/c99.php`, `/webshell` | **95** |
836
+ | UnknownFormat Exception | Medium | `/users/1.exe`, `/api/data.bat` | 60 |
837
+ | IP Spoofing Exception | **Critical** | Conflicting IP headers | **95** |
838
+ | Invalid MIME Type Exception | Medium | `GET "../../../../etc/passwd{{"` | 60 |
839
+ | RecordNotFound Exception | Low | `/admin/users/999999` | 30 |
647
840
 
648
841
  **Pattern matching is:**
649
842
  - Case-insensitive
@@ -658,21 +851,17 @@ Security events are logged to the `beskar_security_events` table for analysis an
658
851
  When first enabling WAF, use monitor-only mode to tune thresholds:
659
852
 
660
853
  ```ruby
661
- config.waf = {
662
- enabled: true,
663
- monitor_only: true, # Log but don't block
664
- create_security_events: true
665
- }
854
+ config.monitor_only = true # Log but don't block
855
+ config.waf[:enabled] = true
856
+ config.waf[:create_security_events] = true
666
857
  ```
667
858
 
668
859
  After reviewing logs for false positives, enable blocking:
669
860
 
670
861
  ```ruby
671
- config.waf = {
672
- enabled: true,
673
- monitor_only: false,
674
- auto_block: true
675
- }
862
+ config.monitor_only = false
863
+ config.waf[:enabled] = true
864
+ config.waf[:auto_block] = true
676
865
  ```
677
866
 
678
867
  ### 2. Whitelist Carefully
@@ -773,7 +962,7 @@ config.ip_whitelist = ["user.ip.address.here"]
773
962
  **Solution:** Enable monitor-only mode and review patterns:
774
963
 
775
964
  ```ruby
776
- config.waf[:monitor_only] = true
965
+ config.monitor_only = true # This is a global setting, not WAF-specific
777
966
 
778
967
  # Review what's being flagged
779
968
  Beskar::SecurityEvent.where(event_type: 'waf_violation').last(20).each do |event|
@@ -853,7 +1042,7 @@ $ bin/rails test
853
1042
 
854
1043
  ## Contributing
855
1044
 
856
- Bug reports and pull requests are welcome on GitHub at [https://github.com/prograis/beskar](https://github.com/prograils/beskar).
1045
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/prograis/beskar](https://github.com/prograils/beskar).
857
1046
 
858
1047
  ## License
859
1048
 
@@ -862,4 +1051,3 @@ The gem is available as open source under the terms of the [MIT License](https:/
862
1051
  ## Code of Conduct
863
1052
 
864
1053
  Just be nice to each other.
865
-