Files
dip-ids/internal/analyzer/bruteforce.go
T
2026-04-12 22:26:26 +04:00

110 lines
2.6 KiB
Go

package analyzer
import (
"fmt"
"sync"
"time"
"dip-ids/internal/config"
"dip-ids/internal/storage"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
// bruteForceDetector обнаруживает перебор паролей (brute-force).
// Алгоритм: отслеживает количество SYN-пакетов от одного источника
// к одному порту назначения за скользящее временное окно.
type bruteForceDetector struct {
mu sync.Mutex
cfg config.BruteForceConfig
ports map[int32]struct{}
// ключ: "srcIP:dstIP:dstPort"
tracker map[string][]time.Time
alerted map[string]time.Time
}
func NewBruteForceDetector(cfg config.BruteForceConfig) Detector {
ports := make(map[int32]struct{}, len(cfg.Ports))
for _, p := range cfg.Ports {
ports[p] = struct{}{}
}
return &bruteForceDetector{
cfg: cfg,
ports: ports,
tracker: make(map[string][]time.Time),
alerted: make(map[string]time.Time),
}
}
func (d *bruteForceDetector) Name() string { return "brute_force" }
func (d *bruteForceDetector) Analyze(pkt gopacket.Packet) []storage.Alert {
if !d.cfg.Enabled {
return nil
}
netLayer := pkt.NetworkLayer()
tcpLayer := pkt.Layer(layers.LayerTypeTCP)
if netLayer == nil || tcpLayer == nil {
return nil
}
tcp := tcpLayer.(*layers.TCP)
if !tcp.SYN || tcp.ACK {
return nil
}
dstPort := int32(tcp.DstPort)
if _, monitored := d.ports[dstPort]; !monitored {
return nil
}
srcIP := netLayer.NetworkFlow().Src().String()
dstIP := netLayer.NetworkFlow().Dst().String()
now := pkt.Metadata().Timestamp
if now.IsZero() {
now = time.Now()
}
key := fmt.Sprintf("%s:%s:%d", srcIP, dstIP, dstPort)
d.mu.Lock()
defer d.mu.Unlock()
window := time.Duration(d.cfg.WindowSeconds) * time.Second
cutoff := now.Add(-window)
d.tracker[key] = append(d.tracker[key], now)
d.tracker[key] = filterTimes(d.tracker[key], cutoff)
count := len(d.tracker[key])
if count < d.cfg.Threshold {
return nil
}
if last, ok := d.alerted[key]; ok && now.Sub(last) < 30*time.Second {
return nil
}
d.alerted[key] = now
return []storage.Alert{{
CreatedAt: now,
Type: "brute_force",
Severity: d.cfg.Severity,
SrcIP: srcIP,
DstIP: dstIP,
DstPort: uint16(dstPort),
Protocol: "TCP",
Description: fmt.Sprintf("Brute-force detected on port %d: %d connection attempts in %ds", dstPort, count, d.cfg.WindowSeconds),
Count: count,
}}
}
func (d *bruteForceDetector) Reset() {
d.mu.Lock()
defer d.mu.Unlock()
d.tracker = make(map[string][]time.Time)
d.alerted = make(map[string]time.Time)
}