安全编码规范
JavaScript作为一门动态、弱类型的脚本语言,在开发过程中容易因编码不规范导致安全漏洞。遵循安全编码规范能有效减少潜在风险,提升代码质量和应用安全性。
变量声明与作用域控制
始终使用const
和let
替代var
声明变量,避免变量提升和全局污染。对于需要保护的常量,使用Object.freeze()
进行深度冻结:
// 不安全做法
var apiKey = '12345';
// 安全做法
const CONFIG = Object.freeze({
API_KEY: '12345',
MAX_RETRY: 3
});
// 尝试修改会静默失败
CONFIG.API_KEY = 'hack'; // 严格模式下报错
函数作用域内避免声明全局变量,使用IIFE封装敏感操作:
(function() {
const secretToken = generateToken();
// 安全的作用域操作
})();
输入验证与过滤
所有外部输入都应视为不可信数据。使用严格验证库如Joi或Yup:
import * as yup from 'yup';
const userSchema = yup.object().shape({
username: yup.string()
.required()
.matches(/^[a-zA-Z0-9_]+$/, '仅允许字母数字和下划线'),
age: yup.number()
.integer()
.min(13)
.max(120)
});
// 验证示例
try {
await userSchema.validate({
username: "admin'--",
age: "25"
});
} catch (err) {
console.error('验证失败:', err.errors);
}
对于DOM操作,始终对动态内容进行转义:
function safeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
document.getElementById('output').innerHTML = safeHTML(userInput);
安全通信与数据处理
使用HTTPS且避免在前端存储敏感信息。对于必须的API密钥,考虑使用HttpOnly Cookie:
// 不安全
localStorage.setItem('auth_token', 'Bearer xyz');
// 相对安全
fetch('/api/login', {
method: 'POST',
credentials: 'include', // 配合后端HttpOnly Cookie
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ /* 数据 */ })
});
加密敏感数据时使用Web Crypto API而非自定义算法:
async function hashPassword(password) {
const encoder = new TextEncoder();
const data = encoder.encode(password + process.env.SALT);
const hash = await crypto.subtle.digest('SHA-256', data);
return Array.from(new Uint8Array(hash))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
防范XSS攻击
对于现代框架,React/Vue等已提供基础防护,但仍需注意危险场景:
// React中的危险操作示例
function UnsafeComponent({ userInput }) {
// 错误做法
return <div dangerouslySetInnerHTML={{ __html: userInput }} />;
// 正确做法
return <div>{userInput}</div>; // 自动转义
}
动态创建脚本时使用textContent
而非innerHTML
:
const script = document.createElement('script');
script.textContent = 'console.log("安全执行")';
document.body.appendChild(script);
防范CSRF攻击
实现CSRF令牌机制,并与框架集成:
// 前端获取令牌
let csrfToken = document.querySelector('meta[name="csrf-token"]').content;
// 请求时携带
fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({ amount: 100 })
});
依赖管理安全
定期使用npm audit
检查依赖,锁定版本号:
{
"dependencies": {
"lodash": "4.17.21", // 固定版本
"react": "^18.2.0" // 允许补丁更新
}
}
使用Content Security Policy (CSP)增强防护:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline' cdn.example.com;">
错误处理与日志
避免暴露敏感信息的错误消息:
try {
await criticalOperation();
} catch (err) {
// 不安全
console.error(`操作失败: ${err.stack}`);
// 安全做法
console.error('操作失败: 系统错误');
sendToMonitoring({
errorCode: 'E500',
timestamp: Date.now()
});
}
性能与内存安全
避免内存泄漏的常见模式:
// 事件监听器泄漏
window.addEventListener('resize', heavyCalculation);
// 正确做法
const debouncedCalc = debounce(heavyCalculation, 200);
window.addEventListener('resize', debouncedCalc);
// 组件卸载时
window.removeEventListener('resize', debouncedCalc);
处理大文件时使用流式API:
async function processFile(file) {
const stream = file.stream();
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 分块处理数据
}
}
现代API的安全实践
使用structuredClone
替代JSON.parse(JSON.stringify())
进行深拷贝:
const original = new Set([1, 2, new Map([[3, 4]])]);
const copy = structuredClone(original); // 保持引用关系
Worker通信时验证消息来源:
// worker.js
self.onmessage = (event) => {
if (event.origin !== window.location.origin) return;
// 安全处理消息
};
类型安全增强
即使使用JavaScript也应考虑类型检查:
// 使用JSDoc增强类型安全
/**
* @param {string} url
* @param {{ method?: string, body?: object }} options
* @returns {Promise<{ data: any, status: number }>}
*/
async function safeFetch(url, options = {}) {
// 实现
}
浏览器存储规范
区分不同存储场景的使用:
// 会话级数据
sessionStorage.setItem('temp_data', JSON.stringify({ tabState: 'active' }));
// 长期偏好设置
localStorage.setItem('user_prefs', JSON.stringify({
theme: 'dark',
fontSize: 14
}));
// 敏感数据
const secureStore = {
set(key, value) {
crypto.subtle.encrypt(/* 加密参数 */)
.then(ciphertext => {
localStorage.setItem(key, ciphertext);
});
}
};
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn