javascript
examples
examples.js⚡javascript
/**
* 21.1 Security Fundamentals - Examples
*
* Demonstrates common security vulnerabilities and prevention techniques
* WARNING: These examples show vulnerable code for educational purposes.
* Never use vulnerable patterns in production!
*/
// ============================================
// XSS (CROSS-SITE SCRIPTING) EXAMPLES
// ============================================
console.log('=== XSS Prevention Examples ===\n');
/**
* HTML Escaping - Prevents XSS by encoding special characters
*/
const HTMLEscaper = {
// Character entity map
entities: {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/',
'`': '`',
'=': '=',
},
// Escape HTML special characters
escape(str) {
if (typeof str !== 'string') return str;
return str.replace(/[&<>"'`=/]/g, (char) => this.entities[char]);
},
// Unescape HTML entities
unescape(str) {
if (typeof str !== 'string') return str;
const reverseEntities = {};
for (const [char, entity] of Object.entries(this.entities)) {
reverseEntities[entity] = char;
}
return str.replace(
/&(?:amp|lt|gt|quot|#x27|#x2F|#x60|#x3D);/g,
(entity) => reverseEntities[entity] || entity
);
},
};
// Demo
console.log('HTML Escaping:');
const maliciousInput = '<script>alert("XSS")</script>';
console.log(' Original:', maliciousInput);
console.log(' Escaped:', HTMLEscaper.escape(maliciousInput));
console.log('');
/**
* Safe DOM manipulation utilities
*/
const SafeDOM = {
// Create text node (always safe)
createText(text) {
return document.createTextNode(text);
},
// Set element text content safely
setText(element, text) {
element.textContent = text;
return element;
},
// Create element with safe attributes
createElement(tag, attributes = {}, textContent = '') {
const element = document.createElement(tag);
for (const [key, value] of Object.entries(attributes)) {
// Validate attribute names
if (!/^[a-zA-Z][\w-]*$/.test(key)) {
throw new Error(`Invalid attribute name: ${key}`);
}
// Handle special attributes
if (key === 'href' || key === 'src') {
if (!this.isValidUrl(value)) {
throw new Error(`Invalid URL for ${key}`);
}
}
// Handle event handlers - reject them
if (key.startsWith('on')) {
throw new Error('Event handler attributes not allowed');
}
element.setAttribute(key, value);
}
if (textContent) {
element.textContent = textContent;
}
return element;
},
// Validate URL (prevent javascript: protocol)
isValidUrl(url) {
if (!url) return false;
try {
const parsed = new URL(url, 'https://example.com');
return ['http:', 'https:', 'mailto:'].includes(parsed.protocol);
} catch {
// Relative URLs are OK
return !url.toLowerCase().includes('javascript:');
}
},
// Sanitize HTML (very basic - use DOMPurify in production)
sanitizeHTML(html) {
// Remove script tags
let clean = html.replace(
/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
''
);
// Remove event handlers
clean = clean.replace(/\s*on\w+\s*=\s*("[^"]*"|'[^']*'|[^\s>]*)/gi, '');
// Remove javascript: URLs
clean = clean.replace(/javascript\s*:/gi, '');
return clean;
},
};
console.log('Safe URL Validation:');
console.log(
' https://example.com:',
SafeDOM.isValidUrl('https://example.com')
);
console.log(
' javascript:alert(1):',
SafeDOM.isValidUrl('javascript:alert(1)')
);
console.log(' /relative/path:', SafeDOM.isValidUrl('/relative/path'));
console.log('');
// ============================================
// CSRF PROTECTION EXAMPLES
// ============================================
console.log('=== CSRF Protection Examples ===\n');
/**
* CSRF Token Generator and Validator
*/
class CSRFProtection {
constructor() {
this.tokens = new Map();
this.tokenLifetime = 3600000; // 1 hour
}
// Generate secure random token
generateToken() {
// In browser, use crypto.getRandomValues
// In Node.js, use crypto.randomBytes
const array = new Uint8Array(32);
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
crypto.getRandomValues(array);
} else {
// Fallback for Node.js without webcrypto
const nodeCrypto = require('crypto');
const buffer = nodeCrypto.randomBytes(32);
array.set(buffer);
}
return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(
''
);
}
// Create token for a session
createToken(sessionId) {
const token = this.generateToken();
const expiry = Date.now() + this.tokenLifetime;
this.tokens.set(sessionId, { token, expiry });
return token;
}
// Validate token
validateToken(sessionId, token) {
const stored = this.tokens.get(sessionId);
if (!stored) {
return { valid: false, reason: 'No token found for session' };
}
if (Date.now() > stored.expiry) {
this.tokens.delete(sessionId);
return { valid: false, reason: 'Token expired' };
}
// Constant-time comparison to prevent timing attacks
if (!this.timingSafeEqual(token, stored.token)) {
return { valid: false, reason: 'Token mismatch' };
}
return { valid: true };
}
// Constant-time string comparison
timingSafeEqual(a, b) {
if (typeof a !== 'string' || typeof b !== 'string') {
return false;
}
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;
}
// Rotate token (call after successful validation)
rotateToken(sessionId) {
if (this.tokens.has(sessionId)) {
return this.createToken(sessionId);
}
return null;
}
// Cleanup expired tokens
cleanup() {
const now = Date.now();
for (const [sessionId, data] of this.tokens) {
if (now > data.expiry) {
this.tokens.delete(sessionId);
}
}
}
}
// Demo CSRF protection
const csrf = new CSRFProtection();
const sessionId = 'user-123';
const token = csrf.createToken(sessionId);
console.log('CSRF Token generated:', token.substring(0, 20) + '...');
console.log('Validate correct token:', csrf.validateToken(sessionId, token));
console.log(
'Validate wrong token:',
csrf.validateToken(sessionId, 'invalid-token')
);
console.log('');
/**
* Fetch wrapper with CSRF protection
*/
function createSecureFetch(csrfToken) {
return async function secureFetch(url, options = {}) {
const isModifyingMethod = ['POST', 'PUT', 'DELETE', 'PATCH'].includes(
options.method?.toUpperCase()
);
if (isModifyingMethod) {
options.headers = {
...options.headers,
'X-CSRF-Token': csrfToken,
};
}
// Include credentials for same-origin requests
options.credentials = options.credentials || 'same-origin';
return fetch(url, options);
};
}
console.log('Secure fetch wrapper created with CSRF token');
console.log('');
// ============================================
// INPUT VALIDATION AND SANITIZATION
// ============================================
console.log('=== Input Validation Examples ===\n');
/**
* Comprehensive input validator
*/
class InputValidator {
// Validate email
static isValidEmail(email) {
if (typeof email !== 'string') return false;
// RFC 5322 simplified regex
const emailRegex =
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
return emailRegex.test(email) && email.length <= 254;
}
// Validate URL
static isValidUrl(url, allowedProtocols = ['http:', 'https:']) {
try {
const parsed = new URL(url);
return allowedProtocols.includes(parsed.protocol);
} catch {
return false;
}
}
// Validate integer
static isValidInteger(value, min = -Infinity, max = Infinity) {
const num = Number(value);
return Number.isInteger(num) && num >= min && num <= max;
}
// Validate string length
static isValidLength(str, minLen = 0, maxLen = Infinity) {
return (
typeof str === 'string' && str.length >= minLen && str.length <= maxLen
);
}
// Validate alphanumeric
static isAlphanumeric(str) {
return typeof str === 'string' && /^[a-zA-Z0-9]+$/.test(str);
}
// Validate against whitelist
static isInWhitelist(value, whitelist) {
return whitelist.includes(value);
}
// Validate JSON
static isValidJSON(str) {
try {
JSON.parse(str);
return true;
} catch {
return false;
}
}
// Validate file extension
static isValidFileExtension(filename, allowedExtensions) {
const ext = filename.toLowerCase().split('.').pop();
return allowedExtensions.includes(ext);
}
// Validate path (no traversal)
static isValidPath(path) {
if (typeof path !== 'string') return false;
// Check for path traversal
const normalized = path.replace(/\\/g, '/');
return !normalized.includes('..') && !normalized.startsWith('/');
}
}
console.log('Input Validation:');
console.log(' Valid email:', InputValidator.isValidEmail('user@example.com'));
console.log(' Invalid email:', InputValidator.isValidEmail('not-an-email'));
console.log(' Valid URL:', InputValidator.isValidUrl('https://example.com'));
console.log(
' JavaScript URL:',
InputValidator.isValidUrl('javascript:alert(1)')
);
console.log(
' Path traversal:',
InputValidator.isValidPath('../../../etc/passwd')
);
console.log(' Safe path:', InputValidator.isValidPath('uploads/image.jpg'));
console.log('');
/**
* Input sanitizer
*/
class InputSanitizer {
// Remove control characters
static removeControlChars(str) {
if (typeof str !== 'string') return str;
// eslint-disable-next-line no-control-regex
return str.replace(/[\x00-\x1F\x7F]/g, '');
}
// Trim and normalize whitespace
static normalizeWhitespace(str) {
if (typeof str !== 'string') return str;
return str.trim().replace(/\s+/g, ' ');
}
// Remove null bytes
static removeNullBytes(str) {
if (typeof str !== 'string') return str;
return str.replace(/\0/g, '');
}
// Truncate string
static truncate(str, maxLength) {
if (typeof str !== 'string') return str;
return str.slice(0, maxLength);
}
// Sanitize filename
static sanitizeFilename(filename) {
if (typeof filename !== 'string') return '';
// Remove path components
filename = filename.split(/[\\/]/).pop() || '';
// Remove dangerous characters
filename = filename.replace(/[<>:"/\\|?*\x00-\x1F]/g, '');
// Remove leading dots
filename = filename.replace(/^\.+/, '');
// Truncate
filename = filename.slice(0, 255);
return filename || 'unnamed';
}
// Sanitize for SQL LIKE clause
static escapeSqlLike(str) {
if (typeof str !== 'string') return str;
return str.replace(/[%_\\]/g, '\\$&');
}
// Sanitize for regex
static escapeRegex(str) {
if (typeof str !== 'string') return str;
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
}
console.log('Input Sanitization:');
console.log(
' Control chars removed:',
InputSanitizer.removeControlChars('Hello\x00World\x1F!')
);
console.log(
' Filename sanitized:',
InputSanitizer.sanitizeFilename('../../../etc/passwd')
);
console.log(
' Dangerous filename:',
InputSanitizer.sanitizeFilename('<script>alert("xss")</script>.js')
);
console.log(' SQL LIKE escaped:', InputSanitizer.escapeSqlLike('100%'));
console.log('');
// ============================================
// INJECTION PREVENTION
// ============================================
console.log('=== Injection Prevention Examples ===\n');
/**
* SQL Query Builder (parameterized)
*/
class SafeQueryBuilder {
constructor() {
this.query = '';
this.params = [];
this.paramIndex = 0;
}
// Start SELECT
select(...columns) {
const safeColumns = columns.map((col) => {
// Only allow valid column names
if (!/^[\w.]+$/.test(col)) {
throw new Error(`Invalid column name: ${col}`);
}
return col;
});
this.query = `SELECT ${safeColumns.join(', ')}`;
return this;
}
// FROM clause
from(table) {
// Validate table name
if (!/^\w+$/.test(table)) {
throw new Error(`Invalid table name: ${table}`);
}
this.query += ` FROM ${table}`;
return this;
}
// WHERE clause with parameterized values
where(column, operator, value) {
// Validate column
if (!/^[\w.]+$/.test(column)) {
throw new Error(`Invalid column name: ${column}`);
}
// Validate operator
const allowedOperators = [
'=',
'!=',
'<>',
'>',
'<',
'>=',
'<=',
'LIKE',
'IN',
];
if (!allowedOperators.includes(operator.toUpperCase())) {
throw new Error(`Invalid operator: ${operator}`);
}
// Use parameterized value
this.paramIndex++;
const placeholder = `$${this.paramIndex}`;
this.query += ` WHERE ${column} ${operator} ${placeholder}`;
this.params.push(value);
return this;
}
// AND clause
and(column, operator, value) {
if (!/^[\w.]+$/.test(column)) {
throw new Error(`Invalid column name: ${column}`);
}
this.paramIndex++;
const placeholder = `$${this.paramIndex}`;
this.query += ` AND ${column} ${operator} ${placeholder}`;
this.params.push(value);
return this;
}
// Build final query
build() {
return {
query: this.query,
params: this.params,
};
}
}
// Demo safe query building
const queryBuilder = new SafeQueryBuilder();
const result = queryBuilder
.select('id', 'username', 'email')
.from('users')
.where('username', '=', 'john')
.and('active', '=', true)
.build();
console.log('Safe Query Builder:');
console.log(' Query:', result.query);
console.log(' Params:', result.params);
console.log('');
/**
* Command execution sanitizer
*/
class SafeCommand {
// Escape shell argument
static escapeShellArg(arg) {
if (typeof arg !== 'string') {
arg = String(arg);
}
// In Unix: wrap in single quotes and escape single quotes
return `'${arg.replace(/'/g, "'\\''")}'`;
}
// Validate command name against whitelist
static isAllowedCommand(cmd, whitelist) {
return whitelist.includes(cmd);
}
// Build safe command with arguments
static buildCommand(cmd, args, whitelist) {
if (!this.isAllowedCommand(cmd, whitelist)) {
throw new Error(`Command not allowed: ${cmd}`);
}
const escapedArgs = args.map((arg) => this.escapeShellArg(arg));
return `${cmd} ${escapedArgs.join(' ')}`;
}
}
console.log('Safe Command Execution:');
const allowedCommands = ['ls', 'cat', 'echo'];
try {
const safeCmd = SafeCommand.buildCommand(
'echo',
['Hello; rm -rf /', '$(whoami)'],
allowedCommands
);
console.log(' Safe command:', safeCmd);
} catch (e) {
console.log(' Error:', e.message);
}
console.log('');
// ============================================
// CONTENT SECURITY POLICY BUILDER
// ============================================
console.log('=== Content Security Policy Builder ===\n');
/**
* CSP Header Builder
*/
class CSPBuilder {
constructor() {
this.directives = new Map();
}
// Add directive
addDirective(name, ...values) {
const existing = this.directives.get(name) || [];
this.directives.set(name, [...existing, ...values]);
return this;
}
// Common presets
defaultSrc(...values) {
return this.addDirective('default-src', ...values);
}
scriptSrc(...values) {
return this.addDirective('script-src', ...values);
}
styleSrc(...values) {
return this.addDirective('style-src', ...values);
}
imgSrc(...values) {
return this.addDirective('img-src', ...values);
}
connectSrc(...values) {
return this.addDirective('connect-src', ...values);
}
fontSrc(...values) {
return this.addDirective('font-src', ...values);
}
frameSrc(...values) {
return this.addDirective('frame-src', ...values);
}
frameAncestors(...values) {
return this.addDirective('frame-ancestors', ...values);
}
formAction(...values) {
return this.addDirective('form-action', ...values);
}
baseUri(...values) {
return this.addDirective('base-uri', ...values);
}
objectSrc(...values) {
return this.addDirective('object-src', ...values);
}
reportUri(uri) {
return this.addDirective('report-uri', uri);
}
upgradeInsecureRequests() {
return this.addDirective('upgrade-insecure-requests');
}
// Generate nonce
static generateNonce() {
const array = new Uint8Array(16);
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
crypto.getRandomValues(array);
} else {
const nodeCrypto = require('crypto');
const buffer = nodeCrypto.randomBytes(16);
array.set(buffer);
}
return Buffer.from(array).toString('base64');
}
// Build header value
build() {
const parts = [];
for (const [directive, values] of this.directives) {
if (values.length === 0) {
parts.push(directive);
} else {
parts.push(`${directive} ${values.join(' ')}`);
}
}
return parts.join('; ');
}
// Common secure presets
static strict() {
const nonce = CSPBuilder.generateNonce();
return {
policy: new CSPBuilder()
.defaultSrc("'self'")
.scriptSrc("'self'", `'nonce-${nonce}'`)
.styleSrc("'self'", "'unsafe-inline'")
.imgSrc("'self'", 'data:', 'https:')
.fontSrc("'self'")
.connectSrc("'self'")
.frameAncestors("'none'")
.baseUri("'self'")
.formAction("'self'")
.objectSrc("'none'")
.build(),
nonce,
};
}
}
// Demo CSP Builder
const cspBuilder = new CSPBuilder()
.defaultSrc("'self'")
.scriptSrc("'self'", "'nonce-abc123'")
.styleSrc("'self'", "'unsafe-inline'")
.imgSrc("'self'", 'data:', 'https://cdn.example.com')
.connectSrc("'self'", 'https://api.example.com')
.frameAncestors("'none'")
.baseUri("'self'");
console.log('CSP Header:');
console.log(' ' + cspBuilder.build());
console.log('');
// ============================================
// SECURITY HEADERS CONFIGURATION
// ============================================
console.log('=== Security Headers Configuration ===\n');
/**
* Security headers configuration for different environments
*/
const SecurityHeaders = {
// Production headers (strict)
production: {
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'geolocation=(), microphone=(), camera=()',
'Content-Security-Policy': CSPBuilder.strict().policy,
},
// Development headers (relaxed)
development: {
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'SAMEORIGIN',
},
// Apply headers to response
apply(res, env = 'production') {
const headers = this[env] || this.production;
for (const [key, value] of Object.entries(headers)) {
res.setHeader(key, value);
}
},
// Generate middleware
middleware(env = 'production') {
const headers = this[env] || this.production;
return (req, res, next) => {
for (const [key, value] of Object.entries(headers)) {
res.setHeader(key, value);
}
next();
};
},
};
console.log('Production Security Headers:');
for (const [header, value] of Object.entries(SecurityHeaders.production)) {
const displayValue =
value.length > 60 ? value.substring(0, 60) + '...' : value;
console.log(` ${header}: ${displayValue}`);
}
console.log('');
// ============================================
// SECURE DATA HANDLING
// ============================================
console.log('=== Secure Data Handling ===\n');
/**
* Secure data masking for logs
*/
class DataMasker {
static patterns = {
email: {
regex: /([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g,
mask: (match, local, domain) => {
const maskedLocal = local[0] + '*'.repeat(local.length - 1);
return `${maskedLocal}@${domain}`;
},
},
creditCard: {
regex: /\b(\d{4})[\s-]?(\d{4})[\s-]?(\d{4})[\s-]?(\d{4})\b/g,
mask: (match, g1, g2, g3, g4) => `****-****-****-${g4}`,
},
ssn: {
regex: /\b(\d{3})[-\s]?(\d{2})[-\s]?(\d{4})\b/g,
mask: () => '***-**-****',
},
phone: {
regex: /\b(\+\d{1,3}[-\s]?)?(\(?\d{3}\)?[-\s]?)(\d{3}[-\s]?)(\d{4})\b/g,
mask: (match, country, area, mid, last) =>
`${country || ''}***-***-${last}`,
},
ipv4: {
regex: /\b(\d{1,3}\.\d{1,3})\.\d{1,3}\.\d{1,3}\b/g,
mask: (match, first) => `${first}.xxx.xxx`,
},
};
static mask(text, types = Object.keys(this.patterns)) {
let masked = text;
for (const type of types) {
const pattern = this.patterns[type];
if (pattern) {
masked = masked.replace(pattern.regex, pattern.mask);
}
}
return masked;
}
static maskObject(
obj,
sensitiveFields = ['password', 'token', 'secret', 'key', 'auth']
) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const masked = Array.isArray(obj) ? [] : {};
for (const [key, value] of Object.entries(obj)) {
const lowerKey = key.toLowerCase();
// Check if field name indicates sensitive data
const isSensitive = sensitiveFields.some((field) =>
lowerKey.includes(field.toLowerCase())
);
if (isSensitive) {
masked[key] = '[REDACTED]';
} else if (typeof value === 'object' && value !== null) {
masked[key] = this.maskObject(value, sensitiveFields);
} else if (typeof value === 'string') {
masked[key] = this.mask(value);
} else {
masked[key] = value;
}
}
return masked;
}
}
console.log('Data Masking:');
const sensitiveText = `
User: john@example.com
Card: 4111-1111-1111-1234
SSN: 123-45-6789
Phone: +1 (555) 123-4567
IP: 192.168.1.100
`;
console.log('Original:', sensitiveText);
console.log('Masked:', DataMasker.mask(sensitiveText));
const sensitiveObject = {
user: 'john@example.com',
password: 'secret123',
apiKey: 'sk_live_1234567890',
profile: {
name: 'John Doe',
authToken: 'bearer xyz123',
},
};
console.log(
'\nMasked Object:',
JSON.stringify(DataMasker.maskObject(sensitiveObject), null, 2)
);
console.log('');
// ============================================
// SUMMARY
// ============================================
console.log('=== Security Fundamentals Summary ===\n');
console.log('Key Security Components Demonstrated:');
console.log('1. HTML Escaping - Prevent XSS attacks');
console.log('2. CSRF Protection - Token-based request validation');
console.log('3. Input Validation - Whitelist and type checking');
console.log('4. Input Sanitization - Clean and normalize input');
console.log('5. Injection Prevention - Parameterized queries');
console.log('6. CSP Builder - Content Security Policy configuration');
console.log('7. Security Headers - HTTP security headers');
console.log('8. Data Masking - Protect sensitive data in logs');
console.log('\nRemember: Security is a process, not a feature!');
// Export for testing
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
HTMLEscaper,
SafeDOM,
CSRFProtection,
InputValidator,
InputSanitizer,
SafeQueryBuilder,
SafeCommand,
CSPBuilder,
SecurityHeaders,
DataMasker,
};
}