138 lines
3.0 KiB
Python
138 lines
3.0 KiB
Python
|
|
"""
|
|||
|
|
React 框架安全知识
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from ..base import KnowledgeDocument, KnowledgeCategory
|
|||
|
|
|
|||
|
|
|
|||
|
|
REACT_SECURITY = KnowledgeDocument(
|
|||
|
|
id="framework_react",
|
|||
|
|
title="React Security",
|
|||
|
|
category=KnowledgeCategory.FRAMEWORK,
|
|||
|
|
tags=["react", "javascript", "frontend", "jsx"],
|
|||
|
|
content="""
|
|||
|
|
React 默认对XSS有较好的防护,但仍有一些需要注意的安全问题。
|
|||
|
|
|
|||
|
|
## 安全特性
|
|||
|
|
1. JSX自动转义
|
|||
|
|
2. 虚拟DOM隔离
|
|||
|
|
|
|||
|
|
## 常见漏洞模式
|
|||
|
|
|
|||
|
|
### dangerouslySetInnerHTML
|
|||
|
|
```jsx
|
|||
|
|
// 危险 - 直接渲染HTML
|
|||
|
|
function Comment({ content }) {
|
|||
|
|
return <div dangerouslySetInnerHTML={{ __html: content }} />;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 安全 - 使用DOMPurify
|
|||
|
|
import DOMPurify from 'dompurify';
|
|||
|
|
function Comment({ content }) {
|
|||
|
|
return <div dangerouslySetInnerHTML={{
|
|||
|
|
__html: DOMPurify.sanitize(content)
|
|||
|
|
}} />;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### href/src注入
|
|||
|
|
```jsx
|
|||
|
|
// 危险 - javascript:协议
|
|||
|
|
function Link({ url }) {
|
|||
|
|
return <a href={url}>Click</a>;
|
|||
|
|
// 攻击: url = "javascript:alert('XSS')"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 安全 - 验证协议
|
|||
|
|
function Link({ url }) {
|
|||
|
|
const safeUrl = url.startsWith('http') ? url : '#';
|
|||
|
|
return <a href={safeUrl}>Click</a>;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### eval和Function
|
|||
|
|
```jsx
|
|||
|
|
// 危险
|
|||
|
|
function Calculator({ expression }) {
|
|||
|
|
const result = eval(expression); // RCE风险
|
|||
|
|
return <div>{result}</div>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 安全 - 使用安全的表达式解析器
|
|||
|
|
import { evaluate } from 'mathjs';
|
|||
|
|
function Calculator({ expression }) {
|
|||
|
|
const result = evaluate(expression);
|
|||
|
|
return <div>{result}</div>;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 服务端渲染(SSR) XSS
|
|||
|
|
```jsx
|
|||
|
|
// 危险 - Next.js中
|
|||
|
|
export async function getServerSideProps({ query }) {
|
|||
|
|
return {
|
|||
|
|
props: {
|
|||
|
|
search: query.q // 未转义
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 页面中
|
|||
|
|
function Page({ search }) {
|
|||
|
|
return <script dangerouslySetInnerHTML={{
|
|||
|
|
__html: `window.search = "${search}"` // XSS!
|
|||
|
|
}} />;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 安全 - 使用JSON序列化
|
|||
|
|
function Page({ search }) {
|
|||
|
|
return <script dangerouslySetInnerHTML={{
|
|||
|
|
__html: `window.search = ${JSON.stringify(search)}`
|
|||
|
|
}} />;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 敏感数据暴露
|
|||
|
|
```jsx
|
|||
|
|
// 危险 - 前端存储敏感数据
|
|||
|
|
localStorage.setItem('token', apiToken);
|
|||
|
|
localStorage.setItem('user', JSON.stringify(userData));
|
|||
|
|
|
|||
|
|
// 危险 - 在状态中存储敏感信息
|
|||
|
|
const [creditCard, setCreditCard] = useState(cardNumber);
|
|||
|
|
|
|||
|
|
// 安全 - 使用HttpOnly Cookie存储token
|
|||
|
|
// 敏感数据只在需要时从后端获取
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 依赖安全
|
|||
|
|
```jsx
|
|||
|
|
// 危险 - 使用有漏洞的包
|
|||
|
|
import serialize from 'serialize-javascript';
|
|||
|
|
// 某些版本有XSS漏洞
|
|||
|
|
|
|||
|
|
// 安全 - 定期审计
|
|||
|
|
// npm audit
|
|||
|
|
// yarn audit
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 环境变量泄露
|
|||
|
|
```jsx
|
|||
|
|
// 危险 - 暴露服务端密钥
|
|||
|
|
// .env
|
|||
|
|
REACT_APP_API_KEY=secret_key // 会被打包到前端!
|
|||
|
|
|
|||
|
|
// 安全 - 敏感密钥只在服务端使用
|
|||
|
|
// 前端只使用公开的配置
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 安全最佳实践
|
|||
|
|
1. 避免使用dangerouslySetInnerHTML
|
|||
|
|
2. 验证所有URL协议
|
|||
|
|
3. 不要在前端存储敏感数据
|
|||
|
|
4. 使用Content Security Policy
|
|||
|
|
5. 定期更新依赖
|
|||
|
|
6. 使用TypeScript增强类型安全
|
|||
|
|
""",
|
|||
|
|
)
|