Files
honeydany/app/services/security.go
T

232 lines
5.5 KiB
Go

package services
import (
"bufio"
"context"
"fmt"
"net"
"strings"
"sync"
"time"
)
// SecurityConfig defines security limits for honeypot services
type SecurityConfig struct {
MaxInputLength int // Maximum input length per command
MaxConnDuration time.Duration // Maximum connection duration
MaxCommands int // Maximum commands per connection
RateLimitWindow time.Duration // Rate limiting window
MaxConnPerIP int // Maximum concurrent connections per IP
ReadTimeout time.Duration // Read timeout for each operation
WriteTimeout time.Duration // Write timeout for each operation
}
// DefaultSecurityConfig returns secure default configuration
func DefaultSecurityConfig() SecurityConfig {
return SecurityConfig{
MaxInputLength: 4096, // 4KB max input
MaxConnDuration: 5 * time.Minute,
MaxCommands: 100, // Max 100 commands per connection
RateLimitWindow: time.Minute,
MaxConnPerIP: 10, // Max 10 concurrent connections per IP
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Second,
}
}
// ConnectionTracker tracks connections per IP for rate limiting
type ConnectionTracker struct {
mu sync.RWMutex
conns map[string]int
}
func NewConnectionTracker() *ConnectionTracker {
return &ConnectionTracker{
conns: make(map[string]int),
}
}
func (ct *ConnectionTracker) AddConnection(ip string) bool {
ct.mu.Lock()
defer ct.mu.Unlock()
if ct.conns[ip] >= 10 { // Max 10 connections per IP
return false
}
ct.conns[ip]++
return true
}
func (ct *ConnectionTracker) RemoveConnection(ip string) {
ct.mu.Lock()
defer ct.mu.Unlock()
if ct.conns[ip] > 0 {
ct.conns[ip]--
if ct.conns[ip] == 0 {
delete(ct.conns, ip)
}
}
}
// SecureReader provides safe input reading with limits
type SecureReader struct {
scanner *bufio.Scanner
conn net.Conn
config SecurityConfig
commandCount int
startTime time.Time
}
func NewSecureReader(conn net.Conn, config SecurityConfig) *SecureReader {
scanner := bufio.NewScanner(conn)
// Set maximum token size to prevent buffer overflow
scanner.Buffer(make([]byte, 1024), config.MaxInputLength)
return &SecureReader{
scanner: scanner,
conn: conn,
config: config,
startTime: time.Now(),
}
}
func (sr *SecureReader) ReadLine() (string, error) {
// Check connection duration limit
if time.Since(sr.startTime) > sr.config.MaxConnDuration {
return "", fmt.Errorf("connection duration exceeded")
}
// Check command count limit
if sr.commandCount >= sr.config.MaxCommands {
return "", fmt.Errorf("command limit exceeded")
}
// Set read timeout
sr.conn.SetReadDeadline(time.Now().Add(sr.config.ReadTimeout))
if !sr.scanner.Scan() {
if err := sr.scanner.Err(); err != nil {
return "", err
}
return "", fmt.Errorf("connection closed")
}
line := strings.TrimSpace(sr.scanner.Text())
// Validate input length
if len(line) > sr.config.MaxInputLength {
return "", fmt.Errorf("input too long")
}
sr.commandCount++
return line, nil
}
// SecureWriter provides safe output writing with timeouts
type SecureWriter struct {
conn net.Conn
config SecurityConfig
}
func NewSecureWriter(conn net.Conn, config SecurityConfig) *SecureWriter {
return &SecureWriter{
conn: conn,
config: config,
}
}
func (sw *SecureWriter) Write(data []byte) error {
sw.conn.SetWriteDeadline(time.Now().Add(sw.config.WriteTimeout))
_, err := sw.conn.Write(data)
return err
}
func (sw *SecureWriter) WriteString(s string) error {
return sw.Write([]byte(s))
}
// InputSanitizer provides input sanitization utilities
type InputSanitizer struct{}
func NewInputSanitizer() *InputSanitizer {
return &InputSanitizer{}
}
// SanitizeString removes dangerous characters and limits length
func (is *InputSanitizer) SanitizeString(input string, maxLen int) string {
// Remove null bytes and control characters
cleaned := strings.Map(func(r rune) rune {
if r == 0 || (r < 32 && r != 9 && r != 10 && r != 13) {
return -1
}
return r
}, input)
// Limit length
if len(cleaned) > maxLen {
cleaned = cleaned[:maxLen]
}
return cleaned
}
// ValidateCommand checks if a command is safe to process
func (is *InputSanitizer) ValidateCommand(cmd string) bool {
// Block potentially dangerous commands
dangerous := []string{
"../", "..\\", "/etc/", "/proc/", "/sys/", "/dev/",
"system", "exec", "eval", "shell", "bash", "sh",
"cmd.exe", "powershell", "wget", "curl", "nc", "netcat",
}
cmdLower := strings.ToLower(cmd)
for _, danger := range dangerous {
if strings.Contains(cmdLower, danger) {
return false
}
}
return true
}
// SecureHandler wraps a handler with security protections
func SecureHandler(handler Handler, tracker *ConnectionTracker, config SecurityConfig) Handler {
return func(conn net.Conn) {
defer conn.Close()
// Extract IP address
remoteAddr := conn.RemoteAddr().String()
ip := remoteIP(remoteAddr)
// Check connection limits
if !tracker.AddConnection(ip) {
return // Too many connections from this IP
}
defer tracker.RemoveConnection(ip)
// Set overall connection deadline
conn.SetDeadline(time.Now().Add(config.MaxConnDuration))
// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), config.MaxConnDuration)
defer cancel()
// Run handler with context
done := make(chan struct{})
go func() {
defer close(done)
handler(conn)
}()
select {
case <-done:
// Handler completed normally
case <-ctx.Done():
// Timeout exceeded, force close
conn.Close()
}
}
}