# Security

> Implement security best practices for voice agents including authentication, data protection, and compliance.

Source: https://edesy.in/docs/voice-agent/advanced/security

Build secure voice agents with proper authentication, data protection, and compliance with industry standards.

## Security Overview

```
┌────────────────────────────────────────────────────────────────┐
│                    Security Layers                              │
│                                                                │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐            │
│  │   Network   │  │   Data      │  │   Access    │            │
│  │   Security  │  │   Security  │  │   Control   │            │
│  ├─────────────┤  ├─────────────┤  ├─────────────┤            │
│  │ TLS 1.3     │  │ Encryption  │  │ API Keys    │            │
│  │ WSS         │  │ PII Masking │  │ RBAC        │            │
│  │ Firewall    │  │ Retention   │  │ Audit Logs  │            │
│  └─────────────┘  └─────────────┘  └─────────────┘            │
│                                                                │
└────────────────────────────────────────────────────────────────┘
```

## Authentication

### API Key Authentication

```go
type APIKeyAuth struct {
    keys     map[string]*APIKey
    rateLimit *RateLimiter
}

type APIKey struct {
    Key         string
    WorkspaceID string
    Permissions []string
    RateLimit   int
    CreatedAt   time.Time
    ExpiresAt   *time.Time
}

func (a *APIKeyAuth) Authenticate(key string) (*APIKey, error) {
    // Hash the key for comparison
    hashedKey := hashKey(key)

    apiKey, ok := a.keys[hashedKey]
    if !ok {
        return nil, ErrInvalidAPIKey
    }

    // Check expiration
    if apiKey.ExpiresAt != nil && time.Now().After(*apiKey.ExpiresAt) {
        return nil, ErrExpiredAPIKey
    }

    return apiKey, nil
}

func hashKey(key string) string {
    h := sha256.New()
    h.Write([]byte(key))
    return hex.EncodeToString(h.Sum(nil))
}
```

### Webhook Signature Verification

```go
func VerifyWebhookSignature(payload []byte, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(payload)
    expectedSignature := hex.EncodeToString(mac.Sum(nil))

    return hmac.Equal([]byte(signature), []byte(expectedSignature))
}

func (h *WebhookHandler) Handle(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    signature := r.Header.Get("X-Edesy-Signature")
    timestamp := r.Header.Get("X-Edesy-Timestamp")

    // Verify timestamp to prevent replay attacks
    ts, _ := strconv.ParseInt(timestamp, 10, 64)
    if time.Now().Unix()-ts > 300 { // 5 minute window
        http.Error(w, "Expired signature", http.StatusUnauthorized)
        return
    }

    // Verify signature
    signedPayload := fmt.Sprintf("%s.%s", timestamp, string(body))
    if !VerifyWebhookSignature([]byte(signedPayload), signature, h.secret) {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }

    // Process webhook
    h.processWebhook(body)
    w.WriteHeader(http.StatusOK)
}
```

### JWT Authentication

```go
type JWTAuth struct {
    secretKey []byte
    issuer    string
}

type Claims struct {
    jwt.StandardClaims
    WorkspaceID string   `json:"workspace_id"`
    Permissions []string `json:"permissions"`
}

func (j *JWTAuth) GenerateToken(workspaceID string, permissions []string) (string, error) {
    claims := Claims{
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
            IssuedAt:  time.Now().Unix(),
            Issuer:    j.issuer,
        },
        WorkspaceID: workspaceID,
        Permissions: permissions,
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(j.secretKey)
}

func (j *JWTAuth) ValidateToken(tokenString string) (*Claims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
        return j.secretKey, nil
    })

    if err != nil {
        return nil, err
    }

    if claims, ok := token.Claims.(*Claims); ok && token.Valid {
        return claims, nil
    }

    return nil, ErrInvalidToken
}
```

## Data Protection

### Encryption at Rest

```go
type EncryptionService struct {
    key []byte
}

func (e *EncryptionService) Encrypt(plaintext []byte) ([]byte, error) {
    block, err := aes.NewCipher(e.key)
    if err != nil {
        return nil, err
    }

    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return nil, err
    }

    nonce := make([]byte, gcm.NonceSize())
    if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
        return nil, err
    }

    return gcm.Seal(nonce, nonce, plaintext, nil), nil
}

func (e *EncryptionService) Decrypt(ciphertext []byte) ([]byte, error) {
    block, err := aes.NewCipher(e.key)
    if err != nil {
        return nil, err
    }

    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return nil, err
    }

    nonceSize := gcm.NonceSize()
    nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]

    return gcm.Open(nil, nonce, ciphertext, nil)
}
```

### PII Detection and Redaction

