JavaScript Security Best Practices: Protecting Your Applications
Essential JavaScript security practices to protect against XSS, CSRF, injection attacks, and other vulnerabilities. Build secure, robust web applications.
JavaScript security is crucial for protecting web applications from various attacks and vulnerabilities. This guide covers essential security practices, common attack vectors, and defensive coding techniques to build secure applications.
Common Security Vulnerabilities
Cross-Site Scripting (XSS)
XSS attacks inject malicious scripts into web pages, potentially stealing user data or performing unauthorized actions.
// Vulnerable code - DON'T DO THIS
function displayUserContent(content) {
document.getElementById('content').innerHTML = content; // Dangerous!
}
// Secure approach - Sanitize and escape content
class XSSProtection {
static escapeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
static sanitizeHTML(html) {
// Create a temporary DOM element
const temp = document.createElement('div');
temp.innerHTML = html;
// Remove dangerous elements and attributes
const dangerous = temp.querySelectorAll(
'script, object, embed, link, style, iframe'
);
dangerous.forEach((el) => el.remove());
// Remove dangerous attributes
const allElements = temp.querySelectorAll('*');
allElements.forEach((el) => {
for (let i = el.attributes.length - 1; i >= 0; i--) {
const attr = el.attributes[i];
if (
attr.name.startsWith('on') ||
(attr.name === 'href' && attr.value.startsWith('javascript:'))
) {
el.removeAttribute(attr.name);
}
}
});
return temp.innerHTML;
}
static safeSetHTML(element, content, allowHTML = false) {
if (allowHTML) {
element.innerHTML = this.sanitizeHTML(content);
} else {
element.textContent = content; // Safe by default
}
}
// Content Security Policy helper
static setupCSP() {
const cspMeta = document.createElement('meta');
cspMeta.httpEquiv = 'Content-Security-Policy';
cspMeta.content = [
"default-src 'self'",
"script-src 'self' 'unsafe-inline'", // Avoid 'unsafe-inline' in production
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"font-src 'self'",
"connect-src 'self'",
"frame-ancestors 'none'",
].join('; ');
document.head.appendChild(cspMeta);
}
}
// Safe content rendering
function displayUserContentSafely(content, allowHTML = false) {
const container = document.getElementById('content');
XSSProtection.safeSetHTML(container, content, allowHTML);
}
// Input validation and sanitization
class InputValidator {
static validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email) && email.length <= 254;
}
static validateURL(url) {
try {
const urlObj = new URL(url);
return ['http:', 'https:'].includes(urlObj.protocol);
} catch {
return false;
}
}
static sanitizeInput(input, maxLength = 1000) {
if (typeof input !== 'string') {
return '';
}
return input
.trim()
.slice(0, maxLength)
.replace(/[<>'"&]/g, (char) => {
const map = {
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'&': '&',
};
return map[char];
});
}
static validateAndSanitize(input, type, options = {}) {
let sanitized = this.sanitizeInput(input, options.maxLength);
switch (type) {
case 'email':
return this.validateEmail(sanitized) ? sanitized : null;
case 'url':
return this.validateURL(sanitized) ? sanitized : null;
case 'alphanumeric':
return /^[a-zA-Z0-9]+$/.test(sanitized) ? sanitized : null;
case 'number':
const num = parseFloat(sanitized);
return !isNaN(num) && isFinite(num) ? num : null;
default:
return sanitized;
}
}
}
Cross-Site Request Forgery (CSRF)
CSRF attacks trick users into performing unintended actions on websites where they're authenticated.
class CSRFProtection {
constructor() {
this.token = this.generateToken();
this.tokenExpiry = Date.now() + 30 * 60 * 1000; // 30 minutes
}
generateToken() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(
''
);
}
getToken() {
// Refresh token if expired
if (Date.now() > this.tokenExpiry) {
this.token = this.generateToken();
this.tokenExpiry = Date.now() + 30 * 60 * 1000;
}
return this.token;
}
// Add CSRF token to all forms
protectForms() {
const forms = document.querySelectorAll('form');
forms.forEach((form) => {
// Skip forms that already have CSRF token
if (form.querySelector('input[name="csrf_token"]')) {
return;
}
const csrfInput = document.createElement('input');
csrfInput.type = 'hidden';
csrfInput.name = 'csrf_token';
csrfInput.value = this.getToken();
form.appendChild(csrfInput);
});
}
// Protect AJAX requests
protectAjaxRequests() {
const originalFetch = window.fetch;
window.fetch = async (url, options = {}) => {
// Add CSRF token to non-GET requests
if (options.method && options.method.toUpperCase() !== 'GET') {
options.headers = {
...options.headers,
'X-CSRF-Token': this.getToken(),
};
}
return originalFetch(url, options);
};
// Protect XMLHttpRequest
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (method, url, ...args) {
this._method = method;
return originalOpen.call(this, method, url, ...args);
};
XMLHttpRequest.prototype.send = function (data) {
if (this._method && this._method.toUpperCase() !== 'GET') {
this.setRequestHeader('X-CSRF-Token', csrfProtection.getToken());
}
return originalSend.call(this, data);
};
}
// Verify referrer
verifyReferrer(allowedDomains = []) {
const referrer = document.referrer;
const currentDomain = window.location.hostname;
if (!referrer) {
return false; // No referrer (suspicious)
}
try {
const referrerDomain = new URL(referrer).hostname;
return (
referrerDomain === currentDomain ||
allowedDomains.includes(referrerDomain)
);
} catch {
return false;
}
}
}
// Initialize CSRF protection
const csrfProtection = new CSRFProtection();
csrfProtection.protectForms();
csrfProtection.protectAjaxRequests();
SQL Injection Prevention
While JavaScript doesn't directly interact with databases, it's important to understand injection prevention when building APIs.
class APISecurityHelper {
// Validate and sanitize API parameters
static validateAPIRequest(params, schema) {
const errors = [];
const sanitized = {};
for (const [key, rules] of Object.entries(schema)) {
const value = params[key];
// Check required fields
if (
rules.required &&
(value === undefined || value === null || value === '')
) {
errors.push(`${key} is required`);
continue;
}
// Skip validation for optional empty fields
if (
!rules.required &&
(value === undefined || value === null || value === '')
) {
continue;
}
// Type validation
if (rules.type === 'string' && typeof value !== 'string') {
errors.push(`${key} must be a string`);
continue;
}
if (
rules.type === 'number' &&
(typeof value !== 'number' || isNaN(value))
) {
errors.push(`${key} must be a number`);
continue;
}
if (rules.type === 'email' && !InputValidator.validateEmail(value)) {
errors.push(`${key} must be a valid email`);
continue;
}
// Length validation
if (rules.maxLength && value.length > rules.maxLength) {
errors.push(`${key} cannot exceed ${rules.maxLength} characters`);
continue;
}
if (rules.minLength && value.length < rules.minLength) {
errors.push(`${key} must be at least ${rules.minLength} characters`);
continue;
}
// Pattern validation
if (rules.pattern && !rules.pattern.test(value)) {
errors.push(`${key} format is invalid`);
continue;
}
// Sanitize and store
sanitized[key] =
rules.type === 'string'
? InputValidator.sanitizeInput(value, rules.maxLength)
: value;
}
return {
isValid: errors.length === 0,
errors,
sanitized,
};
}
// Rate limiting helper
static createRateLimiter(maxRequests = 100, windowMs = 60000) {
const requests = new Map();
return function rateLimiter(identifier) {
const now = Date.now();
const windowStart = now - windowMs;
// Clean old entries
for (const [key, timestamps] of requests.entries()) {
const validTimestamps = timestamps.filter((t) => t > windowStart);
if (validTimestamps.length === 0) {
requests.delete(key);
} else {
requests.set(key, validTimestamps);
}
}
// Check current identifier
const userRequests = requests.get(identifier) || [];
const recentRequests = userRequests.filter((t) => t > windowStart);
if (recentRequests.length >= maxRequests) {
return {
allowed: false,
remaining: 0,
resetTime: Math.min(...recentRequests) + windowMs,
};
}
// Add current request
recentRequests.push(now);
requests.set(identifier, recentRequests);
return {
allowed: true,
remaining: maxRequests - recentRequests.length,
resetTime: now + windowMs,
};
};
}
}
// Example API request validation
const userSchema = {
username: {
type: 'string',
required: true,
minLength: 3,
maxLength: 20,
pattern: /^[a-zA-Z0-9_]+$/,
},
email: {
type: 'email',
required: true,
maxLength: 254,
},
age: {
type: 'number',
required: false,
min: 13,
max: 120,
},
};
function handleUserRegistration(userData) {
const validation = APISecurityHelper.validateAPIRequest(userData, userSchema);
if (!validation.isValid) {
return {
success: false,
errors: validation.errors,
};
}
// Proceed with sanitized data
const { sanitized } = validation;
// ... register user with sanitized data
}
Secure Authentication
class SecureAuth {
constructor() {
this.tokenKey = 'auth_token';
this.refreshKey = 'refresh_token';
this.maxLoginAttempts = 5;
this.lockoutDuration = 15 * 60 * 1000; // 15 minutes
}
// Secure password validation
validatePassword(password) {
const requirements = {
minLength: password.length >= 8,
hasUppercase: /[A-Z]/.test(password),
hasLowercase: /[a-z]/.test(password),
hasNumbers: /\d/.test(password),
hasSpecialChars: /[!@#$%^&*(),.?":{}|<>]/.test(password),
noCommonPatterns: !this.isCommonPassword(password),
};
const score = Object.values(requirements).filter(Boolean).length;
return {
isValid: score >= 5,
score,
requirements,
strength: score < 3 ? 'weak' : score < 5 ? 'medium' : 'strong',
};
}
isCommonPassword(password) {
const common = [
'password',
'123456',
'password123',
'admin',
'qwerty',
'letmein',
'welcome',
'monkey',
'1234567890',
];
return common.includes(password.toLowerCase());
}
// Secure token storage
setToken(token, refreshToken = null) {
// Use secure storage if available
if (window.crypto && window.crypto.subtle) {
this.storeTokenSecurely(token, refreshToken);
} else {
// Fallback to sessionStorage (more secure than localStorage for tokens)
sessionStorage.setItem(this.tokenKey, token);
if (refreshToken) {
sessionStorage.setItem(this.refreshKey, refreshToken);
}
}
}
async storeTokenSecurely(token, refreshToken) {
try {
// Generate encryption key
const key = await window.crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
// Store key reference securely
const keyData = await window.crypto.subtle.exportKey('raw', key);
const keyArray = new Uint8Array(keyData);
// Encrypt token
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const encodedToken = new TextEncoder().encode(token);
const encryptedToken = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
encodedToken
);
// Store encrypted data
const tokenData = {
encrypted: Array.from(new Uint8Array(encryptedToken)),
iv: Array.from(iv),
key: Array.from(keyArray),
};
sessionStorage.setItem(this.tokenKey, JSON.stringify(tokenData));
} catch (error) {
console.error('Failed to encrypt token:', error);
// Fallback to plain storage
sessionStorage.setItem(this.tokenKey, token);
}
}
getToken() {
const stored = sessionStorage.getItem(this.tokenKey);
if (!stored) return null;
try {
const tokenData = JSON.parse(stored);
if (tokenData.encrypted) {
return this.decryptToken(tokenData);
}
return stored; // Plain token
} catch {
return stored; // Plain token
}
}
async decryptToken(tokenData) {
try {
const key = await window.crypto.subtle.importKey(
'raw',
new Uint8Array(tokenData.key),
{ name: 'AES-GCM' },
false,
['decrypt']
);
const decrypted = await window.crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: new Uint8Array(tokenData.iv) },
key,
new Uint8Array(tokenData.encrypted)
);
return new TextDecoder().decode(decrypted);
} catch (error) {
console.error('Failed to decrypt token:', error);
return null;
}
}
// Login attempt tracking
trackLoginAttempt(username, success) {
const key = `login_attempts_${username}`;
const stored = localStorage.getItem(key);
let attempts = stored
? JSON.parse(stored)
: { count: 0, lastAttempt: 0, lockedUntil: 0 };
if (success) {
// Reset on successful login
localStorage.removeItem(key);
return { allowed: true };
}
// Check if still locked out
if (attempts.lockedUntil > Date.now()) {
return {
allowed: false,
lockedUntil: attempts.lockedUntil,
reason: 'Account temporarily locked',
};
}
// Increment failed attempts
attempts.count++;
attempts.lastAttempt = Date.now();
// Lock account if max attempts reached
if (attempts.count >= this.maxLoginAttempts) {
attempts.lockedUntil = Date.now() + this.lockoutDuration;
localStorage.setItem(key, JSON.stringify(attempts));
return {
allowed: false,
lockedUntil: attempts.lockedUntil,
reason: 'Too many failed attempts',
};
}
localStorage.setItem(key, JSON.stringify(attempts));
return {
allowed: true,
attemptsRemaining: this.maxLoginAttempts - attempts.count,
};
}
// Secure logout
logout() {
// Clear all auth data
sessionStorage.removeItem(this.tokenKey);
sessionStorage.removeItem(this.refreshKey);
localStorage.removeItem('user_preferences');
// Clear any auth cookies
document.cookie.split(';').forEach((cookie) => {
const eqPos = cookie.indexOf('=');
const name = eqPos > -1 ? cookie.substr(0, eqPos).trim() : cookie.trim();
document.cookie = name + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/';
});
// Redirect to login
window.location.href = '/login';
}
// Token validation
isTokenValid(token) {
if (!token) return false;
try {
// Basic JWT validation (for client-side checks only)
const parts = token.split('.');
if (parts.length !== 3) return false;
const payload = JSON.parse(atob(parts[1]));
return payload.exp * 1000 > Date.now();
} catch {
return false;
}
}
}
Secure Data Handling
class SecureDataHandler {
// Encrypt sensitive data before storing
static async encryptData(data, password) {
const encoder = new TextEncoder();
const dataBuffer = encoder.encode(JSON.stringify(data));
// Derive key from password
const keyMaterial = await window.crypto.subtle.importKey(
'raw',
encoder.encode(password),
{ name: 'PBKDF2' },
false,
['deriveKey']
);
const salt = window.crypto.getRandomValues(new Uint8Array(16));
const key = await window.crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt,
iterations: 100000,
hash: 'SHA-256',
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt']
);
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const encrypted = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
dataBuffer
);
return {
encrypted: Array.from(new Uint8Array(encrypted)),
salt: Array.from(salt),
iv: Array.from(iv),
};
}
// Decrypt stored data
static async decryptData(encryptedData, password) {
const encoder = new TextEncoder();
const keyMaterial = await window.crypto.subtle.importKey(
'raw',
encoder.encode(password),
{ name: 'PBKDF2' },
false,
['deriveKey']
);
const key = await window.crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: new Uint8Array(encryptedData.salt),
iterations: 100000,
hash: 'SHA-256',
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false,
['decrypt']
);
const decrypted = await window.crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: new Uint8Array(encryptedData.iv) },
key,
new Uint8Array(encryptedData.encrypted)
);
const decoder = new TextDecoder();
return JSON.parse(decoder.decode(decrypted));
}
// Secure random string generation
static generateSecureRandom(length = 32) {
const array = new Uint8Array(length);
window.crypto.getRandomValues(array);
return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(
''
);
}
// Hash sensitive data
static async hashData(data) {
const encoder = new TextEncoder();
const dataBuffer = encoder.encode(data);
const hashBuffer = await window.crypto.subtle.digest('SHA-256', dataBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
}
// Secure comparison to prevent timing attacks
static secureCompare(a, b) {
if (a.length !== b.length) {
return false;
}
let result = 0;
for (let i = 0; i < a.length; i++) {
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return result === 0;
}
// Clear sensitive data from memory
static clearSensitiveData(obj) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'string') {
// Overwrite string (though this may not work in all JS engines)
obj[key] = '0'.repeat(obj[key].length);
}
delete obj[key];
}
}
}
}
Content Security Policy
class CSPManager {
static generateCSP(options = {}) {
const defaultCSP = {
'default-src': ["'self'"],
'script-src': ["'self'"],
'style-src': ["'self'", "'unsafe-inline'"],
'img-src': ["'self'", 'data:', 'https:'],
'font-src': ["'self'"],
'connect-src': ["'self'"],
'frame-src': ["'none'"],
'object-src': ["'none'"],
'base-uri': ["'self'"],
'form-action': ["'self'"],
'frame-ancestors': ["'none'"],
'upgrade-insecure-requests': [],
};
const csp = { ...defaultCSP, ...options };
return Object.entries(csp)
.map(([directive, sources]) => {
if (sources.length === 0) {
return directive;
}
return `${directive} ${sources.join(' ')}`;
})
.join('; ');
}
static applyCSP(cspString) {
const meta = document.createElement('meta');
meta.httpEquiv = 'Content-Security-Policy';
meta.content = cspString;
document.head.appendChild(meta);
}
static setupSecurityHeaders() {
// These would typically be set by the server, but can be enforced client-side too
const securityMeta = [
{
httpEquiv: 'X-Content-Type-Options',
content: 'nosniff',
},
{
httpEquiv: 'X-Frame-Options',
content: 'DENY',
},
{
httpEquiv: 'X-XSS-Protection',
content: '1; mode=block',
},
{
httpEquiv: 'Referrer-Policy',
content: 'strict-origin-when-cross-origin',
},
];
securityMeta.forEach((meta) => {
const element = document.createElement('meta');
element.httpEquiv = meta.httpEquiv;
element.content = meta.content;
document.head.appendChild(element);
});
}
}
Security Monitoring
class SecurityMonitor {
constructor() {
this.violations = [];
this.suspiciousActivity = [];
this.setupMonitoring();
}
setupMonitoring() {
// CSP violation monitoring
document.addEventListener('securitypolicyviolation', (event) => {
this.handleCSPViolation(event);
});
// Monitor for suspicious DOM manipulation
this.setupDOMMonitoring();
// Monitor network requests
this.setupNetworkMonitoring();
// Monitor for unusual user behavior
this.setupBehaviorMonitoring();
}
handleCSPViolation(event) {
const violation = {
type: 'csp_violation',
directive: event.violatedDirective,
blockedURI: event.blockedURI,
sourceFile: event.sourceFile,
lineNumber: event.lineNumber,
timestamp: new Date().toISOString(),
};
this.violations.push(violation);
console.warn('CSP Violation:', violation);
// Report to server
this.reportViolation(violation);
}
setupDOMMonitoring() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
this.checkSuspiciousElement(node);
}
});
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
}
checkSuspiciousElement(element) {
// Check for suspicious scripts
if (element.tagName === 'SCRIPT') {
const suspicious = {
type: 'suspicious_script',
src: element.src,
innerHTML: element.innerHTML.substring(0, 100),
timestamp: new Date().toISOString(),
};
this.suspiciousActivity.push(suspicious);
console.warn('Suspicious script detected:', suspicious);
}
// Check for suspicious iframes
if (element.tagName === 'IFRAME') {
const suspicious = {
type: 'suspicious_iframe',
src: element.src,
timestamp: new Date().toISOString(),
};
this.suspiciousActivity.push(suspicious);
console.warn('Suspicious iframe detected:', suspicious);
}
}
setupNetworkMonitoring() {
// Monitor fetch requests
const originalFetch = window.fetch;
window.fetch = async (...args) => {
const url = args[0];
const options = args[1] || {};
// Check for suspicious requests
if (this.isSuspiciousRequest(url, options)) {
const suspicious = {
type: 'suspicious_request',
url: url.toString(),
method: options.method || 'GET',
timestamp: new Date().toISOString(),
};
this.suspiciousActivity.push(suspicious);
console.warn('Suspicious request detected:', suspicious);
}
return originalFetch(...args);
};
}
isSuspiciousRequest(url, options) {
const urlString = url.toString();
// Check for common attack patterns
const suspiciousPatterns = [
/javascript:/i,
/data:text\/html/i,
/vbscript:/i,
/<script/i,
/eval\(/i,
/document\.write/i,
];
return suspiciousPatterns.some((pattern) => pattern.test(urlString));
}
setupBehaviorMonitoring() {
let clickCount = 0;
let keyCount = 0;
const timeWindow = 5000; // 5 seconds
document.addEventListener('click', () => {
clickCount++;
setTimeout(() => clickCount--, timeWindow);
if (clickCount > 20) {
// Unusually high click rate
this.reportSuspiciousBehavior('high_click_rate', { clickCount });
}
});
document.addEventListener('keydown', () => {
keyCount++;
setTimeout(() => keyCount--, timeWindow);
if (keyCount > 100) {
// Unusually high key press rate
this.reportSuspiciousBehavior('high_key_rate', { keyCount });
}
});
}
reportSuspiciousBehavior(type, data) {
const behavior = {
type,
data,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
url: window.location.href,
};
this.suspiciousActivity.push(behavior);
console.warn('Suspicious behavior detected:', behavior);
}
async reportViolation(violation) {
try {
await fetch('/api/security/violations', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(violation),
});
} catch (error) {
console.error('Failed to report violation:', error);
}
}
getSecurityReport() {
return {
violations: this.violations,
suspiciousActivity: this.suspiciousActivity,
generatedAt: new Date().toISOString(),
};
}
}
Best Practices Summary
// Security checklist and implementation
class SecurityChecklist {
static getChecklist() {
return {
inputValidation: {
description: 'Validate and sanitize all user inputs',
implemented: false,
priority: 'critical',
},
outputEncoding: {
description: 'Encode output to prevent XSS',
implemented: false,
priority: 'critical',
},
csrfProtection: {
description: 'Implement CSRF tokens',
implemented: false,
priority: 'high',
},
secureAuthentication: {
description: 'Use secure authentication methods',
implemented: false,
priority: 'critical',
},
httpsOnly: {
description: 'Use HTTPS for all communications',
implemented: false,
priority: 'critical',
},
cspImplemented: {
description: 'Implement Content Security Policy',
implemented: false,
priority: 'high',
},
secureStorage: {
description: 'Store sensitive data securely',
implemented: false,
priority: 'high',
},
rateLimiting: {
description: 'Implement rate limiting',
implemented: false,
priority: 'medium',
},
securityHeaders: {
description: 'Set appropriate security headers',
implemented: false,
priority: 'high',
},
dependencyChecks: {
description: 'Regularly check for vulnerable dependencies',
implemented: false,
priority: 'medium',
},
};
}
static auditSecurity() {
const checklist = this.getChecklist();
// Check for XSS vulnerabilities
checklist.outputEncoding.implemented = this.checkXSSProtection();
// Check for CSRF protection
checklist.csrfProtection.implemented = this.checkCSRFProtection();
// Check HTTPS usage
checklist.httpsOnly.implemented = location.protocol === 'https:';
// Check CSP implementation
checklist.cspImplemented.implemented = this.checkCSP();
return checklist;
}
static checkXSSProtection() {
// Basic check for innerHTML usage
return !document.body.innerHTML.includes('<script>');
}
static checkCSRFProtection() {
// Check if CSRF tokens are present in forms
const forms = document.querySelectorAll('form');
return Array.from(forms).some((form) =>
form.querySelector('input[name="csrf_token"]')
);
}
static checkCSP() {
const cspMeta = document.querySelector(
'meta[http-equiv="Content-Security-Policy"]'
);
return !!cspMeta;
}
}
// Initialize security measures
document.addEventListener('DOMContentLoaded', () => {
// Setup CSP
CSPManager.setupSecurityHeaders();
// Initialize security monitoring
const securityMonitor = new SecurityMonitor();
// Setup CSRF protection
const csrfProtection = new CSRFProtection();
csrfProtection.protectForms();
csrfProtection.protectAjaxRequests();
// Run security audit
const audit = SecurityChecklist.auditSecurity();
console.log('Security Audit:', audit);
});
Conclusion
JavaScript security requires a multi-layered approach combining input validation, output encoding, secure authentication, and proper data handling. By implementing these security practices, monitoring for threats, and regularly auditing your applications, you can build robust defenses against common web vulnerabilities. Remember that security is an ongoing process that requires constant vigilance and updates as new threats emerge.