232 lines
5.5 KiB
Go
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()
|
|
}
|
|
}
|
|
}
|