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
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
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
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
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
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
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)
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
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
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
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
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
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
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
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
- Use API keys with rotation policy
- Implement rate limiting
- Validate and sanitize all input
- Use HTTPS/WSS only
- Implement request signing
Data Security
- Encrypt data at rest
- Encrypt data in transit (TLS 1.2+)
- Redact PII from logs and transcripts
- Implement data retention policies
- Secure key management
Access Control
- Implement RBAC
- Audit all access
- Use principle of least privilege
- Regular access reviews
Compliance
- GDPR: Data portability and deletion
- PCI-DSS: Secure payment handling
- HIPAA: Healthcare data protection (if applicable)
- SOC 2: Security controls
Next Steps
- Error Handling - Resilience patterns
- Monitoring - Security monitoring
- Scaling - Secure scaling