beskar 0.0.1 → 0.0.2

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.
data/README.md CHANGED
@@ -4,9 +4,18 @@
4
4
 
5
5
  ## Features
6
6
 
7
- - **Web Application Firewall (WAF):** Real-time protection against common attack vectors like SQL Injection (SQLi) and Cross-Site Scripting (XSS).
7
+ - **Devise Integration:** Seamless integration with Devise authentication for automatic login tracking and security analysis.
8
+ - **Risk-Based Account Locking:** Automatically locks accounts when authentication risk scores exceed configurable thresholds, preventing compromised account access.
9
+ - **Smart Rate Limiting:** Distributed rate limiting using Rails.cache with IP-based and account-based throttling with exponential backoff.
10
+ - **Brute Force Detection:** Advanced pattern recognition to detect single account attacks vs credential stuffing attempts, with automatic IP banning.
11
+ - **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
+ - **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.
14
+ - **Security Event Tracking:** Comprehensive logging of authentication events with risk scoring and metadata extraction.
15
+ - **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
+ - **Geographic Anomaly Detection:** Haversine-based impossible travel detection and location-based risk assessment.
8
17
  - **Advanced Bot Detection:** Multi-layered defense using JavaScript challenges and invisible honeypots to filter out malicious bots while allowing legitimate ones.
9
- - **Account Takeover (ATO) Prevention:** Actively monitors and blocks brute-force attacks, credential stuffing, and impossible travel anomalies.
18
+ - **Modular Architecture:** Devise-specific code is isolated in separate services for maintainability and extensibility.
10
19
  - **Rails-Native Architecture:** Built as a mountable `Rails::Engine`, it leverages `ActiveJob` and `Rails.cache` for high performance and low overhead.
11
20
  - **Real-Time Dashboard (Coming Soon):** A mountable dashboard to visualize security events and monitor threats as they happen.
12
21
 
@@ -16,52 +25,821 @@ Add this line to your application's Gemfile:
16
25
 
17
26
  ```ruby
18
27
  gem 'beskar'
19
- ````
28
+ ```
20
29
 
21
30
  And then execute:
22
31
 
23
32
  ```bash
24
- $ bundle install
33
+ bundle install
25
34
  ```
26
35
 
27
- Next, run the installation generator. This will copy the necessary migrations and create an initializer file.
36
+ Run the installation task to set up Beskar:
28
37
 
29
38
  ```bash
30
- $ rails g beskar:install
39
+ bin/rails beskar:install
31
40
  ```
32
41
 
33
- Finally, run the database migrations:
42
+ This will:
43
+ - Copy all necessary migrations to your application
44
+ - Create `config/initializers/beskar.rb` with sensible defaults
45
+ - Display next steps for completing the setup
46
+
47
+ Then run the database migrations:
34
48
 
35
49
  ```bash
36
- $ rails db:migrate
50
+ bin/rails db:migrate
51
+ ```
52
+
53
+ ### Quick Start
54
+
55
+ By default, Beskar enables the **Web Application Firewall (WAF) in monitor-only mode**. This means:
56
+ - ✅ Vulnerability scans are detected and logged
57
+ - ✅ Security events are created for analysis
58
+ - ⚠️ No requests are blocked yet (safe to enable in production)
59
+
60
+ After monitoring for 24-48 hours, review the logs and disable monitor-only mode to enable active blocking:
61
+
62
+ ```ruby
63
+ # config/initializers/beskar.rb
64
+ config.waf = {
65
+ enabled: true,
66
+ monitor_only: false, # Change this to enable blocking
67
+ # ... rest of configuration
68
+ }
69
+ ```
70
+
71
+ ### Add to Your User Model
72
+
73
+ Include the `SecurityTrackable` concern in your Devise user model:
74
+
75
+ ```ruby
76
+ # app/models/user.rb
77
+ class User < ApplicationRecord
78
+ include Beskar::SecurityTrackable
79
+
80
+ devise :database_authenticatable, :registerable,
81
+ :recoverable, :rememberable, :validatable
82
+ # ... other Devise modules
83
+ end
37
84
  ```
