Frontend Security Vulnerabilities: Complete Guide with Examples and Prevention
β οΈ Frontend Security Alert
Frontend security vulnerabilities can expose sensitive user data and compromise entire applications. This guide covers 8 critical vulnerabilities with practical examples and prevention techniques.
Frontend security is often overlooked, yet it's the first line of defense against cyber attacks. As a Development Lead who has worked on applications serving millions of users, I've encountered and mitigated numerous security vulnerabilities. This comprehensive guide covers the most critical frontend security issues with practical examples and actionable solutions.
π Table of Contents
1. π¨ XSS (Cross-Site Scripting) Vulnerabilities
XSS attacks inject malicious scripts into web applications, allowing attackers to steal user data, hijack sessions, or perform actions on behalf of users.
Vulnerable Code Example
// β VULNERABLE: Direct innerHTML injection
function displayUserComment(comment) {
document.getElementById('comments').innerHTML += `
<div class="comment">${comment}</div>
`;
}
// β React - Dangerous HTML injection
function CommentComponent({ comment }) {
return <div dangerouslySetInnerHTML={{ __html: comment }} />;
}
Secure Implementation
// β
SECURE: Use textContent instead of innerHTML
function displayUserComment(comment) {
const commentDiv = document.createElement('div');
commentDiv.className = 'comment';
commentDiv.textContent = comment; // Automatically escapes HTML
document.getElementById('comments').appendChild(commentDiv);
}
// β
React - Safe text rendering
function CommentComponent({ comment }) {
return <div className="comment">{comment}</div>; // React escapes by default
}
// β
For rich text, use a sanitization library
import DOMPurify from 'dompurify';
function SafeRichComment({ htmlContent }) {
const sanitizedContent = DOMPurify.sanitize(htmlContent);
return <div dangerouslySetInnerHTML={{ __html: sanitizedContent }} />;
}
π‘ Pro Tip: Always validate and sanitize user input on both frontend and backend. Use libraries like DOMPurify for HTML sanitization and implement Content Security Policy (CSP).
2. π‘οΈ CSRF (Cross-Site Request Forgery) Protection
CSRF attacks trick users into performing unwanted actions on applications where they're authenticated. This is particularly dangerous for state-changing operations.
Vulnerable Implementation
// β VULNERABLE: No CSRF protection
async function transferMoney(toAccount, amount) {
const response = await fetch('/api/transfer', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ toAccount, amount })
});
return response.json();
}
Secure Implementation
// β
SECURE: CSRF token implementation
async function transferMoney(toAccount, amount) {
// Get CSRF token from meta tag or API
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content;
const response = await fetch('/api/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken,
'X-Requested-With': 'XMLHttpRequest' // Additional protection
},
credentials: 'same-origin', // Include cookies for same-origin requests
body: JSON.stringify({ toAccount, amount })
});
if (!response.ok) {
throw new Error('Transfer failed');
}
return response.json();
}
// β
React Hook for CSRF protection
function useCSRFToken() {
const [token, setToken] = useState('');
useEffect(() => {
// Fetch CSRF token on component mount
fetch('/api/csrf-token')
.then(res => res.json())
.then(data => setToken(data.token));
}, []);
return token;
}
3. π Content Security Policy (CSP) Implementation
CSP is a security layer that helps detect and mitigate XSS and data injection attacks by controlling which resources the browser is allowed to load.
Basic CSP Configuration
<!-- HTML Meta Tag -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline' https://trusted-cdn.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;">
// Next.js Configuration
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-eval' 'unsafe-inline'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' blob: data:",
"font-src 'self'",
"connect-src 'self'"
].join('; ')
}
]
}
];
}
};
Advanced CSP with Nonce
// Generate unique nonce for each request
function generateNonce() {
return crypto.randomBytes(16).toString('base64');
}
// CSP with nonce
const nonce = generateNonce();
const csp = `
default-src 'self';
script-src 'self' 'nonce-${nonce}';
style-src 'self' 'nonce-${nonce}';
`;
// Use nonce in scripts
<script nonce={${nonce}}>
// Your inline script here
</script>
4. π Authentication & Authorization Vulnerabilities
Weak authentication mechanisms can lead to unauthorized access, session hijacking, and privilege escalation.
Insecure Authentication
// β VULNERABLE: Weak password validation
function validatePassword(password) {
return password.length >= 6; // Too weak!
}
// β Storing tokens in localStorage (XSS vulnerable)
localStorage.setItem('authToken', token);
// β No token expiration check
function isAuthenticated() {
return !!localStorage.getItem('authToken');
}
Secure Authentication
// β
SECURE: Strong password validation
function validatePassword(password) {
const minLength = 12;
const hasUpperCase = /[A-Z]/.test(password);
const hasLowerCase = /[a-z]/.test(password);
const hasNumbers = /d/.test(password);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
return password.length >= minLength &&
hasUpperCase && hasLowerCase &&
hasNumbers && hasSpecialChar;
}
// β
Secure token storage using httpOnly cookies
function setAuthToken(token) {
// Set via server-side with httpOnly, secure, sameSite flags
document.cookie = `authToken=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`;
}
// β
JWT token validation with expiration
function isAuthenticated() {
const token = getTokenFromSecureCookie();
if (!token) return false;
try {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.exp * 1000 > Date.now(); // Check expiration
} catch {
return false;
}
}
// β
Role-based access control
function hasPermission(user, resource, action) {
return user.roles.some(role =>
role.permissions.includes(`${resource}:${action}`)
);
}
5. β Input Validation & Sanitization
Proper input validation prevents injection attacks and ensures data integrity.
Comprehensive Input Validation
// β
Email validation with regex and DNS check
function validateEmail(email) {
const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;
if (!emailRegex.test(email)) return false;
// Additional checks
const domain = email.split('@')[1];
return domain.length > 0 && !domain.includes('..');
}
// β
SQL injection prevention for search queries
function sanitizeSearchQuery(query) {
return query
.replace(/[<>]/g, '') // Remove angle brackets
.replace(/['";]/g, '') // Remove quotes and semicolons
.trim()
.substring(0, 100); // Limit length
}
// β
File upload validation
function validateFileUpload(file) {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
const maxSize = 5 * 1024 * 1024; // 5MB
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif'];
const fileExtension = file.name.toLowerCase().substring(file.name.lastIndexOf('.'));
return allowedTypes.includes(file.type) &&
file.size <= maxSize &&
allowedExtensions.includes(fileExtension);
}
// β
React form validation with Zod
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(2).max(50).regex(/^[a-zA-Zs]+$/),
email: z.string().email(),
age: z.number().min(13).max(120),
website: z.string().url().optional()
});
function validateUserData(data) {
try {
return userSchema.parse(data);
} catch (error) {
throw new Error(`Validation failed: ${error.message}`);
}
}
6. πΎ Secure vs Insecure Data Storage
Proper data storage practices protect sensitive information from unauthorized access.
Storage Security Comparison
β Insecure Storage
// Storing sensitive data in localStorage
localStorage.setItem('creditCard', '4111-1111-1111-1111');
localStorage.setItem('ssn', '123-45-6789');
// Storing passwords in plain text
const user = {
password: 'mypassword123',
apiKey: 'sk-1234567890abcdef'
};
β Secure Storage
// Use sessionStorage for temporary data
sessionStorage.setItem('tempData', encryptedData);
// Store sensitive data server-side only
const user = {
hashedPassword: await bcrypt.hash(password, 12),
// API keys stored server-side
};
// Encrypt sensitive localStorage data
const encrypted = CryptoJS.AES.encrypt(
JSON.stringify(data),
userSessionKey
).toString();
Encryption Implementation
// β
Client-side encryption for non-sensitive cached data
import CryptoJS from 'crypto-js';
class SecureStorage {
constructor() {
this.key = this.generateSessionKey();
}
generateSessionKey() {
return CryptoJS.lib.WordArray.random(256/8).toString();
}
encrypt(data) {
return CryptoJS.AES.encrypt(JSON.stringify(data), this.key).toString();
}
decrypt(encryptedData) {
const bytes = CryptoJS.AES.decrypt(encryptedData, this.key);
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
}
setItem(key, value) {
const encrypted = this.encrypt(value);
sessionStorage.setItem(key, encrypted);
}
getItem(key) {
const encrypted = sessionStorage.getItem(key);
return encrypted ? this.decrypt(encrypted) : null;
}
}
7. π HTTPS/TLS Security Implementation
HTTPS encrypts data in transit and prevents man-in-the-middle attacks.
HTTPS Configuration
// β
Force HTTPS redirect (Next.js)
module.exports = {
async redirects() {
return [
{
source: '/(.*)',
has: [
{
type: 'header',
key: 'x-forwarded-proto',
value: 'http',
},
],
destination: 'https://yourdomain.com/:path*',
permanent: true,
},
];
},
};
// β
Security headers
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains; preload'
},
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'X-Frame-Options',
value: 'DENY'
},
{
key: 'X-XSS-Protection',
value: '1; mode=block'
}
]
}
];
}
};
// β
Secure API calls
async function secureApiCall(endpoint, data) {
const response = await fetch(`https://api.yourdomain.com${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${await getSecureToken()}`
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`API call failed: ${response.status}`);
}
return response.json();
}
8. π Dependency Vulnerability Scanning
Regular dependency scanning helps identify and fix security vulnerabilities in third-party packages.
Automated Security Scanning
// package.json scripts for security
{
"scripts": {
"audit": "npm audit",
"audit:fix": "npm audit fix",
"security:check": "npm audit --audit-level moderate",
"snyk:test": "snyk test",
"snyk:monitor": "snyk monitor"
},
"husky": {
"hooks": {
"pre-commit": "npm run security:check"
}
}
}
// GitHub Actions workflow for security scanning
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run npm audit
run: npm audit --audit-level moderate
- name: Run Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
Dependency Management Best Practices
// β
Lock dependency versions
{
"dependencies": {
"react": "18.2.0", // Exact version
"lodash": "~4.17.21", // Patch updates only
"express": "^4.18.2" // Minor updates allowed
}
}
// β
Regular dependency updates
npm update
npm outdated
// β
Use .nvmrc for Node.js version consistency
echo "18.17.0" > .nvmrc
// β
Renovate bot configuration
{
"extends": ["config:base"],
"schedule": ["before 4am on Monday"],
"packageRules": [
{
"updateTypes": ["patch"],
"automerge": true
}
]
}
π‘οΈ Frontend Security Checklist
π Authentication & Authorization
- β Strong password requirements
- β Multi-factor authentication
- β Secure token storage
- β Session timeout
- β Role-based access control
π« Input & Output
- β Input validation & sanitization
- β Output encoding
- β File upload restrictions
- β SQL injection prevention
- β XSS protection
π Network Security
- β HTTPS enforcement
- β CSRF protection
- β CORS configuration
- β Security headers
- β Content Security Policy
β‘ Dependencies & Monitoring
- β Regular security audits
- β Dependency updates
- β Vulnerability scanning
- β Error logging
- β Performance monitoring
π Practical Examples Repository
All the examples in this article are available as working demos in my GitHub repository. Each vulnerability includes both vulnerable and secure implementations with detailed explanations.
π― Conclusion
Frontend security is an ongoing process that requires constant vigilance and regular updates. By implementing these security measures and following best practices, you can significantly reduce the risk of security vulnerabilities in your web applications.
Remember that security is not a one-time implementation but a continuous process. Stay updated with the latest security threats, regularly audit your code, and always validate both frontend and backend implementations.