```go
type PIIRedactor struct {
    patterns map[string]*regexp.Regexp
}

func NewPIIRedactor() *PIIRedactor {
    return &PIIRedactor{
        patterns: map[string]*regexp.Regexp{
            "credit_card": regexp.MustCompile(`\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b`),
            "ssn":         regexp.MustCompile(`\b\d{3}-\d{2}-\d{4}\b`),
            "phone":       regexp.MustCompile(`\b\d{3}[-.]?\d{3}[-.]?\d{4}\b`),
            "email":       regexp.MustCompile(`\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b`),
            "aadhaar":     regexp.MustCompile(`\b\d{4}[\s-]?\d{4}[\s-]?\d{4}\b`), // Indian Aadhaar
            "pan":         regexp.MustCompile(`\b[A-Z]{5}[0-9]{4}[A-Z]\b`),       // Indian PAN
        },
    }
}

func (r *PIIRedactor) Redact(text string) string {
    replacements := map[string]string{
        "credit_card": "[CARD_REDACTED]",
        "ssn":         "[SSN_REDACTED]",
        "phone":       "[PHONE_REDACTED]",
        "email":       "[EMAIL_REDACTED]",
        "aadhaar":     "[AADHAAR_REDACTED]",
        "pan":         "[PAN_REDACTED]",
    }

    for name, pattern := range r.patterns {
        text = pattern.ReplaceAllString(text, replacements[name])
    }

    return text
}

func (r *PIIRedactor) DetectPII(text string) []PIIMatch {
    var matches []PIIMatch

    for name, pattern := range r.patterns {
        locs := pattern.FindAllStringIndex(text, -1)
        for _, loc := range locs {
            matches = append(matches, PIIMatch{
                Type:  name,
                Start: loc[0],
                End:   loc[1],
                Value: text[loc[0]:loc[1]],
            })
        }
    }

    return matches
}
```

### Secure Recording

```go
type SecureRecorder struct {
    encryption  *EncryptionService
    storage     Storage
    piiRedactor *PIIRedactor
}

func (r *SecureRecorder) SaveRecording(callID string, audio []byte, transcript string) error {
    // Encrypt audio
    encryptedAudio, err := r.encryption.Encrypt(audio)
    if err != nil {
        return err
    }

    // Redact PII from transcript
    redactedTranscript := r.piiRedactor.Redact(transcript)

    // Store with encryption key reference
    return r.storage.Save(Recording{
        CallID:            callID,
        EncryptedAudio:    encryptedAudio,
        RedactedTranscript: redactedTranscript,
        EncryptionKeyID:   r.encryption.KeyID(),
        CreatedAt:         time.Now(),
    })
}
```

## Access Control

### Role-Based Access Control (RBAC)

```go
type Permission string

const (
    PermissionAgentRead   Permission = "agent:read"
    PermissionAgentWrite  Permission = "agent:write"
    PermissionCallRead    Permission = "call:read"
    PermissionCallWrite   Permission = "call:write"
    PermissionRecordRead  Permission = "recording:read"
    PermissionRecordDelete Permission = "recording:delete"
    PermissionAdmin       Permission = "admin"
)

type Role struct {
    Name        string
    Permissions []Permission
}

var Roles = map[string]Role{
    "admin": {
        Name: "admin",
        Permissions: []Permission{
            PermissionAgentRead, PermissionAgentWrite,
            PermissionCallRead, PermissionCallWrite,
            PermissionRecordRead, PermissionRecordDelete,
            PermissionAdmin,
        },
    },
    "operator": {
        Name: "operator",
        Permissions: []Permission{
            PermissionAgentRead,
            PermissionCallRead, PermissionCallWrite,
            PermissionRecordRead,
        },
    },
    "viewer": {
        Name: "viewer",
        Permissions: []Permission{
            PermissionAgentRead,
            PermissionCallRead,
        },
    },
}

type RBAC struct {
    userRoles map[string][]string
}

func (r *RBAC) HasPermission(userID string, permission Permission) bool {
    roles := r.userRoles[userID]

    for _, roleName := range roles {
        role := Roles[roleName]
        for _, p := range role.Permissions {
            if p == permission || p == PermissionAdmin {
                return true
            }
        }
    }

    return false
}
```

### Request Authorization

```go
func AuthorizationMiddleware(rbac *RBAC, permission Permission) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            userID := r.Context().Value("user_id").(string)

            if !rbac.HasPermission(userID, permission) {
                http.Error(w, "Forbidden", http.StatusForbidden)
                return
            }

            next.ServeHTTP(w, r)
        })
    }
}

// Usage
router.Handle("/agents", AuthorizationMiddleware(rbac, PermissionAgentRead)(agentHandler))
```

## Audit Logging

### Audit Events

```go
type AuditEvent struct {
    ID          string
    Timestamp   time.Time
    UserID      string
    Action      string
    Resource    string
    ResourceID  string
    Details     map[string]any
    IPAddress   string
    UserAgent   string
    Success     bool
    ErrorReason string
}

type AuditLogger struct {
    storage Storage
}

func (l *AuditLogger) Log(ctx context.Context, event AuditEvent) {
    event.ID = uuid.New().String()
    event.Timestamp = time.Now()
    event.IPAddress = GetIPFromContext(ctx)
    event.UserAgent = GetUserAgentFromContext(ctx)

    // Async write to avoid blocking
    go func() {
        if err := l.storage.Save(event); err != nil {
            log.Printf("Failed to save audit log: %v", err)
        }
    }()
}

// Usage
auditLogger.Log(ctx, AuditEvent{
    UserID:     userID,
    Action:     "agent.update",
    Resource:   "agent",
    ResourceID: agentID,
    Details: map[string]any{
        "changed_fields": []string{"prompt", "greeting"},
    },
    Success: true,
})
```