38
85
 
39
86
  ## Configuration
40
87
 
41
- You can configure Rails Security Shield in the initializer file created by the installer:
88
+ You can configure Beskar in the initializer file created by the installer:
42
89
 
43
90
  ```ruby
44
91
  # config/initializers/beskar.rb
45
- RailsSecurityShield.configure do |config|
92
+ Beskar.configure do |config|
93
+ # === Security Tracking ===
94
+ # Controls what security events are tracked and analyzed
95
+ config.security_tracking = {
96
+ enabled: true, # Master switch - disables all tracking when false
97
+ track_successful_logins: true, # Track successful authentication events
98
+ track_failed_logins: true, # Track failed authentication attempts
99
+ auto_analyze_patterns: true # Enable automatic pattern analysis for threats
100
+ }
101
+
102
+ # === Rate Limiting ===
103
+ config.rate_limiting = {
104
+ ip_attempts: {
105
+ limit: 10, # Max attempts per IP
106
+ period: 1.hour, # Time window
107
+ exponential_backoff: true # Enable exponential backoff
108
+ },
109
+ account_attempts: {
110
+ limit: 5, # Max attempts per account
111
+ period: 15.minutes, # Time window
112
+ exponential_backoff: true
113
+ },
114
+ global_attempts: {
115
+ limit: 100, # System-wide limit
116
+ period: 1.minute,
117
+ exponential_backoff: false
118
+ }
119
+ }
120
+
121
+ # === 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
+ ]
130
+
46
131
  # === Web Application Firewall (WAF) ===
47
- # Enable or disable the WAF middleware. Defaults to false.
48
- config.enable_waf = true
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
+ }
49
143
 
50
- # === Account Protection ===
51
- # Set the class name of your user model.
52
- # This is used for tracking security events related to users.
53
- config.user_class = 'User'
144
+ # === Risk-Based Account Locking ===
145
+ # Automatically lock accounts when authentication risk score exceeds threshold
146
+ config.risk_based_locking = {
147
+ enabled: false, # Master switch for risk-based locking
148
+ risk_threshold: 75, # Lock account if risk score >= this value (0-100)
149
+ lock_strategy: :devise_lockable, # Strategy: :devise_lockable, :custom, :none
150
+ auto_unlock_time: 1.hour, # Time until automatic unlock (if supported)
151
+ notify_user: true, # Send notification on lock (future feature)
152
+ log_lock_events: true # Create security event for locks
153
+ }
54
154
 
55
- # More configuration options will be available here.
155
+ # === IP Geolocation ===
156
+ # Configure IP geolocation for enhanced risk assessment
157
+ # Note: You must provide your own MaxMind GeoLite2-City database due to licensing
158
+ # Download from: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
159
+ config.geolocation = {
160
+ provider: :maxmind, # Provider: :maxmind or :mock (for testing)
161
+ maxmind_city_db_path: Rails.root.join('db', 'geoip', 'GeoLite2-City.mmdb').to_s,
162
+ cache_ttl: 4.hours # How long to cache geolocation results
163
+ }
56
164
  end
165
+
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.
57
175
  ```
58
176
 
59
177
  ## Usage
60
178
 
