目录
一、现代前端框架概览
框架安全特性对比
| 特性 | React | Vue | Angular | Svelte |
|---|
| 默认XSS防护 | ✓ (JSX转义) | ✓ (模板转义) | ✓ (模板转义) | ✓ (编译时) |
| dangerouslySetInnerHTML | ✓ | - | - | - |
| v-html | - | ✓ | - | - |
| [innerHTML] | - | - | ✓ | - |
| {@html} | - | - | - | ✓ |
| 客户端模板 | JSX | template | template | 编译时 |
| SSR支持 | Next.js | Nuxt | Angular Universal | SvelteKit |
| CSP友好 | ✓ | ✓ | ✓ | ✓ |
| 自动净化 | ✗ (需手动) | ✗ (需手动) | ✓ (DomSanitizer) | ✗ (需手动) |
XSS在框架中的体现
// 所有框架的默认行为:安全(自动转义)
React: <div>{userInput}</div> → <...>
Vue: <div>{{ userInput }}</div> → <...>
Angular: <div>{{ userInput }}</div> → <...>
// 危险API(原始HTML)
React: <div dangerouslySetInnerHTML={{__html: userInput}} />
Vue: <div v-html="userInput"></div>
Angular: <div [innerHTML]="userInput"></div>
二、React 安全分析
dangerouslySetInnerHTML
// 危险:最直接的XSS入口
function UserComment({ text }) {
return <div dangerouslySetInnerHTML={{ __html: text }} />;
}
// 如果 text = '<img src=x onerror=alert(1)>' → XSS!
href 属性注入
// 危险:javascript: 伪协议
function UserLink({ url }) {
return <a href={url}>Click me</a>;
}
// url = 'javascript:alert(1)' → 点击弹窗!
// 安全:验证协议
function SafeLink({ url }) {
const safe = url.startsWith('https://') || url.startsWith('http://')
? url : '#';
return <a href={safe}>Click me</a>;
}
React的防护机制
// 1. JSX自动转义
const userInput = '<img src=x onerror=alert(1)>';
return <div>{userInput}</div>;
// 渲染结果:<div><img src=x onerror=alert(1)></div>
// → XSS被自动防护!
// 2. 但以下情况不会转义:
// - dangerouslySetInnerHTML
// - href属性
// - 自定义属性(通过回调注入JS)
React SSR (Next.js)
// 危险:SSR中HTML注入
export async function getServerSideProps(context) {
return {
props: {
// 如果用户输入直接渲染到HTML → SSR XSS
content: context.query.content,
},
};
}
// 防御:使用DOMPurify
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(dirtyHTML);
React Native 特有攻击面
// WebView注入
<WebView source={{ html: userControlledHTML }} />
// 如果HTML可控 → XSS
// Deep Link劫持
// myapp://callback?code=xxx
// 如果Deep Link不验证 → 应用被劫持
三、Vue 安全分析
v-html 注入
<!-- 危险 -->
<div v-html="userInput"></div>
<!-- userInput = '<img src=x onerror=alert(1)>' → XSS -->
动态模板编译
// 非常危险
const template = '<div>' + userInput + '</div>';
new Vue({ template });
// 如果 userInput 是 {{constructor.constructor('alert(1)')()}} → CSTI
Vue SSR攻击 (Nuxt.js)
// Nuxt.js服务端渲染
// 如果数据中含有未转义的内容
<template>
<div v-html="content"></div>
</template>
<script>
export default {
async asyncData({ query }) {
return { content: query.content };
// query.content 如果是服务器渲染的模板 → SSR XSS
}
}
</script>
Vue特有攻击
1. 原型链污染(Vue 2.x的合并选项)
Vue.util.defineReactive → 可能被污染利用
2. 动态组件
<component :is="userControlledComponent">
如果组件名可控 → 可能加载恶意组件
3. 自定义指令
v-custom="userInput"
如果在指令中执行eval → RCE
4. Vuex Store
XSS读取Vuex中的数据 → 窃取所有前端状态
四、Angular 安全分析
内置防护
// Angular默认进行模板表达式安全上下文检查
@Component({
template: `<div>{{ userInput }}</div>`
})
// 默认安全 —— Angular自动转义
// DOM Sanitizer
constructor(private sanitizer: DomSanitizer) {}
// 需要显式绕过安全检查(标记为危险)
this.sanitizer.bypassSecurityTrustHtml(dangerousHtml);
this.sanitizer.bypassSecurityTrustUrl('javascript:alert(1)');
this.sanitizer.bypassSecurityTrustScript(dangerousScript);
Angular安全漏洞历史
1. AngularJS沙箱绕过(1.x系列)
- 多个版本被绕过(见CSTI章节)
2. Angular Universal (SSR)
- 服务端渲染时的HTML注入
3. 第三方组件
- Angular Material / PrimeNG中的XSS
五、Next.js / Nuxt SSR安全
SSR特有安全问题
// 问题1:__NEXT_DATA__ 泄露
// Next.js在HTML中内嵌初始数据:
<script id="__NEXT_DATA__" type="application/json">
{"props":{"pageProps":{"user":{"token":"secret"}}}}
</script>
// XSS可以读取这个JSON!
// 问题2:API Routes中的SSRF
// pages/api/proxy.js
export default async function handler(req, res) {
const url = req.query.url;
const response = await fetch(url); // ← SSRF!
res.json(await response.json());
}
// 问题3:动态导入中的路径遍历
import(`../components/${userInput}`); // 如果userInput可控
SSR XSS (Server-Side XSS)
// 场景:用户输入被服务端渲染为HTML
// GET /page?content=<script>alert(1)</script>
// 如果服务端直接渲染用户输入:
app.get('/page', (req, res) => {
const html = `<div>${req.query.content}</div>`;
res.send(html); // ← SSR XSS!
});
// 防御:服务端也做转义
const escapeHtml = (str) => str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
六、SPA安全特性
SPA vs 传统MPA
| 特性 | SPA | MPA |
|---|
| 渲染位置 | 客户端 | 服务端 |
| 路由 | Hash/History API | URL路径 |
| 状态管理 | Redux/Vuex/Pinia | Session/数据库 |
| XSS面 | 所有JS暴露 | 相对较小 |
| 敏感数据 | 前端状态中 | 仅在服务端 |
| SEO | 需要SSR | 天然支持 |
SPA攻击面
// 1. 前端路由中的Token
// https://app.com/reset-password#access_token=xxx
// token在hash → 可能被JS读取
// 2. 状态管理中的数据泄露
// Redux DevTools在生产环境启用 → 所有状态可见
const store = createStore(reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
// 3. Service Worker缓存敏感数据
// Cache API可以存储API响应
caches.open('api-cache').then(cache => {
cache.put('/api/user/profile', response.clone());
});
七、前端供应链安全
npm依赖攻击
# 1. Typosquatting(拼写错误劫持)
npm install loadsh # 而不是 lodash → 恶意包
# 2. 依赖混淆
# 如果公司使用私有包 @company/utils
# 攻击者在npm注册一个同名的公开包
npm install @company/utils # → 获取攻击者的包
# 3. 版本回退
# 恶意维护者接手不再维护的包
# 发布"安全更新"实际含后门
CDN劫持
<!-- 如果CDN域名过期或被接管 -->
<script src="https://cdn-legacy.example.com/jquery.min.js"></script>
<!-- 攻击者注册 cdn-legacy.example.com → 任意JS -->
子资源完整性 (SRI)
<!-- 安全:使用SRI hash确保CDN脚本未被篡改 -->
<script src="https://cdn.example.com/library.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYXiN21q2yXJEJIYXqL"
crossorigin="anonymous">
</script>
<!-- 如果CDN返回的库内容被篡改 → SRI不匹配 → 浏览器拒绝加载 -->
八、红队视角总结
框架攻击速查
| 框架 | 主要弱点 | 利用方式 |
|---|
| React | dangerouslySetInnerHTML | 寻找使用点 + XSS payload |
| Vue | v-html + 动态模板 | 注入模板表达式 |
| Angular | bypassSecurityTrust | 寻找标记为安全的输入 |
| Next.js | SSR XSS + API routes SSRF | 注入服务端渲染内容 |
| jQuery | html() + 选择器注入 | XSS + DOM XSS |
框架检测方法
// 检测React
!!document.querySelector('[data-reactroot]')
!!window.React
// 检测Vue
!!document.querySelector('[data-v-]') // scoped css attribute
!!window.__VUE_DEVTOOLS_GLOBAL_HOOK__
// 检测Angular
!!document.querySelector('[ng-version]')
!!window.ng
// 检测Next.js
!!document.getElementById('__NEXT_DATA__')
// 检测Nuxt
!!document.getElementById('__NUXT_DATA__')
推荐工具
| 工具 | 用途 |
|---|
| React Developer Tools | 检查React组件状态 |
| Vue DevTools | 检查Vuex Store |
| Wappalyzer | 自动检测前端框架 |
| npm audit | 检查依赖漏洞 |
| retire.js | 检测已知漏洞的JS库 |
返回 前端基础总目录