Frontend focus areas in penetration testing
Frontend Input Validation and Sanitization
Frontend input validation is a primary focus in penetration testing. User-controllable data such as form inputs, URL parameters, and cookie values must undergo strict validation. Even if backend validation exists, missing frontend validation can still lead to vulnerabilities like XSS and SQL injection.
// Insecure example: Directly using user input
const userInput = document.getElementById('search').value;
document.getElementById('result').innerHTML = `Search results: ${userInput}`;
// Secure example: Using textContent instead of innerHTML
const sanitizedInput = document.getElementById('search').value;
document.getElementById('result').textContent = `Search results: ${sanitizedInput}`;
When using regex validation, ensure edge cases are covered:
// Common email validation flaw: Missing TLD length check
const badEmailRegex = /^[^@]+@[^@]+\.[a-z]{2,}$/i;
// Improved version: Limits TLD length to 2-6 characters
const betterEmailRegex = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,6}$/i;
DOM-based XSS Protection
DOM-based XSS is particularly dangerous because the attack payload never touches the server. Common risk points include:
- location.hash
- document.referrer
- window.name
- postMessage data
// Dangerous use of location.hash
const token = location.hash.substring(1);
ajaxRequest('/api?token=' + token);
// Secure handling
const sanitize = str => str.replace(/[^a-z0-9]/gi, '');
const safeToken = sanitize(location.hash.substring(1));
Use CSP as a last line of defense:
<!-- Strict CSP policy example -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; object-src 'none'">
Client-Side Storage Security
localStorage and sessionStorage are common attack targets:
// Insecure storage method
localStorage.setItem('user', JSON.stringify({
id: 123,
isAdmin: true,
token: 'abc123xyz'
}));
// More secure approach
const secureStore = {
set: (key, value) => {
const encrypted = CryptoJS.AES.encrypt(
JSON.stringify(value),
'Key should not be hardcoded here'
).toString();
localStorage.setItem(key, encrypted);
},
get: (key) => {
const data = localStorage.getItem(key);
return data ? JSON.parse(
CryptoJS.AES.decrypt(data, 'Key should not be hardcoded here').toString(CryptoJS.enc.Utf8)
) : null;
}
};
IndexedDB also requires attention:
// Enable encryption when creating a database
const request = indexedDB.open('SecureDB', 1);
request.onupgradeneeded = (e) => {
const db = e.target.result;
const store = db.createObjectStore('users', {
keyPath: 'id',
autoIncrement: true
});
// Add encrypted index
store.createIndex('encrypted_email', 'email', { unique: true });
};
API Communication Security
Frontend API calls require multiple layers of protection:
- CSRF Token handling:
// Automatically retrieve CSRF Token from cookies
const csrfToken = document.cookie.match(/XSRF-TOKEN=([^;]+)/)[1];
fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-XSRF-TOKEN': csrfToken
},
body: JSON.stringify({ query: 'test' })
});
- Request parameter signing:
const signRequest = (params, secret) => {
const sorted = Object.keys(params).sort().map(k => `${k}=${params[k]}`).join('&');
return CryptoJS.HmacSHA256(sorted, secret).toString();
};
const params = { user: '123', action: 'delete' };
params.sig = signRequest(params, 'shared-secret');
Third-Party Dependency Risks
npm package security requires continuous monitoring:
- Check dependency tree:
npm audit --production
- Lock file integrity:
# Generate checksum for package-lock.json
shasum -a 256 package-lock.json > .lockfile_checksum
- Dynamic loading risks:
// Dangerous dynamic import
const pluginName = getUserInput();
import(`./plugins/${pluginName}`).then(...);
// Safer implementation
const allowedPlugins = ['chart', 'table'];
if (allowedPlugins.includes(pluginName)) {
import(`./plugins/${pluginName}`).then(...);
}
Frontend Configuration Leaks
Build tools like Webpack may expose sensitive information:
// Insecure webpack configuration
module.exports = {
plugins: [
new webpack.DefinePlugin({
API_KEY: JSON.stringify('123-456-789'),
DEBUG_MODE: true
})
]
};
// Recommended secure configuration
const secrets = require('./secrets.json');
module.exports = (env) => ({
plugins: [
new webpack.DefinePlugin({
'process.env.API_ENDPOINT': env.production
? JSON.stringify('https://api.prod.com')
: JSON.stringify('https://api.dev.com')
})
]
});
Browser Feature Abuse Protection
Modern browser APIs can be maliciously exploited:
- Sensor API protection:
// Detect and restrict sensor access
if ('accelerometer' in window) {
const accelerometer = new Accelerometer({ frequency: 1 });
accelerometer.addEventListener('reading', () => {
if (isSuspiciousMovement(accelerometer)) {
alert('Suspicious activity detected');
accelerometer.stop();
}
});
accelerometer.start();
}
- Web Worker communication validation:
// main.js
const worker = new Worker('worker.js');
worker.onmessage = (e) => {
if (e.origin !== expectedOrigin) return;
// Process message
};
// worker.js
self.onmessage = (e) => {
if (!validateMessage(e.data)) return;
// Process task
};
Clickjacking Defense
Modern clickjacking requires multi-layered protection:
- Traditional X-Frame-Options:
X-Frame-Options: DENY
- CSP frame-ancestors combination:
Content-Security-Policy: frame-ancestors 'none'
- JavaScript defense as fallback:
if (top !== self) {
document.body.innerHTML = '<h1>This page cannot be embedded</h1>';
throw new Error('Frame busting detected');
}
Client-Side Encryption Implementation
Frontend encryption considerations:
// Correct usage of Web Crypto API
async function encryptData(data, keyMaterial) {
const key = await window.crypto.subtle.importKey(
'raw',
keyMaterial,
{ name: 'AES-GCM' },
false,
['encrypt']
);
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const encrypted = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
new TextEncoder().encode(data)
);
return { iv, encrypted };
}
Key management golden rules:
- Never hardcode keys
- Use key derivation functions (PBKDF2)
- Rotate keys periodically
Error Handling and Logging
Secure practices for frontend error handling:
// Insecure error exposure
try {
sensitiveOperation();
} catch (err) {
console.error(`Operation failed: ${err.message}`);
showToast(`Error details: ${err.stack}`);
}
// Secure error handling
const sanitizeError = (err) => ({
message: err.message.replace(/token=([^&]+)/, 'token=[REDACTED]'),
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
});
try {
processSensitiveData();
} catch (err) {
const safeError = sanitizeError(err);
logToServer(safeError);
showToast('Operation failed, please contact admin');
}
Automated Testing Integration
Penetration testing should be integrated into CI/CD:
- Using OWASP ZAP CLI:
docker run -v $(pwd):/zap/wrk/:rw \
-t owasp/zap2docker-stable zap-baseline.py \
-t https://example.com \
-g gen.conf -r testreport.html
- Integrating Playwright security tests:
// playwright-security-test.js
const { test } = require('@playwright/test');
test('Detect XSS vulnerabilities', async ({ page }) => {
await page.goto('/search');
const xssPayload = '<img src=x onerror=alert(1)>';
await page.fill('#search-input', xssPayload);
await page.click('#search-btn');
const alerts = await page.evaluate(() => {
return window.alerts; // Monitor alert calls
});
expect(alerts.length).toBe(0);
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:自动化安全测试方案
下一篇:WebAssembly 安全风险