61
- Once installed and configured, Rails Security Shield works automatically. Its middleware is injected into the Rails request stack to analyze incoming traffic and block threats before they reach your application.
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
+ ```
192
+
193
+ ### Risk-Based Account Locking (with Devise Lockable)
194
+
195
+ Beskar can automatically lock user accounts when the calculated risk score exceeds a configured threshold. This prevents compromised accounts from being accessed even after successful authentication.
196
+
197
+ **Setup with Devise Lockable:**
198
+
199
+ 1. Enable the `:lockable` module in your User model:
200
+
201
+ ```ruby
202
+ class User < ApplicationRecord
203
+ devise :database_authenticatable, :registerable,
204
+ :recoverable, :rememberable, :validatable,
205
+ :lockable # Add this for risk-based locking
206
+
207
+ include Beskar::Models::SecurityTrackable
208
+ end
209
+ ```
210
+
211
+ 2. Generate and run the migration to add lockable columns:
212
+
213
+ ```bash
214
+ rails generate devise User # This will add lockable columns if not present
215
+ # Or manually add:
216
+ # - failed_attempts (integer)
217
+ # - unlock_token (string)
218
+ # - locked_at (datetime)
219
+ rails db:migrate
220
+ ```
221
+
222
+ 3. Enable risk-based locking in your initializer:
223
+
224
+ ```ruby
225
+ # config/initializers/beskar.rb
226
+ Beskar.configure do |config|
227
+ config.risk_based_locking = {
228
+ enabled: true, # Enable the feature
229
+ risk_threshold: 75, # Lock when risk >= 75
230
+ lock_strategy: :devise_lockable, # Use Devise's lockable module
231
+ auto_unlock_time: 1.hour, # Automatic unlock after 1 hour
232
+ notify_user: true, # Log notification intent
233
+ log_lock_events: true # Create security events
234
+ }
235
+ end
236
+ ```
237
+
238
+ **How it works:**
239
+
240
+ - After each successful authentication, Beskar calculates a risk score (0-100) based on:
241
+ - Geographic anomalies (impossible travel, country changes)
242
+ - Device fingerprints (suspicious user agents, bot signatures)
243
+ - Login patterns (velocity, time of day, recent failures)
244
+ - IP reputation and geolocation risk
245
+
246
+ - **Adaptive Learning:** The system learns from user behavior:
247
+ - After 2+ successful logins from an IP, that location becomes "established"
248
+ - If a user unlocks and logs in successfully, that pattern is trusted
249
+ - Risk scores are reduced to 30% for established patterns (capped at 25)
250
+ - This prevents repeated locks after users validate their login context
251
+
252
+ - If the risk score meets or exceeds the configured threshold, the account is automatically locked
253
+ - The user session is terminated immediately to prevent access
254
+ - A security event is logged with the lock reason and risk details (always logged for audit trail)
255
+ - The account remains locked until manually unlocked or the auto-unlock time expires (if supported)
256
+
257
+ **Example Adaptive Flow:**
258
+ 1. User travels to new location → High risk (85) → Account locked
259
+ 2. User unlocks account → Validates legitimacy
260
+ 3. User logs in from same location → Pattern established → Risk reduced to 25 → Login succeeds ✅
261
+ 4. Future logins from this location → Normal risk → No more locks
262
+
263
+ See `ADAPTIVE_LEARNING.md` for detailed documentation.
264
+
265
+ **Lock Reasons:**
266
+
267
+ The system identifies specific reasons for locking:
268
+ - `:impossible_travel` - Login from location requiring impossible travel speed
269
+ - `:suspicious_device` - Bot signature or suspicious user agent detected
270
+ - `:geographic_anomaly` - Country change or high-risk location
271
+ - `:high_risk_authentication` - General high-risk authentication pattern
272
+
273
+ **Manual Lock/Unlock Operations:**
274
+
275
+ ```ruby
276
+ # Manually lock an account based on risk
277
+ locker = Beskar::Services::AccountLocker.new(
278
+ user,
279
+ risk_score: 85,
280
+ reason: :suspicious_device,
281
+ metadata: { ip_address: request.ip }
282
+ )
283
+
284
+ if locker.should_lock?
285
+ locker.lock! # Lock the account
286
+ end
287
+
288
+ # Check if account is locked
289
+ locker.locked? # => true/false
290
+
291
+ # Manually unlock
292
+ locker.unlock!
293
+ ```
294
+
295
+ ### Security Event Tracking
296
+
297
+ Beskar automatically tracks login attempts and creates security events with rich metadata:
298
+
299
+ ```ruby
300
+ # Check recent failed attempts for a user
301
+ user.recent_failed_attempts(within: 1.hour)
302
+
303
+ # Check if user has suspicious login patterns
304
+ user.suspicious_login_pattern?
305
+
306
+ # Get recent successful logins
307
+ user.recent_successful_logins(within: 24.hours)
308
+
309
+ # Access security events
310
+ user.security_events.login_failures.recent
311
+ ```
312
+
313
+ ### Rate Limiting
314
+
315
+ Check if a request should be rate limited:
316
+
317
+ ```ruby
318
+ # In a controller or middleware
319
+ if Beskar.rate_limited?(request, current_user)
320
+ render json: { error: 'Rate limit exceeded' }, status: 429
321
+ return
322
+ end
323
+
324
+ # Manual rate limiting check
325
+ rate_limiter = Beskar::Services::RateLimiter.new(request.ip, current_user)
326
+ unless rate_limiter.allowed?
327
+ # Handle rate limiting
328
+ time_until_reset = rate_limiter.time_until_reset
329
+ end
330
+ ```
331
+
332
+ ### Security Events Analysis
333
+
334
+ Security events are automatically created and include:
335
+
336
+ - **Event Type**: `login_success`, `login_failure`
337
+ - **IP Address**: Client IP with proxy detection
338
+ - **User Agent**: Browser and device information
339
+ - **Risk Score**: 0-100 based on various factors
340
+ - **Metadata**: Device info, geolocation, timestamps
341
+ - **Attack Patterns**: Detection of brute force, credential stuffing, etc.
342
+
343
+ ### Attack Pattern Detection
344
+
345
+ Beskar can identify different types of attacks:
346
+
347
+ ```ruby
348
+ rate_limiter = Beskar::Services::RateLimiter.new(ip_address, user)
349
+ attack_type = rate_limiter.attack_pattern_type
350
+
351
+ case attack_type
352
+ when :brute_force_single_account
353
+ # Single IP attacking one account
354
+ when :distributed_single_account
355
+ # Multiple IPs attacking one account
356
+ when :single_ip_multiple_accounts
357
+ # One IP attacking multiple accounts (credential stuffing)
358
+ when :mixed_attack_pattern
359
+ # Complex attack pattern
360
+ end
361
+ ```
362
+
363
+ ### IP Whitelisting
364
+
365
+ Whitelist trusted IPs to bypass all security blocking while maintaining full audit logs:
366
+
367
+ ```ruby
368
+ # In config/initializers/beskar.rb
369
+ Beskar.configure do |config|
370
+ config.ip_whitelist = [
371
+ "203.0.113.0/24", # Office network (CIDR notation)
372
+ "198.51.100.50", # VPN gateway (single IP)
373
+ "2001:db8::1" # IPv6 address
374
+ ]
375
+ end
376
+ ```
377
+
378
+ **How it works:**
379
+ - Whitelisted IPs bypass **all blocking** (banned IPs, rate limits, WAF violations)
380
+ - All requests from whitelisted IPs are **still logged** for audit purposes
381
+ - Supports individual IPs and CIDR notation (IPv4 and IPv6)
382
+ - Configuration is validated on startup
383
+ - Efficient caching for high-performance checks
384
+
385
+ **Check if an IP is whitelisted:**
386
+ ```ruby
387
+ if Beskar::Services::IpWhitelist.whitelisted?(request.ip)
388
+ # IP is trusted - allow but log activity
389
+ end
390
+
391
+ # Clear whitelist cache after config changes
392
+ Beskar::Services::IpWhitelist.clear_cache!
393
+ ```
394
+
395
+ ### Web Application Firewall (WAF)
396
+
397
+ Beskar's WAF detects and blocks vulnerability scanning attempts across 7 attack categories:
398
+
399
+ **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`
407
+
408
+ **Configuration Examples:**
409
+
410
+ ```ruby
411
+ # Production - Aggressive protection
412
+ 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
421
+ }
422
+ end
423
+
424
+ # Development - Monitor only
425
+ Beskar.configure do |config|
426
+ config.waf = {
427
+ enabled: true,
428
+ monitor_only: true, # Log but never block
429
+ create_security_events: true
430
+ }
431
+
432
+ config.ip_whitelist = ["127.0.0.1", "::1"] # Whitelist localhost
433
+ end
434
+ ```
435
+
436
+ **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)
442
+
443
+ **Check WAF status:**
444
+ ```ruby
445
+ # Check if WAF detected threats
446
+ violation_count = Beskar::Services::Waf.get_violation_count(ip_address)
447
+
448
+ # Reset violations (admin action)
449
+ Beskar::Services::Waf.reset_violations(ip_address)
450
+
451
+ # Analyze a request without blocking
452
+ waf_analysis = Beskar::Services::Waf.analyze_request(request)
453
+ if waf_analysis
454
+ puts "Detected: #{waf_analysis[:patterns].map { |p| p[:description] }}"
455
+ puts "Severity: #{waf_analysis[:highest_severity]}"
456
+ puts "Risk Score: #{waf_analysis[:risk_score]}"
457
+ end
458
+ ```
459
+
460
+ ### IP Blocking and Banning
461
+
462
+ Beskar uses a hybrid cache + database blocking system that persists across application restarts.
463
+
464
+ **Automatic IP Banning:**
465
+
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)
470
+
471
+ **Manual IP Management:**
472
+
473
+ ```ruby
474
+ # Ban an IP address
475
+ Beskar::BannedIp.ban!(
476
+ "203.0.113.50",
477
+ reason: "manual_block",
478
+ duration: 24.hours,
479
+ details: "Suspicious activity reported by admin",
480
+ metadata: { reporter: "admin@example.com", ticket: "#12345" }
481
+ )
482
+
483
+ # Permanent ban
484
+ Beskar::BannedIp.ban!(
485
+ "203.0.113.51",
486
+ reason: "confirmed_attack",
487
+ permanent: true,
488
+ details: "Confirmed malicious actor"
489
+ )
490
+
491
+ # Check if IP is banned
492
+ Beskar::BannedIp.banned?("203.0.113.50") # => true
493
+
494
+ # Unban an IP
495
+ Beskar::BannedIp.unban!("203.0.113.50")
496
+
497
+ # Extend existing ban
498
+ ban = Beskar::BannedIp.find_by(ip_address: "203.0.113.50")
499
+ ban.extend_ban!(12.hours) # Add 12 hours to current expiry
500
+ ```
501
+
502
+ **Query banned IPs:**
503
+
504
+ ```ruby
505
+ # Get all active bans
506
+ Beskar::BannedIp.active
507
+
508
+ # Get permanent bans
509
+ Beskar::BannedIp.permanent
510
+
511
+ # Get expired bans (not enforced but in database)
512
+ Beskar::BannedIp.expired
513
+
514
+ # Find bans by reason
515
+ Beskar::BannedIp.where(reason: 'waf_violation')
516
+ Beskar::BannedIp.where(reason: 'authentication_abuse')
517
+ Beskar::BannedIp.where(reason: 'rate_limit_abuse')
518
+
519
+ # Cleanup expired bans from database
520
+ removed_count = Beskar::BannedIp.cleanup_expired!
521
+ ```
522
+
523
+ **Preload cache on startup:**
524
+
525
+ The cache is automatically preloaded when your app starts, but you can manually trigger it:
526
+
527
+ ```ruby
528
+ # In config/initializers/beskar.rb (optional - happens automatically)
529
+ Rails.application.config.after_initialize do
530
+ Beskar::BannedIp.preload_cache!
531
+ end
532
+ ```
533
+
534
+ ### Security Events and Monitoring
535
+
536
+ **Query WAF violations:**
537
+
538
+ ```ruby
539
+ # Recent WAF violations
540
+ waf_events = Beskar::SecurityEvent
541
+ .where(event_type: 'waf_violation')
542
+ .where('created_at > ?', 24.hours.ago)
543
+ .order(created_at: :desc)
544
+
545
+ # High-risk WAF events
546
+ high_risk = Beskar::SecurityEvent
547
+ .where(event_type: 'waf_violation')
548
+ .where('risk_score >= ?', 80)
549
+ .includes(:user)
550
+
551
+ # Group by IP to find repeat offenders
552
+ repeat_offenders = Beskar::SecurityEvent
553
+ .where(event_type: 'waf_violation')
554
+ .where('created_at > ?', 7.days.ago)
555
+ .group(:ip_address)
556
+ .having('COUNT(*) >= ?', 5)
557
+ .count
558
+
559
+ # WAF violations by pattern type
560
+ waf_events.each do |event|
561
+ patterns = event.metadata['waf_analysis']['patterns']
562
+ patterns.each do |pattern|
563
+ puts "#{event.ip_address}: #{pattern['category']} - #{pattern['description']}"
564
+ end
565
+ end
566
+ ```
567
+
568
+ **Scheduled maintenance:**
569
+
570
+ ```ruby
571
+ # In a background job (e.g., daily)
572
+ class SecurityCleanupJob < ApplicationJob
573
+ def perform
574
+ # Remove expired bans from database
575
+ removed = Beskar::BannedIp.cleanup_expired!
576
+ Rails.logger.info "Cleaned up #{removed} expired IP bans"
577
+
578
+ # Archive old security events (optional)
579
+ Beskar::SecurityEvent.where('created_at < ?', 90.days.ago).delete_all
580
+
581
+ # Generate security report (example)
582
+ report = {
583
+ active_bans: Beskar::BannedIp.active.count,
584
+ permanent_bans: Beskar::BannedIp.permanent.count,
585
+ waf_violations_today: Beskar::SecurityEvent.where(
586
+ event_type: 'waf_violation',
587
+ created_at: 24.hours.ago..Time.current
588
+ ).count
589
+ }
590
+
591
+ # Send to monitoring service
592
+ Rails.logger.info "Security Report: #{report}"
593
+ end
594
+ end
595
+ ```
596
+
597
+ ### Middleware Integration
598
+
599
+ Beskar automatically injects its middleware (`Beskar::Middleware::RequestAnalyzer`) into the Rails stack to provide comprehensive request-level protection.
600
+
601
+ **Request Processing Order:**
602
+
603
+ Every request passes through these security checks in order:
604
+
605
+ 1. **Whitelist Check** - Determine if IP is whitelisted (bypasses blocking but still logs)
606
+ 2. **Banned IP Check** - Block immediately if IP is banned (403 Forbidden)
607
+ 3. **Rate Limiting** - Check rate limits (429 Too Many Requests if exceeded)
608
+ 4. **WAF Analysis** - Scan for vulnerability patterns (403 Forbidden if detected and threshold met)
609
+ 5. **Request Processing** - Continue to application if all checks pass
610
+
611
+ **Features:**
612
+ - **Early exit** - Banned IPs are blocked immediately for performance
613
+ - **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)
618
+ - **Custom error pages** - Returns helpful 403/429 error responses
619
+ - **Response headers** - Adds `X-Beskar-Blocked` and `X-Beskar-Rate-Limited` headers
620
+ - **Graceful degradation** - Continues working if cache is unavailable
621
+
622
+ **Middleware Logs:**
623
+
624
+ The middleware generates structured log messages for monitoring:
625
+
626
+ ```
627
+ [Beskar::Middleware] Blocked request from banned IP: 203.0.113.50
628
+ [Beskar::Middleware] Rate limit exceeded for IP: 203.0.113.51
629
+ [Beskar::Middleware] WAF violation from whitelisted IP 192.168.1.100 (not blocking): WordPress vulnerability scan
630
+ [Beskar::Middleware] 🔒 Auto-blocked IP 203.0.113.52 after 3 WAF violations (duration: 1 hours)
631
+ [Beskar::Middleware] 🔒 Auto-blocked IP 203.0.113.53 for authentication brute force (15 failures)
632
+ ```
62
633
 
