安全最佳实践 #
一、安全开发原则 #
1.1 核心原则 #
| 原则 | 说明 |
|---|---|
| 最小权限 | 只授予必要的权限 |
| 深度防御 | 多层安全防护 |
| 安全默认 | 默认配置是安全的 |
| 失败安全 | 失败时保持安全状态 |
| 输入验证 | 验证所有输入 |
1.2 安全开发流程 #
text
┌─────────────────────────────────────────────────────────────┐
│ 安全开发流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 设计阶段 ──► 开发阶段 ──► 测试阶段 ──► 发布阶段 │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ 安全设计 安全编码 安全测试 安全审计 │
│ 威胁建模 代码审查 渗透测试 依赖检查 │
│ │
└─────────────────────────────────────────────────────────────┘
二、输入验证 #
2.1 验证原则 #
rust
// 白名单验证
fn validate_username(username: &str) -> Result<String, String> {
if username.len() < 3 || username.len() > 50 {
return Err("Username must be 3-50 characters".to_string());
}
if !username.chars().all(|c| c.is_alphanumeric() || c == '_') {
return Err("Username can only contain letters, numbers, and underscore".to_string());
}
Ok(username.to_string())
}
2.2 类型验证 #
rust
use serde::Deserialize;
#[derive(Deserialize)]
struct UserInput {
#[serde(deserialize_with = "validate_email")]
email: String,
#[serde(deserialize_with = "validate_age")]
age: u32,
}
fn validate_email<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: serde::Deserializer<'de>,
{
let email: String = serde::Deserialize::deserialize(deserializer)?;
if !email.contains('@') {
return Err(serde::de::Error::custom("Invalid email format"));
}
Ok(email)
}
fn validate_age<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
D: serde::Deserializer<'de>,
{
let age: u32 = serde::Deserialize::deserialize(deserializer)?;
if age > 150 {
return Err(serde::de::Error::custom("Invalid age"));
}
Ok(age)
}
2.3 路径验证 #
rust
use std::path::{Path, PathBuf};
fn validate_path(base: &Path, user_path: &str) -> Result<PathBuf, String> {
let full_path = base.join(user_path);
let canonical = full_path.canonicalize()
.map_err(|_| "Invalid path")?;
let base_canonical = base.canonicalize()
.map_err(|_| "Invalid base path")?;
if !canonical.starts_with(&base_canonical) {
return Err("Path traversal detected".to_string());
}
Ok(canonical)
}
三、数据保护 #
3.1 敏感数据处理 #
rust
// 不要记录敏感数据
#[tauri::command]
fn login(username: String, password: String) -> Result<User, String> {
// ❌ 不要记录密码
// log::info!("Login: {} - {}", username, password);
// ✅ 只记录必要信息
log::info!("Login attempt for: {}", username);
let user = verify_credentials(&username, &password)?;
// 清除内存中的密码
drop(password);
Ok(user)
}
3.2 数据加密 #
rust
use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm, Nonce,
};
fn encrypt_data(key: &[u8], data: &[u8]) -> Result<Vec<u8>, String> {
let cipher = Aes256Gcm::new_from_slice(key)
.map_err(|e| e.to_string())?;
let nonce = Nonce::from_slice(b"unique nonce");
cipher.encrypt(nonce, data)
.map_err(|e| e.to_string())
}
fn decrypt_data(key: &[u8], encrypted: &[u8]) -> Result<Vec<u8>, String> {
let cipher = Aes256Gcm::new_from_slice(key)
.map_err(|e| e.to_string())?;
let nonce = Nonce::from_slice(b"unique nonce");
cipher.decrypt(nonce, encrypted)
.map_err(|e| e.to_string())
}
3.3 安全存储 #
rust
use keyring::Entry;
fn store_secret(service: &str, key: &str, value: &str) -> Result<(), String> {
let entry = Entry::new(service, key)
.map_err(|e| e.to_string())?;
entry.set_password(value)
.map_err(|e| e.to_string())
}
fn get_secret(service: &str, key: &str) -> Result<String, String> {
let entry = Entry::new(service, key)
.map_err(|e| e.to_string())?;
entry.get_password()
.map_err(|e| e.to_string())
}
四、安全配置 #
4.1 最小权限配置 #
json
// capabilities/default.json
{
"permissions": [
"core:default",
"fs:allow-read-text-file"
]
}
4.2 严格 CSP #
json
{
"security": {
"csp": "default-src 'self'; script-src 'self'; style-src 'self'"
}
}
4.3 禁用危险功能 #
json
{
"windows": [{
"webPreferences": {
"nodeIntegration": false,
"contextIsolation": true,
"sandbox": true
}
}]
}
五、错误处理 #
5.1 安全错误处理 #
rust
#[tauri::command]
fn safe_operation(input: String) -> Result<String, String> {
// 不要暴露内部错误
let result = internal_operation(&input)
.map_err(|_| "Operation failed".to_string())?;
Ok(result)
}
fn internal_operation(input: &str) -> Result<String, Box<dyn std::error::Error>> {
// 内部错误详情
// ...
Ok(format!("Processed: {}", input))
}
5.2 错误日志 #
rust
use log::{error, info};
#[tauri::command]
fn operation_with_logging(input: String) -> Result<String, String> {
info!("Operation started");
match internal_operation(&input) {
Ok(result) => {
info!("Operation succeeded");
Ok(result)
}
Err(e) => {
error!("Operation failed: {}", e); // 详细日志
Err("Operation failed".to_string()) // 简化错误返回
}
}
}
六、依赖安全 #
6.1 依赖审计 #
bash
# Rust 依赖审计
cargo audit
# 前端依赖审计
npm audit
pnpm audit
yarn audit
6.2 锁定依赖版本 #
toml
# Cargo.toml
[dependencies]
serde = "1.0.190" # 锁定版本
json
// package.json
{
"dependencies": {
"react": "18.2.0" // 锁定版本
}
}
6.3 定期更新 #
bash
# 更新 Rust 依赖
cargo update
# 更新前端依赖
pnpm update
七、安全测试 #
7.1 安全测试清单 #
markdown
## 输入验证测试
- [ ] 测试各种边界输入
- [ ] 测试特殊字符
- [ ] 测试超长输入
- [ ] 测试空输入
## 权限测试
- [ ] 测试未授权访问
- [ ] 测试权限边界
- [ ] 测试权限提升
## 注入测试
- [ ] SQL 注入测试
- [ ] XSS 测试
- [ ] 命令注入测试
- [ ] 路径遍历测试
7.2 自动化安全测试 #
yaml
# .github/workflows/security.yml
name: Security Audit
on: [push, pull_request]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Rust Audit
run: cargo audit
- name: NPM Audit
run: npm audit
八、安全审计清单 #
8.1 代码审计 #
markdown
## 输入处理
- [ ] 所有用户输入都经过验证
- [ ] 使用白名单验证
- [ ] 限制输入长度
- [ ] 正确处理特殊字符
## 输出处理
- [ ] HTML 实体编码
- [ ] URL 编码
- [ ] JavaScript 编码
- [ ] SQL 参数化
## 认证授权
- [ ] 强密码策略
- [ ] 安全会话管理
- [ ] 权限检查
- [ ] 登出功能
## 数据保护
- [ ] 敏感数据加密
- [ ] 安全存储
- [ ] 传输加密
- [ ] 数据清理
8.2 配置审计 #
markdown
## 权限配置
- [ ] 最小权限原则
- [ ] 权限范围限制
- [ ] 无多余权限
## CSP 配置
- [ ] 严格 CSP
- [ ] 无 unsafe-*
- [ ] 限制外部资源
## 安全特性
- [ ] 上下文隔离
- [ ] 沙箱模式
- [ ] 禁用开发工具
九、总结 #
9.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 输入验证 | 验证所有输入 |
| 数据保护 | 加密敏感数据 |
| 最小权限 | 只授予必要权限 |
| 安全配置 | 严格安全设置 |
| 定期审计 | 检查代码和依赖 |
9.2 下一步 #
现在你已经掌握了安全最佳实践,接下来让我们学习 打包配置,了解如何打包发布 Tauri 应用!
最后更新:2026-03-28