### Sensitive Data Logging

```go
func (l *AuditLogger) LogSensitiveAccess(ctx context.Context, dataType, resourceID string) {
    l.Log(ctx, AuditEvent{
        UserID:     GetUserID(ctx),
        Action:     "sensitive_data.access",
        Resource:   dataType,
        ResourceID: resourceID,
        Details: map[string]any{
            "purpose": "customer_support",
        },
        Success: true,
    })
}
```

## Network Security

### TLS Configuration

```go
func SecureTLSConfig() *tls.Config {
    return &tls.Config{
        MinVersion: tls.VersionTLS12,
        CipherSuites: []uint16{
            tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
            tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
            tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
            tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
        },
        PreferServerCipherSuites: true,
    }
}

func StartSecureServer(addr string, handler http.Handler) error {
    server := &http.Server{
        Addr:      addr,
        Handler:   handler,
        TLSConfig: SecureTLSConfig(),
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  120 * time.Second,
    }

    return server.ListenAndServeTLS("cert.pem", "key.pem")
}
```

### Rate Limiting

```go
type RateLimiter struct {
    limits map[string]*rate.Limiter
    mu     sync.RWMutex
}

func (r *RateLimiter) Allow(key string, limit rate.Limit, burst int) bool {
    r.mu.Lock()
    defer r.mu.Unlock()

    limiter, ok := r.limits[key]
    if !ok {
        limiter = rate.NewLimiter(limit, burst)
        r.limits[key] = limiter
    }

    return limiter.Allow()
}

func RateLimitMiddleware(limiter *RateLimiter) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            key := r.Header.Get("X-API-Key")
            if !limiter.Allow(key, 100, 10) { // 100 req/sec, burst 10
                http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
                return
            }
            next.ServeHTTP(w, r)
        })
    }
}
```

## Compliance

### GDPR Compliance

```go
type GDPRHandler struct {
    storage Storage
}

// Right to be forgotten
func (h *GDPRHandler) DeleteUserData(userID string) error {
    // Delete all user-related data
    if err := h.storage.DeleteTranscripts(userID); err != nil {
        return err
    }
    if err := h.storage.DeleteRecordings(userID); err != nil {
        return err
    }
    if err := h.storage.DeleteCallLogs(userID); err != nil {
        return err
    }

    return nil
}

// Right to data portability
func (h *GDPRHandler) ExportUserData(userID string) (*UserDataExport, error) {
    transcripts, _ := h.storage.GetTranscripts(userID)
    callLogs, _ := h.storage.GetCallLogs(userID)

    return &UserDataExport{
        UserID:      userID,
        Transcripts: transcripts,
        CallLogs:    callLogs,
        ExportedAt:  time.Now(),
    }, nil
}

// Data retention
func (h *GDPRHandler) ApplyRetentionPolicy(maxAgeDays int) error {
    cutoff := time.Now().AddDate(0, 0, -maxAgeDays)
    return h.storage.DeleteOlderThan(cutoff)
}
```

### PCI-DSS Compliance

```go
type PCICompliantDTMF struct {
    // Never log DTMF during sensitive input
    sensitiveMode bool
    recording     *Recorder
}

func (d *PCICompliantDTMF) EnterSensitiveMode() {
    d.sensitiveMode = true
    d.recording.Pause() // Stop recording
}

func (d *PCICompliantDTMF) ExitSensitiveMode() {
    d.sensitiveMode = false
    d.recording.Resume()
}

func (d *PCICompliantDTMF) CollectCardNumber() string {
    d.EnterSensitiveMode()
    defer d.ExitSensitiveMode()

    // Collect DTMF without logging
    digits := d.collectDTMF(16)

    // Tokenize immediately
    token := d.tokenizer.Tokenize(digits)

    // Never store raw card number
    return token
}
```

## Security Checklist

### API Security
- [x] Use API keys with rotation policy
- [x] Implement rate limiting
- [x] Validate and sanitize all input
- [x] Use HTTPS/WSS only
- [x] Implement request signing

### Data Security
- [x] Encrypt data at rest
- [x] Encrypt data in transit (TLS 1.2+)
- [x] Redact PII from logs and transcripts
- [x] Implement data retention policies
- [x] Secure key management

### Access Control
- [x] Implement RBAC
- [x] Audit all access
- [x] Use principle of least privilege
- [x] Regular access reviews

### Compliance
- [x] GDPR: Data portability and deletion
- [x] PCI-DSS: Secure payment handling
- [x] HIPAA: Healthcare data protection (if applicable)
- [x] SOC 2: Security controls

## Next Steps

- [Error Handling](https://edesy.in/docs/voice-agent/advanced/error-handling) - Resilience patterns
- [Monitoring](https://edesy.in/docs/voice-agent/advanced/monitoring) - Security monitoring
- [Scaling](https://edesy.in/docs/voice-agent/advanced/scaling) - Secure scaling
