multiversx-dapp-audit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMultiversX dApp Auditor
MultiversX dApp 审计指南
Audit the frontend components of MultiversX applications built with . This skill focuses on client-side security, transaction construction, and wallet integration vulnerabilities.
@multiversx/sdk-dapp审计基于构建的MultiversX应用前端组件。本指南聚焦客户端安全、交易构造以及钱包集成中的漏洞排查。
@multiversx/sdk-dappWhen to Use
适用场景
- Reviewing React/TypeScript dApp code
- Auditing wallet connection implementations
- Assessing transaction signing security
- Checking for XSS and data exposure vulnerabilities
- Validating frontend-backend security boundaries
- 审查React/TypeScript dApp代码
- 审计钱包连接实现逻辑
- 评估交易签名安全性
- 检查XSS与数据泄露漏洞
- 验证前端与后端的安全边界
1. Transaction Construction Security
1. 交易构造安全性
The Threat Model
威胁模型
The frontend constructs transaction payloads that users sign. Vulnerabilities here can trick users into signing malicious transactions.
前端负责构造用户需要签名的交易载荷。此处的漏洞可能导致用户被诱骗签署恶意交易。
Payload Manipulation
载荷操纵风险
typescript
// VULNERABLE: User input directly in transaction data
const sendTransaction = async (userInput: string) => {
const tx = new Transaction({
receiver: Address.newFromBech32(recipientAddress),
data: Buffer.from(userInput), // Attacker controls data!
// ...
});
await signAndSend(tx);
};
// SECURE: Validate and sanitize all inputs
const sendTransaction = async (functionName: string, args: string[]) => {
// Whitelist allowed functions
const allowedFunctions = ['stake', 'unstake', 'claim'];
if (!allowedFunctions.includes(functionName)) {
throw new Error('Invalid function');
}
// Validate arguments
const sanitizedArgs = args.map(arg => validateArgument(arg));
const tx = new Transaction({
receiver: contractAddress,
data: Buffer.from(`${functionName}@${sanitizedArgs.join('@')}`),
// ...
});
await signAndSend(tx);
};typescript
// VULNERABLE: User input directly in transaction data
const sendTransaction = async (userInput: string) => {
const tx = new Transaction({
receiver: Address.newFromBech32(recipientAddress),
data: Buffer.from(userInput), // Attacker controls data!
// ...
});
await signAndSend(tx);
};
// SECURE: Validate and sanitize all inputs
const sendTransaction = async (functionName: string, args: string[]) => {
// Whitelist allowed functions
const allowedFunctions = ['stake', 'unstake', 'claim'];
if (!allowedFunctions.includes(functionName)) {
throw new Error('Invalid function');
}
// Validate arguments
const sanitizedArgs = args.map(arg => validateArgument(arg));
const tx = new Transaction({
receiver: contractAddress,
data: Buffer.from(`${functionName}@${sanitizedArgs.join('@')}`),
// ...
});
await signAndSend(tx);
};Critical Checks
关键检查项
| Check | Risk | Mitigation |
|---|---|---|
| Receiver address validation | Funds sent to wrong address | Validate against known addresses |
| Data payload construction | Malicious function calls | Whitelist allowed operations |
| Amount validation | Incorrect value transfers | Confirm amounts with user |
| Gas limit manipulation | Transaction failures | Use appropriate limits |
| 检查项 | 风险 | 缓解措施 |
|---|---|---|
| 接收方地址验证 | 资金转入错误地址 | 与已知地址进行校验 |
| 数据载荷构造 | 恶意函数调用 | 白名单允许的操作 |
| 金额验证 | 转账金额错误 | 与用户确认转账金额 |
| Gas限制操纵 | 交易失败 | 使用合适的限制值 |
2. Signing Security
2. 签名安全性
Blind Signing Risks
盲签风险
Users may sign transactions without understanding the content:
typescript
// DANGEROUS: Signing opaque data
const signMessage = async (data: string) => {
const hash = keccak256(data);
return await wallet.signMessage(hash); // User sees only hash!
};
// SECURE: Show clear message to user
const signMessage = async (message: string) => {
// Display message to user before signing
const confirmed = await showConfirmationDialog({
title: 'Sign Message',
content: `You are signing: "${message}"`,
warning: 'Only sign messages you understand'
});
if (!confirmed) throw new Error('User rejected');
return await wallet.signMessage(message);
};用户可能在未理解交易内容的情况下签署交易:
typescript
// DANGEROUS: Signing opaque data
const signMessage = async (data: string) => {
const hash = keccak256(data);
return await wallet.signMessage(hash); // User sees only hash!
};
// SECURE: Show clear message to user
const signMessage = async (message: string) => {
// Display message to user before signing
const confirmed = await showConfirmationDialog({
title: 'Sign Message',
content: `You are signing: "${message}"`,
warning: 'Only sign messages you understand'
});
if (!confirmed) throw new Error('User rejected');
return await wallet.signMessage(message);
};Transaction Preview Requirements
交易预览要求
Before signing, users should see:
- Recipient address (with verification if known)
- Amount being transferred
- Token type (EGLD, ESDT, NFT)
- Function being called (if smart contract interaction)
- Gas cost estimate
签署前,用户应能查看以下信息:
- 接收方地址(若为已知地址需附带验证标识)
- 转账金额
- 代币类型(EGLD、ESDT、NFT)
- 调用的智能合约函数(若涉及合约交互)
- Gas成本预估
3. Sensitive Data Handling
3. 敏感数据处理
Private Key Security
私钥安全
typescript
// CRITICAL VULNERABILITY: Never do this
localStorage.setItem('privateKey', wallet.privateKey);
localStorage.setItem('mnemonic', wallet.mnemonic);
sessionStorage.setItem('seed', wallet.seed);
// Check for these patterns in code review:
// - Any storage of private keys, mnemonics, or seeds
// - Logging of sensitive data
// - Sending sensitive data to APIstypescript
// CRITICAL VULNERABILITY: Never do this
localStorage.setItem('privateKey', wallet.privateKey);
localStorage.setItem('mnemonic', wallet.mnemonic);
sessionStorage.setItem('seed', wallet.seed);
// Check for these patterns in code review:
// - Any storage of private keys, mnemonics, or seeds
// - Logging of sensitive data
// - Sending sensitive data to APIsSecure Patterns
安全实践
typescript
// CORRECT: Use sdk-dapp's secure session management
import { initApp } from '@multiversx/sdk-dapp/out/methods/initApp/initApp';
initApp({
storage: {
getStorageCallback: () => sessionStorage // Session only, not persistent
},
dAppConfig: {
nativeAuth: {
expirySeconds: 3600 // Short-lived tokens
}
}
});typescript
// CORRECT: Use sdk-dapp's secure session management
import { initApp } from '@multiversx/sdk-dapp/out/methods/initApp/initApp';
initApp({
storage: {
getStorageCallback: () => sessionStorage // Session only, not persistent
},
dAppConfig: {
nativeAuth: {
expirySeconds: 3600 // Short-lived tokens
}
}
});Access Token Security
访问令牌安全
typescript
// VULNERABLE: Token exposed in URL
window.location.href = `https://api.example.com?accessToken=${token}`;
// VULNERABLE: Token in console
console.log('Auth token:', accessToken);
// SECURE: Token in Authorization header, never logged
fetch(apiUrl, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});typescript
// VULNERABLE: Token exposed in URL
window.location.href = `https://api.example.com?accessToken=${token}`;
// VULNERABLE: Token in console
console.log('Auth token:', accessToken);
// SECURE: Token in Authorization header, never logged
fetch(apiUrl, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});4. XSS Prevention
4. XSS防护
User-Generated Content
用户生成内容
typescript
// VULNERABLE: Direct HTML injection
const UserProfile = ({ bio }: { bio: string }) => {
return <div dangerouslySetInnerHTML={{ __html: bio }} />; // XSS!
};
// SECURE: React's default escaping
const UserProfile = ({ bio }: { bio: string }) => {
return <div>{bio}</div>; // Automatically escaped
};
// If HTML is necessary, sanitize first
import DOMPurify from 'dompurify';
const UserProfile = ({ bio }: { bio: string }) => {
const sanitized = DOMPurify.sanitize(bio);
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
};typescript
// VULNERABLE: Direct HTML injection
const UserProfile = ({ bio }: { bio: string }) => {
return <div dangerouslySetInnerHTML={{ __html: bio }} />; // XSS!
};
// SECURE: React's default escaping
const UserProfile = ({ bio }: { bio: string }) => {
return <div>{bio}</div>; // Automatically escaped
};
// If HTML is necessary, sanitize first
import DOMPurify from 'dompurify';
const UserProfile = ({ bio }: { bio: string }) => {
const sanitized = DOMPurify.sanitize(bio);
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
};URL Handling
URL处理
typescript
// VULNERABLE: Unvalidated redirect
const handleCallback = () => {
const returnUrl = new URLSearchParams(window.location.search).get('returnUrl');
window.location.href = returnUrl!; // Open redirect!
};
// SECURE: Validate redirect URL
const handleCallback = () => {
const returnUrl = new URLSearchParams(window.location.search).get('returnUrl');
const allowed = ['/', '/dashboard', '/profile'];
if (allowed.includes(returnUrl || '')) {
window.location.href = returnUrl!;
} else {
window.location.href = '/'; // Default safe redirect
}
};typescript
// VULNERABLE: Unvalidated redirect
const handleCallback = () => {
const returnUrl = new URLSearchParams(window.location.search).get('returnUrl');
window.location.href = returnUrl!; // Open redirect!
};
// SECURE: Validate redirect URL
const handleCallback = () => {
const returnUrl = new URLSearchParams(window.location.search).get('returnUrl');
const allowed = ['/', '/dashboard', '/profile'];
if (allowed.includes(returnUrl || '')) {
window.location.href = returnUrl!;
} else {
window.location.href = '/'; // Default safe redirect
}
};5. API Communication Security
5. API通信安全
HTTPS Enforcement
HTTPS强制要求
typescript
// VULNERABLE: HTTP connection
const API_URL = 'http://api.example.com'; // Insecure!
// SECURE: Always HTTPS
const API_URL = 'https://api.example.com';
// Verify in code: all API URLs must use https://typescript
// VULNERABLE: HTTP connection
const API_URL = 'http://api.example.com'; // Insecure!
// SECURE: Always HTTPS
const API_URL = 'https://api.example.com';
// Verify in code: all API URLs must use https://Request/Response Validation
请求/响应验证
typescript
// VULNERABLE: Trusting API response blindly
const balance = await fetch('/api/balance').then(r => r.json());
displayBalance(balance.amount); // What if API is compromised?
// SECURE: Validate response structure
const response = await fetch('/api/balance').then(r => r.json());
if (typeof response.amount !== 'string' || !/^\d+$/.test(response.amount)) {
throw new Error('Invalid balance response');
}
displayBalance(response.amount);typescript
// VULNERABLE: Trusting API response blindly
const balance = await fetch('/api/balance').then(r => r.json());
displayBalance(balance.amount); // What if API is compromised?
// SECURE: Validate response structure
const response = await fetch('/api/balance').then(r => r.json());
if (typeof response.amount !== 'string' || !/^\d+$/.test(response.amount)) {
throw new Error('Invalid balance response');
}
displayBalance(response.amount);6. Audit Tools and Techniques
6. 审计工具与技术
Network Traffic Analysis
网络流量分析
bash
undefinedbash
undefinedUse browser DevTools Network tab to inspect:
Use browser DevTools Network tab to inspect:
- All API requests and responses
- All API requests and responses
- Transaction data being sent
- Transaction data being sent
- Headers (especially Authorization)
- Headers (especially Authorization)
- WebSocket messages
- WebSocket messages
Or use proxy tools:
Or use proxy tools:
- Burp Suite
- Burp Suite
- mitmproxy
- mitmproxy
- Charles Proxy
- Charles Proxy
undefinedundefinedCode Review Patterns
代码审查模式
bash
undefinedbash
undefinedSearch for dangerous patterns
Search for dangerous patterns
grep -r "localStorage" src/
grep -r "dangerouslySetInnerHTML" src/
grep -r "eval(" src/
grep -r "privateKey|mnemonic|seed" src/
grep -r "console.log" src/ # Check for sensitive data logging
undefinedgrep -r "localStorage" src/
grep -r "dangerouslySetInnerHTML" src/
grep -r "eval(" src/
grep -r "privateKey|mnemonic|seed" src/
grep -r "console.log" src/ # Check for sensitive data logging
undefinedBrowser Security Headers Check
浏览器安全头检查
Content-Security-Policy: default-src 'self'; script-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-originContent-Security-Policy: default-src 'self'; script-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin7. Authentication Flow Audit
7. 认证流程审计
Wallet Connection
钱包连接
typescript
// Verify wallet connection flow:
// 1. User initiates connection
// 2. Wallet provider prompts for approval
// 3. Only public address shared with dApp
// 4. No private data transmitted
// Check UnlockPanelManager usage
const unlockPanelManager = UnlockPanelManager.init({
loginHandler: () => {
// Verify: no sensitive data handling here
navigate('/dashboard');
}
});typescript
// Verify wallet connection flow:
// 1. User initiates connection
// 2. Wallet provider prompts for approval
// 3. Only public address shared with dApp
// 4. No private data transmitted
// Check UnlockPanelManager usage
const unlockPanelManager = UnlockPanelManager.init({
loginHandler: () => {
// Verify: no sensitive data handling here
navigate('/dashboard');
}
});Session Management
会话管理
typescript
// Verify session security:
// - Token expiration enforced
// - Logout clears all session data
// - No persistent sensitive storage
const handleLogout = async () => {
const provider = getAccountProvider();
await provider.logout();
// Verify: session storage cleared
sessionStorage.clear();
navigate('/');
};typescript
// Verify session security:
// - Token expiration enforced
// - Logout clears all session data
// - No persistent sensitive storage
const handleLogout = async () => {
const provider = getAccountProvider();
await provider.logout();
// Verify: session storage cleared
sessionStorage.clear();
navigate('/');
};8. Audit Checklist
8. 审计检查清单
Transaction Security
交易安全
- All transaction data validated before signing
- User shown clear transaction preview
- Recipient addresses validated
- Amount confirmations for large transfers
- Gas limits appropriate for operation
- 所有交易数据在签署前均经过验证
- 向用户展示清晰的交易预览
- 接收方地址已验证
- 大额转账需用户确认金额
- Gas限制与操作匹配
Data Security
数据安全
- No private keys in any storage
- No sensitive data in console logs
- Access tokens not exposed in URLs
- HTTPS enforced for all API calls
- 任何存储介质中均无私人密钥
- 控制台日志中无敏感数据
- 访问令牌未暴露在URL中
- 所有API调用均强制使用HTTPS
XSS Prevention
XSS防护
- No with user input
dangerouslySetInnerHTML - No with user input
eval() - URL redirects validated
- Content-Security-Policy headers present
- 未使用处理用户输入
dangerouslySetInnerHTML - 未使用处理用户输入
eval() - URL重定向已验证
- 已配置Content-Security-Policy安全头
Session Security
会话安全
- Token expiration enforced
- Logout clears all session data
- Protected routes properly guarded
- Re-authentication for sensitive operations
- 令牌过期机制已生效
- 登出操作清除所有会话数据
- 受保护路由已正确守卫
- 敏感操作需重新认证
9. Report Template
9. 报告模板
markdown
undefinedmarkdown
undefineddApp Security Audit Report
dApp Security Audit Report
Scope
Scope
- Application: [name]
- Version: [version]
- Files reviewed: [count]
- Application: [name]
- Version: [version]
- Files reviewed: [count]
Findings
Findings
Critical
Critical
| ID | Description | Location | Recommendation |
|---|---|---|---|
| C1 | ... | ... | ... |
| ID | Description | Location | Recommendation |
|---|---|---|---|
| C1 | ... | ... | ... |
High
High
...
...
Medium
Medium
...
...
Informational
Informational
...
...
Recommendations Summary
Recommendations Summary
- [Priority recommendation]
- ...
- [Priority recommendation]
- ...
Conclusion
Conclusion
[Overall assessment]
undefined[Overall assessment]
undefined