Security Examples
This guide demonstrates security best practices and patterns when using CVSS Parser in security-sensitive environments.
Overview
Security considerations include:
- Input validation and sanitization
- Secure data handling
- Authentication and authorization
- Audit logging
- Secure communication
- Vulnerability management
Input Validation
Comprehensive Input Validation
go
package security
import (
"fmt"
"regexp"
"strings"
"unicode/utf8"
)
type SecurityValidator struct {
maxVectorLength int
allowedChars *regexp.Regexp
blockedPatterns []*regexp.Regexp
}
func NewSecurityValidator() *SecurityValidator {
return &SecurityValidator{
maxVectorLength: 500,
allowedChars: regexp.MustCompile(`^[A-Za-z0-9:/.]+$`),
blockedPatterns: []*regexp.Regexp{
regexp.MustCompile(`<script`), // XSS
regexp.MustCompile(`javascript:`), // XSS
regexp.MustCompile(`on\w+\s*=`), // Event handlers
regexp.MustCompile(`\x00`), // Null bytes
regexp.MustCompile(`\.\./`), // Path traversal
regexp.MustCompile(`union\s+select`), // SQL injection
regexp.MustCompile(`drop\s+table`), // SQL injection
regexp.MustCompile(`exec\s*\(`), // Command injection
},
}
}
func (sv *SecurityValidator) ValidateVector(vector string) error {
// Check for empty input
if vector == "" {
return fmt.Errorf("vector cannot be empty")
}
// Check length
if len(vector) > sv.maxVectorLength {
return fmt.Errorf("vector exceeds maximum length of %d characters", sv.maxVectorLength)
}
// Check for valid UTF-8
if !utf8.ValidString(vector) {
return fmt.Errorf("vector contains invalid UTF-8 characters")
}
// Check for allowed characters only
if !sv.allowedChars.MatchString(vector) {
return fmt.Errorf("vector contains invalid characters")
}
// Check for blocked patterns
for _, pattern := range sv.blockedPatterns {
if pattern.MatchString(strings.ToLower(vector)) {
return fmt.Errorf("vector contains potentially malicious content")
}
}
// Validate CVSS format
if !strings.HasPrefix(vector, "CVSS:3.") {
return fmt.Errorf("vector must start with CVSS:3.x")
}
return nil
}
func (sv *SecurityValidator) SanitizeVector(vector string) string {
// Remove null bytes
vector = strings.ReplaceAll(vector, "\x00", "")
// Remove control characters
var sanitized strings.Builder
for _, r := range vector {
if r >= 32 && r < 127 { // Printable ASCII only
sanitized.WriteRune(r)
}
}
return sanitized.String()
}
Rate Limiting
go
import (
"sync"
"time"
"golang.org/x/time/rate"
)
type RateLimiter struct {
limiters map[string]*rate.Limiter
mutex sync.RWMutex
limit rate.Limit
burst int
}
func NewRateLimiter(requestsPerSecond int, burst int) *RateLimiter {
return &RateLimiter{
limiters: make(map[string]*rate.Limiter),
limit: rate.Limit(requestsPerSecond),
burst: burst,
}
}
func (rl *RateLimiter) Allow(clientID string) bool {
rl.mutex.RLock()
limiter, exists := rl.limiters[clientID]
rl.mutex.RUnlock()
if !exists {
rl.mutex.Lock()
limiter = rate.NewLimiter(rl.limit, rl.burst)
rl.limiters[clientID] = limiter
rl.mutex.Unlock()
}
return limiter.Allow()
}
func (rl *RateLimiter) CleanupExpired() {
rl.mutex.Lock()
defer rl.mutex.Unlock()
// Remove limiters that haven't been used recently
for clientID, limiter := range rl.limiters {
if limiter.Tokens() == float64(rl.burst) {
delete(rl.limiters, clientID)
}
}
}
Secure Data Handling
Sensitive Data Protection
go
type SecureVectorProcessor struct {
encryptor *DataEncryptor
auditor *AuditLogger
validator *SecurityValidator
rateLimiter *RateLimiter
}
type DataEncryptor struct {
key []byte
}
func NewDataEncryptor(key []byte) *DataEncryptor {
return &DataEncryptor{key: key}
}
func (de *DataEncryptor) Encrypt(data string) (string, error) {
// Implementation would use AES-GCM or similar
// This is a simplified example
return base64.StdEncoding.EncodeToString([]byte(data)), nil
}
func (de *DataEncryptor) Decrypt(encryptedData string) (string, error) {
// Implementation would decrypt using AES-GCM
decoded, err := base64.StdEncoding.DecodeString(encryptedData)
if err != nil {
return "", err
}
return string(decoded), nil
}
func (svp *SecureVectorProcessor) ProcessVector(ctx context.Context, vector string, clientID string) (*SecureResult, error) {
// Rate limiting
if !svp.rateLimiter.Allow(clientID) {
svp.auditor.LogSecurityEvent(ctx, "RATE_LIMIT_EXCEEDED", clientID, vector)
return nil, fmt.Errorf("rate limit exceeded")
}
// Input validation
if err := svp.validator.ValidateVector(vector); err != nil {
svp.auditor.LogSecurityEvent(ctx, "INVALID_INPUT", clientID, vector)
return nil, fmt.Errorf("validation failed: %w", err)
}
// Sanitize input
sanitizedVector := svp.validator.SanitizeVector(vector)
// Process vector
parser := parser.NewCvss3xParser(sanitizedVector)
parsedVector, err := parser.Parse()
if err != nil {
svp.auditor.LogSecurityEvent(ctx, "PARSE_ERROR", clientID, sanitizedVector)
return nil, fmt.Errorf("parsing failed: %w", err)
}
calculator := cvss.NewCalculator(parsedVector)
score, err := calculator.Calculate()
if err != nil {
svp.auditor.LogSecurityEvent(ctx, "CALCULATION_ERROR", clientID, sanitizedVector)
return nil, fmt.Errorf("calculation failed: %w", err)
}
// Encrypt sensitive data if needed
encryptedVector, err := svp.encryptor.Encrypt(sanitizedVector)
if err != nil {
return nil, fmt.Errorf("encryption failed: %w", err)
}
// Log successful processing
svp.auditor.LogProcessingEvent(ctx, clientID, sanitizedVector, score)
return &SecureResult{
EncryptedVector: encryptedVector,
Score: score,
Severity: calculator.GetSeverityRating(score),
ProcessedAt: time.Now(),
ClientID: clientID,
}, nil
}
type SecureResult struct {
EncryptedVector string `json:"encrypted_vector"`
Score float64 `json:"score"`
Severity string `json:"severity"`
ProcessedAt time.Time `json:"processed_at"`
ClientID string `json:"client_id"`
}
Authentication and Authorization
JWT Authentication
go
import (
"github.com/golang-jwt/jwt/v4"
"crypto/rsa"
)
type AuthService struct {
publicKey *rsa.PublicKey
privateKey *rsa.PrivateKey
issuer string
}
type Claims struct {
UserID string `json:"user_id"`
Permissions []string `json:"permissions"`
jwt.RegisteredClaims
}
func NewAuthService(publicKey, privateKey *rsa.Key, issuer string) *AuthService {
return &AuthService{
publicKey: publicKey,
privateKey: privateKey,
issuer: issuer,
}
}
func (as *AuthService) ValidateToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return as.publicKey, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, fmt.Errorf("invalid token")
}
func (as *AuthService) HasPermission(claims *Claims, permission string) bool {
for _, p := range claims.Permissions {
if p == permission || p == "admin" {
return true
}
}
return false
}
// Middleware for HTTP handlers
func (as *AuthService) AuthMiddleware(requiredPermission string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Missing authorization header", http.StatusUnauthorized)
return
}
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
claims, err := as.ValidateToken(tokenString)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
if !as.HasPermission(claims, requiredPermission) {
http.Error(w, "Insufficient permissions", http.StatusForbidden)
return
}
// Add claims to context
ctx := context.WithValue(r.Context(), "claims", claims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
Audit Logging
Comprehensive Audit Trail
go
type AuditLogger struct {
logger *logrus.Logger
db *sql.DB
}
type AuditEvent struct {
ID string `json:"id"`
Timestamp time.Time `json:"timestamp"`
EventType string `json:"event_type"`
UserID string `json:"user_id"`
ClientID string `json:"client_id"`
Action string `json:"action"`
Resource string `json:"resource"`
Result string `json:"result"`
IPAddress string `json:"ip_address"`
UserAgent string `json:"user_agent"`
Details string `json:"details"`
Severity string `json:"severity"`
}
func NewAuditLogger(logger *logrus.Logger, db *sql.DB) *AuditLogger {
return &AuditLogger{
logger: logger,
db: db,
}
}
func (al *AuditLogger) LogSecurityEvent(ctx context.Context, eventType, clientID, details string) {
event := &AuditEvent{
ID: generateUUID(),
Timestamp: time.Now().UTC(),
EventType: eventType,
ClientID: clientID,
Action: "CVSS_PROCESSING",
Resource: "CVSS_VECTOR",
Result: "SECURITY_VIOLATION",
Details: details,
Severity: "HIGH",
}
// Extract additional context
if claims := ctx.Value("claims"); claims != nil {
if c, ok := claims.(*Claims); ok {
event.UserID = c.UserID
}
}
if req := ctx.Value("request"); req != nil {
if r, ok := req.(*http.Request); ok {
event.IPAddress = getClientIP(r)
event.UserAgent = r.UserAgent()
}
}
// Log to structured logger
al.logger.WithFields(logrus.Fields{
"audit_event": event,
}).Warn("Security event detected")
// Store in database
al.storeAuditEvent(event)
}
func (al *AuditLogger) LogProcessingEvent(ctx context.Context, clientID, vector string, score float64) {
event := &AuditEvent{
ID: generateUUID(),
Timestamp: time.Now().UTC(),
EventType: "PROCESSING_SUCCESS",
ClientID: clientID,
Action: "CVSS_PROCESSING",
Resource: "CVSS_VECTOR",
Result: "SUCCESS",
Details: fmt.Sprintf("Vector processed successfully, score: %.1f", score),
Severity: "INFO",
}
al.logger.WithFields(logrus.Fields{
"audit_event": event,
}).Info("Vector processed successfully")
al.storeAuditEvent(event)
}
func (al *AuditLogger) storeAuditEvent(event *AuditEvent) {
query := `
INSERT INTO audit_events (
id, timestamp, event_type, user_id, client_id, action,
resource, result, ip_address, user_agent, details, severity
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`
_, err := al.db.Exec(query,
event.ID, event.Timestamp, event.EventType, event.UserID,
event.ClientID, event.Action, event.Resource, event.Result,
event.IPAddress, event.UserAgent, event.Details, event.Severity,
)
if err != nil {
al.logger.WithError(err).Error("Failed to store audit event")
}
}
Secure Communication
TLS Configuration
go
import (
"crypto/tls"
"crypto/x509"
)
func CreateSecureTLSConfig() *tls.Config {
return &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
},
}
}
func CreateSecureHTTPServer(handler http.Handler, certFile, keyFile string) *http.Server {
tlsConfig := CreateSecureTLSConfig()
server := &http.Server{
Addr: ":8443",
Handler: handler,
TLSConfig: tlsConfig,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
return server
}
Certificate Validation
go
func ValidateClientCertificate(cert *x509.Certificate, caCert *x509.Certificate) error {
// Check if certificate is expired
now := time.Now()
if now.Before(cert.NotBefore) || now.After(cert.NotAfter) {
return fmt.Errorf("certificate is expired or not yet valid")
}
// Verify certificate chain
roots := x509.NewCertPool()
roots.AddCert(caCert)
opts := x509.VerifyOptions{
Roots: roots,
}
_, err := cert.Verify(opts)
if err != nil {
return fmt.Errorf("certificate verification failed: %w", err)
}
return nil
}
Vulnerability Management
Security Scanning Integration
go
type SecurityScanner struct {
vulnerabilityDB *VulnerabilityDB
alertManager *AlertManager
}
type VulnerabilityDB struct {
db *sql.DB
}
func (vs *SecurityScanner) ScanVector(vector string) (*SecurityScanResult, error) {
// Parse vector to extract components
parser := parser.NewCvss3xParser(vector)
parsedVector, err := parser.Parse()
if err != nil {
return nil, err
}
// Check for known vulnerability patterns
threats := vs.checkKnownThreats(parsedVector)
// Analyze risk level
riskLevel := vs.assessRiskLevel(parsedVector, threats)
// Generate recommendations
recommendations := vs.generateRecommendations(parsedVector, threats)
result := &SecurityScanResult{
Vector: vector,
Threats: threats,
RiskLevel: riskLevel,
Recommendations: recommendations,
ScanTime: time.Now(),
}
// Alert on high-risk findings
if riskLevel == "HIGH" || riskLevel == "CRITICAL" {
vs.alertManager.SendAlert(result)
}
return result, nil
}
type SecurityScanResult struct {
Vector string `json:"vector"`
Threats []ThreatIndicator `json:"threats"`
RiskLevel string `json:"risk_level"`
Recommendations []string `json:"recommendations"`
ScanTime time.Time `json:"scan_time"`
}
type ThreatIndicator struct {
Type string `json:"type"`
Severity string `json:"severity"`
Description string `json:"description"`
Confidence float64 `json:"confidence"`
}
Security Testing
Security Test Suite
go
func TestSecurityValidation(t *testing.T) {
validator := NewSecurityValidator()
maliciousInputs := []struct {
name string
input string
}{
{"XSS Script", "<script>alert('xss')</script>"},
{"SQL Injection", "'; DROP TABLE users; --"},
{"Path Traversal", "../../../etc/passwd"},
{"Null Byte", "CVSS:3.1\x00/AV:N"},
{"Command Injection", "CVSS:3.1; rm -rf /"},
{"Unicode Attack", "CVSS:3.1\u202e/AV:N"},
{"Overlong Input", strings.Repeat("A", 10000)},
}
for _, test := range maliciousInputs {
t.Run(test.name, func(t *testing.T) {
err := validator.ValidateVector(test.input)
assert.Error(t, err, "Should reject malicious input: %s", test.input)
})
}
}
func TestRateLimiting(t *testing.T) {
limiter := NewRateLimiter(5, 10) // 5 requests per second, burst of 10
clientID := "test-client"
// Should allow initial burst
for i := 0; i < 10; i++ {
assert.True(t, limiter.Allow(clientID), "Should allow request %d", i+1)
}
// Should reject additional requests
assert.False(t, limiter.Allow(clientID), "Should reject request after burst")
// Wait and try again
time.Sleep(200 * time.Millisecond)
assert.True(t, limiter.Allow(clientID), "Should allow request after wait")
}
Security Monitoring
Real-time Security Monitoring
go
type SecurityMonitor struct {
alertThresholds map[string]int
eventCounts map[string]int
mutex sync.RWMutex
alertManager *AlertManager
}
func NewSecurityMonitor() *SecurityMonitor {
return &SecurityMonitor{
alertThresholds: map[string]int{
"INVALID_INPUT": 10,
"RATE_LIMIT_EXCEEDED": 50,
"AUTH_FAILURE": 5,
},
eventCounts: make(map[string]int),
}
}
func (sm *SecurityMonitor) RecordSecurityEvent(eventType string) {
sm.mutex.Lock()
defer sm.mutex.Unlock()
sm.eventCounts[eventType]++
if threshold, exists := sm.alertThresholds[eventType]; exists {
if sm.eventCounts[eventType] >= threshold {
sm.alertManager.SendSecurityAlert(eventType, sm.eventCounts[eventType])
sm.eventCounts[eventType] = 0 // Reset counter
}
}
}
func (sm *SecurityMonitor) GetSecurityMetrics() map[string]int {
sm.mutex.RLock()
defer sm.mutex.RUnlock()
metrics := make(map[string]int)
for k, v := range sm.eventCounts {
metrics[k] = v
}
return metrics
}
Best Practices
Security Checklist
Input Validation
- Validate all inputs against strict patterns
- Sanitize inputs before processing
- Implement length limits
- Check for malicious patterns
Authentication & Authorization
- Use strong authentication mechanisms
- Implement proper authorization checks
- Use secure token handling
- Validate permissions for each operation
Data Protection
- Encrypt sensitive data at rest and in transit
- Use secure key management
- Implement proper data retention policies
- Sanitize logs and error messages
Monitoring & Logging
- Log all security-relevant events
- Implement real-time monitoring
- Set up alerting for suspicious activities
- Maintain audit trails
Network Security
- Use TLS for all communications
- Implement proper certificate validation
- Configure secure cipher suites
- Use network segmentation
Next Steps
After implementing security measures:
- Compliance Integration - Regulatory compliance
- Incident Response - Security incident handling
- Security Metrics - Security measurement
Related Documentation
- Error Handling - Secure error handling
- Authentication - Authentication patterns
- Monitoring - Security monitoring