62 lines
1.5 KiB
TypeScript
62 lines
1.5 KiB
TypeScript
export class SecurityUtils {
|
|
// Input sanitization to prevent XSS
|
|
static sanitizeInput(input: string): string {
|
|
if (!input) return ""
|
|
return input
|
|
.replace(/[<>]/g, "") // Remove < and >
|
|
.replace(/javascript:/gi, "") // Remove javascript: protocol
|
|
.replace(/on\w+=/gi, "") // Remove event handlers
|
|
.trim()
|
|
}
|
|
|
|
// Email validation
|
|
static isValidEmail(email: string): boolean {
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
return emailRegex.test(email)
|
|
}
|
|
|
|
// Password strength validation (simplified)
|
|
static validatePasswordStrength(password: string): {
|
|
isValid: boolean
|
|
errors: string[]
|
|
} {
|
|
const errors: string[] = []
|
|
|
|
if (password.length < 4) {
|
|
errors.push("Password minimal 4 karakter")
|
|
}
|
|
|
|
return {
|
|
isValid: errors.length === 0,
|
|
errors,
|
|
}
|
|
}
|
|
|
|
// Rate limiting helper
|
|
static createRateLimiter() {
|
|
const attempts = new Map<string, { count: number; resetTime: number }>()
|
|
|
|
return {
|
|
isAllowed: (key: string, maxAttempts = 5, windowMs: number = 15 * 60 * 1000): boolean => {
|
|
const now = Date.now()
|
|
const record = attempts.get(key)
|
|
|
|
if (!record || now > record.resetTime) {
|
|
attempts.set(key, { count: 1, resetTime: now + windowMs })
|
|
return true
|
|
}
|
|
|
|
if (record.count >= maxAttempts) {
|
|
return false
|
|
}
|
|
|
|
record.count++
|
|
return true
|
|
},
|
|
reset: (key: string) => {
|
|
attempts.delete(key)
|
|
},
|
|
}
|
|
}
|
|
}
|