63
634
  Security events are logged to the `beskar_security_events` table for analysis and will be visualized in the forthcoming security dashboard.
64
635
 
636
+ ## WAF Pattern Reference
637
+
638
+ | Category | Severity | Example Patterns | Risk Score |
639
+ |----------|----------|------------------|------------|
640
+ | WordPress Scans | High | `/wp-admin`, `/wp-login.php`, `/xmlrpc.php` | 80 |
641
+ | PHP Admin Panels | High | `/phpmyadmin`, `/admin.php`, `/phpinfo.php` | 80 |
642
+ | Config Files | **Critical** | `/.env`, `/.git`, `/database.yml`, `/config.php` | **95** |
643
+ | Path Traversal | **Critical** | `/../../../etc/passwd`, `%2e%2e/` | **95** |
644
+ | Framework Debug | Medium | `/rails/info/routes`, `/__debug__`, `/telescope` | 60 |
645
+ | CMS Detection | Medium | `/joomla`, `/drupal`, `/magento` | 60 |
646
+ | Common Exploits | **Critical** | `/shell.php`, `/c99.php`, `/webshell` | **95** |
647
+
648
+ **Pattern matching is:**
649
+ - Case-insensitive
650
+ - Works on full path including query strings
651
+ - Detects URL-encoded variants
652
+ - Can match multiple patterns per request
653
+
654
+ ## Security Best Practices
655
+
656
+ ### 1. Start with Monitor Mode
657
+
658
+ When first enabling WAF, use monitor-only mode to tune thresholds:
659
+
660
+ ```ruby
661
+ config.waf = {
662
+ enabled: true,
663
+ monitor_only: true, # Log but don't block
664
+ create_security_events: true
665
+ }
666
+ ```
667
+
668
+ After reviewing logs for false positives, enable blocking:
669
+
670
+ ```ruby
671
+ config.waf = {
672
+ enabled: true,
673
+ monitor_only: false,
674
+ auto_block: true
675
+ }
676
+ ```
677
+
678
+ ### 2. Whitelist Carefully
679
+
680
+ Only whitelist truly trusted IPs:
681
+
682
+ ```ruby
683
+ # ✅ Good - Documented, legitimate sources
684
+ config.ip_whitelist = [
685
+ "203.0.113.0/24", # Office network - IT approved
686
+ "198.51.100.50" # VPN gateway - documented in wiki
687
+ ]
688
+
689
+ # ❌ Bad - Whitelisting unknown IPs
690
+ config.ip_whitelist = ["0.0.0.0/0"] # Never do this!
691
+ ```
692
+
693
+ ### 3. Regular Maintenance
694
+
695
+ Set up a scheduled job to clean up old data:
696
+
697
+ ```ruby
698
+ # Schedule daily via cron or Sidekiq
699
+ SecurityCleanupJob.perform_later
700
+
701
+ # Or in initializer for quick cleanup on restart
702
+ Rails.application.config.after_initialize do
703
+ Beskar::BannedIp.cleanup_expired! if Rails.env.production?
704
+ end
705
+ ```
706
+
707
+ ### 4. Monitor Security Events
708
+
709
+ Set up alerts for high-risk events:
710
+
711
+ ```ruby
712
+ # Example monitoring
713
+ high_risk_count = Beskar::SecurityEvent
714
+ .where('created_at > ?', 1.hour.ago)
715
+ .where('risk_score >= ?', 80)
716
+ .count
717
+
718
+ alert_service.notify if high_risk_count > 10
719
+ ```
720
+
721
+ ### 5. Document Whitelist Changes
722
+
723
+ Keep a record of why each IP is whitelisted:
724
+
725
+ ```ruby
726
+ # config/initializers/beskar.rb
727
+ config.ip_whitelist = [
728
+ "203.0.113.0/24", # Office HQ network (added 2024-01-15, ticket #1234)
729
+ "198.51.100.50", # Partner API server (added 2024-02-01, contract #5678)
730
+ "192.0.2.10" # Security scanner (added 2024-03-01, vendor: SecurityCo)
731
+ ]
732
+ ```
733
+
734
+ ### 6. Test WAF in Staging
735
+
736
+ Before deploying to production, test WAF rules in staging to catch false positives.
737
+
738
+ ### 7. Review Ban Reasons
739
+
740
+ Periodically review banned IPs to ensure blocking is working correctly:
741
+
742
+ ```ruby
743
+ # Check recent auto-bans
744
+ recent_bans = Beskar::BannedIp
745
+ .where('created_at > ?', 7.days.ago)
746
+ .group(:reason)
747
+ .count
748
+
749
+ # Review specific ban details
750
+ waf_bans = Beskar::BannedIp.where(reason: 'waf_violation')
751
+ waf_bans.each do |ban|
752
+ puts "#{ban.ip_address}: #{ban.details} (violations: #{ban.violation_count})"
753
+ end
754
+ ```
755
+
756
+ ## Troubleshooting
757
+
758
+ ### Issue: Legitimate users being blocked
759
+
760
+ **Solution:** Add their IP to whitelist or reduce WAF `block_threshold`:
761
+
762
+ ```ruby
763
+ config.waf[:block_threshold] = 5 # Increase from default 3
764
+ ```
765
+
766
+ Or whitelist specific IPs:
767
+ ```ruby
768
+ config.ip_whitelist = ["user.ip.address.here"]
769
+ ```
770
+
771
+ ### Issue: Too many false positives
772
+
773
+ **Solution:** Enable monitor-only mode and review patterns:
774
+
775
+ ```ruby
776
+ config.waf[:monitor_only] = true
777
+
778
+ # Review what's being flagged
779
+ Beskar::SecurityEvent.where(event_type: 'waf_violation').last(20).each do |event|
780
+ puts "Path: #{event.metadata['request_path']}"
781
+ puts "Patterns: #{event.metadata['waf_analysis']['patterns']}"
782
+ end
783
+ ```
784
+
785
+ ### Issue: Banned IPs persist after restart
786
+
787
+ **Solution:** This is intentional (database persistence). To unban:
788
+
789
+ ```ruby
790
+ Beskar::BannedIp.unban!("ip.address.here")
791
+
792
+ # Or unban all expired
793
+ Beskar::BannedIp.cleanup_expired!
794
+ ```
795
+
796
+ ### Issue: Performance concerns
797
+
798
+ **Solution:** Beskar uses cache-first architecture. Ensure cache is configured:
799
+
800
+ ```ruby
801
+ # config/environments/production.rb
802
+ config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }
803
+ ```
804
+
805
+ Check cache health:
806
+ ```ruby
807
+ Rails.cache.read("test_key") # Should work
808
+ Beskar::BannedIp.preload_cache! # Reload from database if needed
809
+ ```
810
+
811
+ ## Migration from Previous Versions
812
+
813
+ If upgrading from a version without WAF/IP blocking features:
814
+
815
+ ```bash
816
+ # Run new migrations
817
+ rails db:migrate
818
+
819
+ # Preload cache with existing bans (if any)
820
+ rails runner "Beskar::BannedIp.preload_cache!"
821
+
822
+ # Test in development first
823
+ RAILS_ENV=development rails server
824
+
825
+ # Review logs for any issues
826
+ tail -f log/development.log | grep Beskar
827
+ ```
828
+
829
+ ## Performance Characteristics
830
+
831
+ - **Whitelist check**: O(n) where n = whitelist size, cached, < 1ms
832
+ - **Banned IP check**: O(1) cache lookup, < 1ms
833
+ - **Rate limit check**: O(1) cache lookup, < 1ms
834
+ - **WAF analysis**: O(m) where m = number of patterns, < 5ms
835
+ - **Total middleware overhead**: Typically < 10ms per request
836
+
837
+ **Scalability:**
838
+ - Handles 1000s of requests/second
839
+ - Cache-first architecture minimizes database queries
840
+ - Efficient pattern matching with compiled regexes
841
+ - Parallel test execution: 352 tests run in < 3 seconds
842
+
65
843
  ## Development
66
844
 
67
845
  After checking out the repo, run `bundle install` to install dependencies. The gem contains a dummy Rails application in `test/dummy` for development and testing.