4.4 KiB
4.4 KiB
任务010:安全修复
目标
修复代码审查发现的4个严重安全问题。
修复清单
1. SQL注入修复( 高优先级)
问题位置:
src/routes/operation_logs.rs:49-63src/routes/charge_records.rs:130src/routes/energy_stats.rs:341
修复方案:
将所有 format! 拼接的SQL改为参数化查询。
// 错误示例(operation_logs.rs)
let sql = format!(
"SELECT * FROM operation_logs WHERE action = '{}' AND created_at >= '{}'",
action.replace("'", "''"), start_time
);
// 正确示例
let sql = "SELECT * FROM operation_logs WHERE action = $1 AND created_at >= $2";
sqlx::query_as(sql)
.bind(&action)
.bind(&start_time)
涉及文件:
src/routes/operation_logs.rssrc/routes/charge_records.rssrc/routes/energy_stats.rssrc/routes/downloads.rs
2. 密码哈希(🔴 高优先级)
问题位置: src/routes/auth.rs:51
修复方案:
集成 argon2 crate,密码存储和验证使用哈希。
// Cargo.toml 添加
argon2 = "0.5"
// 密码哈希(创建/重置用户时)
use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, SaltString},
Argon2,
};
fn hash_password(password: &str) -> Result<String> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hash = argon2.hash_password(password.as_bytes(), &salt)?;
Ok(hash.to_string())
}
// 密码验证(登录时)
fn verify_password(password: &str, hash: &str) -> Result<bool> {
let parsed_hash = PasswordHash::new(hash)?;
let argon2 = Argon2::default();
Ok(argon2.verify_password(password.as_bytes(), &parsed_hash).is_ok())
}
涉及文件:
src/routes/auth.rs— 登录验证改用verify_passwordsrc/routes/users.rs— 创建/重置密码改用hash_passwordsrc/db/migrate.rs— users表password字段长度改为255(argon2哈希较长)
3. 组织级数据隔离(🔴 高优先级)
问题位置:
src/routes/organizations.rs全部路由src/routes/charge_records.rssrc/routes/device_logs.rssrc/routes/energy_stats.rs
修复方案:
在 CurrentUser 中携带 organization_id,查询时自动过滤。
// middleware/auth.rs 修改
pub struct CurrentUser {
pub user_id: i64,
pub role_level: i32, // 0普通 1企业管理员 2总管理员
pub organization_id: Option<i64>, // 新增
pub permissions: HashSet<String>,
}
// 查询时根据角色自动过滤
pub fn apply_org_filter(query: &str, user: &CurrentUser) -> String {
if user.role_level >= 2 {
// 总管理员看全部
query.to_string()
} else if let Some(org_id) = user.organization_id {
// 企业管理员只看本组织
format!("{} WHERE organization_id = {}", query, org_id)
} else {
format!("{} WHERE 1=0", query) // 无组织用户看不了
}
}
涉及文件:
src/middleware/auth.rs— CurrentUser增加organization_idsrc/routes/organizations.rs— 所有查询加组织过滤src/routes/charge_records.rs— 加组织过滤src/routes/device_logs.rs— 加组织过滤src/routes/energy_stats.rs— 加组织过滤
4. 补充权限校验(🟡 中优先级)
问题位置:
src/routes/charge_records.rs:158src/routes/device_logs.rs:62src/routes/energy_stats.rs:81-283src/routes/downloads.rs:87
修复方案:
为每个路由添加对应的 check_permission 调用。
// 示例
#[get("/api/charge-records")]
async fn list_charge_records(
user: CurrentUser,
pool: State<MySqlPool>,
query: Query<ChargeRecordQuery>,
) -> Result<Json<Vec<ChargeRecord>>> {
auth::check_permission(&user, "charge_record:view")?; // 新增
// ... 原有逻辑
}
权限码对应:
- 充电记录 →
charge_record:view - 设备日志 →
device_log:view - 能耗统计 →
energy:view - 下载中心 →
charge_record:export或energy:export
质量约束
- 完成代码后执行
cargo check+cargo clippy,零报错零警告 - 分层拆分,单函数≤80行,命名语义化,完整注释
- 所有外部IO/网络请求异常捕获,禁止裸panic
- 分支逻辑全覆盖,不遗漏兜底分支
- 常量抽离,不使用废弃API
- Rust内存安全,合理管理所有权,禁用unsafe无合理理由
- 修复后重新运行审查清单中的安全项目,确保全部通过