Advanced Features
Keep your applications secure with YakaJS's built-in XSS protection, CSRF tokens, and input sanitization
Complete guide to building secure applications with YakaJS. Protection against XSS, CSRF, and more!
YakaJS automatically sanitizes HTML to prevent XSS attacks:
// DANGEROUS: User input without sanitizationconst userInput = '<script>alert("XSS")</script>';_('#content').html(userInput); // ⚠️ XSS vulnerability!// SAFE: Sanitize user input_('#content').html(userInput, true); // ✅ Script tags removed!// Result: <script>alert("XSS")</script> becomes empty or safe text// Sanitize HTML stringconst clean = _.security.sanitizeHtml(userInput);console.log(clean); // All dangerous tags removed// Escape HTML entitiesconst escaped = _.security.escapeHtml(userInput);console.log(escaped); // <script> becomes <script>// URL encodingconst safe = _.security.escapeUrl(userUrl);Configure which HTML tags are allowed:
_.security.setAllowedTags([ 'p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3', 'ul', 'ol', 'li', 'a', 'img']);// Sanitize with custom allowed tagsconst clean = _.security.sanitizeHtml(userInput);// Get token from meta tagconst token = _('meta[name="csrf-token"]').attr('content');_.csrf.setToken(token);// Or set directly_.csrf.setToken('your-csrf-token-here');All POST/PUT/DELETE/PATCH requests automatically include the CSRF token:
// Token automatically included in request_.post('/api/data', { foo: 'bar' });// Also works with _.ajax_.ajax({ url: '/api/data', method: 'POST', data: { foo: 'bar' } // CSRF token auto-included!});// Get current tokenconst token = _.csrf.getToken();// Add to form_('#myForm').append( `<input type="hidden" name="_csrf" value="${token}">`);// Refresh token after login_.csrf.refresh('/api/csrf-token');// Or set new token_.csrf.setToken(newToken);// Sanitize text inputfunction sanitizeText(input) { return _.security.escapeHtml(input);}_('#name').on('blur', function() { const clean = sanitizeText(_(this).val()); _(this).val(clean);});function isValidEmail(email) { const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return pattern.test(email);}_('#email').on('blur', function() { const email = _(this).val(); if (!isValidEmail(email)) { _.notify('Invalid email address', 'error'); }});function isValidUrl(url) { try { new URL(url); return true; } catch { return false; }}// Sanitize URLfunction sanitizeUrl(url) { if (!isValidUrl(url)) return ''; const parsed = new URL(url); // Only allow specific protocols if (!['http:', 'https:'].includes(parsed.protocol)) { return ''; } return _.security.escapeUrl(url);}// Set CSP nonce for inline scripts_.security.setNonce('random-nonce-value');// Add nonce to dynamically created scriptsconst script = document.createElement('script');script.nonce = _.security.getNonce();script.textContent = 'console.log("Hello")';document.body.appendChild(script);<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'nonce-{nonce}'">// Store auth token_.storage.set('auth_token', token);// Check if authenticatedfunction isAuthenticated() { return !!_.storage.get('auth_token');}// Require authenticationfunction requireAuth() { if (!isAuthenticated()) { window.location.href = '/login'; }}// Store token securely_.storage.setSecure('auth_token', token);// Get secured tokenconst token = _.storage.getSecure('auth_token');// Remove token on logout_.storage.remove('auth_token');function checkPasswordStrength(password) { const strength = { length: password.length >= 8, hasUpper: /[A-Z]/.test(password), hasLower: /[a-z]/.test(password), hasNumber: /\d/.test(password), hasSpecial: /[@$!%*?&]/.test(password) }; const score = Object.values(strength).filter(Boolean).length; return { score, strength: score < 3 ? 'weak' : score < 5 ? 'medium' : 'strong', requirements: strength };}_('#password').on('input', function() { const result = checkPasswordStrength(_(this).val()); _('#strength').text(result.strength) .removeClass() .addClass('strength-' + result.strength);});function validateFileType(file, allowedTypes) { return allowedTypes.includes(file.type);}_('#fileInput').on('change', function(e) { const file = e.target.files[0]; const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']; if (!validateFileType(file, allowedTypes)) { _.notify('Invalid file type. Only images allowed.', 'error'); _(this).val(''); // Clear input return; } // File is valid, proceed with upload uploadFile(file);});function validateFileSize(file, maxSizeMB) { const maxBytes = maxSizeMB * 1024 * 1024; return file.size <= maxBytes;}_('#fileInput').on('change', function(e) { const file = e.target.files[0]; if (!validateFileSize(file, 5)) { // 5 MB max _.notify('File too large. Maximum 5 MB allowed.', 'error'); _(this).val(''); return; }});When building API queries on the backend, always use parameterized queries. On the frontend:
// NEVER build queries like this:const query = `SELECT * FROM users WHERE name = '${userName}'`; // ⚠️ Vulnerable!// Instead, send data to API and let backend handle it:_.post('/api/users/search', { name: userName // Let backend sanitize and use parameterized queries});Configure these headers on your server:
X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockStrict-Transport-Security: max-age=31536000; includeSubDomainsContent-Security-Policy: default-src 'self'Referrer-Policy: no-referrer// Verify security headersasync function checkSecurityHeaders() { const response = await fetch('/'); const headers = response.headers; const required = [ 'x-content-type-options', 'x-frame-options', 'strict-transport-security' ]; required.forEach(header => { if (!headers.get(header)) { console.warn(`Missing security header: ${header}`); } });}class RateLimiter { constructor(maxRequests, timeWindow) { this.maxRequests = maxRequests; this.timeWindow = timeWindow; // in milliseconds this.requests = []; } canMakeRequest() { const now = Date.now(); this.requests = this.requests.filter( time => now - time < this.timeWindow ); return this.requests.length < this.maxRequests; } recordRequest() { this.requests.push(Date.now()); }}// Allow 10 requests per minuteconst limiter = new RateLimiter(10, 60000);function makeAPIRequest(url, data) { if (!limiter.canMakeRequest()) { _.notify('Too many requests. Please wait.', 'warning'); return; } limiter.recordRequest(); return _.post(url, data);}_('#secureForm').on('submit', async function(e) { e.preventDefault(); // 1. Validate input const result = _(this).validate({ email: { required: true, email: true, requiredMessage: 'Email is required' }, password: { required: true, minLength: 8, requiredMessage: 'Password is required' } }); if (!result.valid) { _.notify('Please fix errors', 'error'); return; } // 2. Sanitize input const formData = _(this).serializeObject(); Object.keys(formData).forEach(key => { formData[key] = _.security.escapeHtml(formData[key]); }); // 3. Disable submit button const $btn = _('button[type="submit"]'); $btn.prop('disabled', true).text('Submitting...'); try { // 4. Submit with CSRF token (auto-included) const response = await _.post('/api/submit', formData); _.notify('Success!', 'success'); // 5. Clear form _(this)[0].reset(); } catch (error) { _.notify('Submission failed', 'error'); } finally { // 6. Re-enable button $btn.prop('disabled', false).text('Submit'); }});// ALWAYS sanitize before displaying_('#content').html(userInput, true);// Or manually sanitizeconst clean = _.security.sanitizeHtml(userInput);_('#content').html(clean);// Check if using HTTPSif (location.protocol !== 'https:') { console.warn('⚠️ Not using HTTPS! Data not encrypted.');}// Redirect to HTTPSif (location.protocol === 'http:' && location.hostname !== 'localhost') { location.href = 'https:' + window.location.href.substring(window.location.protocol.length);}// Client-side AND server-side validation_('#form').validate(rules); // Client-side// Server also validates!_.post('/api/submit', data); // Server validates again<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'nonce-{nonce}'">// Set token on page loadconst token = _('meta[name="csrf-token"]').attr('content');_.csrf.setToken(token);// All POST requests include token automatically// Use secure storage_.storage.setSecure('auth_token', token);// Never store passwords// Never store credit cards// Use tokens, not credentialsfunction isValidUrl(url) { try { const parsed = new URL(url); return ['http:', 'https:'].includes(parsed.protocol); } catch { return false; }}// Only use valid URLsif (isValidUrl(userUrl)) { window.location.href = userUrl;}// Check if page is in iframeif (window.top !== window.self) { // Page is in iframe - potential clickjacking console.warn('⚠️ Page loaded in iframe'); window.top.location = window.self.location;}// Store auth token securely_.storage.setSecure('auth_token', token);// Include in requests_.ajax({ url: '/api/protected', headers: { 'Authorization': 'Bearer ' + _.storage.getSecure('auth_token') }});// Clear on logoutfunction logout() { _.storage.remove('auth_token'); _.csrf.clear(); window.location.href = '/login';}// Detect suspicious activitywindow.addEventListener('error', function(e) { // Log client-side errors if (e.message.includes('script')) { console.error('Potential XSS attempt:', e); }});// Detect devtoolslet devtoolsOpen = false;setInterval(function() { const threshold = 160; const widthThreshold = window.outerWidth - window.innerWidth > threshold; const heightThreshold = window.outerHeight - window.innerHeight > threshold; if (widthThreshold || heightThreshold) { if (!devtoolsOpen) { console.warn('Developer tools opened'); devtoolsOpen = true; } } else { devtoolsOpen = false; }}, 500);