安全修复: - CRITICAL: SSO重定向URL注入漏洞 - 修复redirect_uri白名单验证 - HIGH: SSO ClientSecret未验证 - 使用crypto/subtle.ConstantTimeCompare验证 - HIGH: 邮件验证码熵值过低(3字节) - 提升到6字节(48位熵) - HIGH: 短信验证码熵值过低(4字节) - 提升到6字节 - HIGH: Goroutine使用已取消上下文 - auth_email.go使用独立context+超时 - HIGH: SQL LIKE查询注入风险 - permission/role仓库使用escapeLikePattern 新功能: - Go SDK: sdk/go/user-management/ 完整SDK实现 - CAS SSO框架: internal/auth/cas.go CAS协议支持 其他: - L1Cache实例问题修复 - AuthMiddleware共享l1Cache - 设备指纹XSS防护 - 内存存储替代localStorage - 响应格式协议中间件 - 导出无界查询修复
9.9 KiB
9.9 KiB
项目质量规范 (Production Quality Standards)
版本: 1.0 更新日期: 2026-04-03 适用范围: D:\project (Go + React/TypeScript)
一、安全规范 (Security)
1.1 加密与随机数
// ✅ 正确:随机数生成失败时返回错误
func generateSecureToken(length int) (string, error) {
bytes := make([]byte, length)
if _, err := rand.Read(bytes); err != nil {
return "", fmt.Errorf("failed to generate secure token: %w", err)
}
return base64.URLEncoding.EncodeToString(bytes)[:length], nil
}
// ❌ 禁止:使用不安全 fallback
func generateSecureToken(length int) string {
// ...
if _, err := rand.Read(bytes); err != nil {
// 禁止使用时间戳或 math/rand 作为 fallback
for i := range bytes {
bytes[i] = byte(time.Now().UnixNano() % 256) // 不安全!
}
}
// ...
}
1.2 敏感数据存储
// ✅ 正确:敏感数据使用内存存储
let deviceFingerprintCache: DeviceFingerprint | null = null
export function getDeviceFingerprint(): DeviceFingerprint {
if (cachedFingerprint) return cachedFingerprint
cachedFingerprint = buildFingerprint()
return cachedFingerprint
}
// ❌ 禁止:敏感数据存入 localStorage/sessionStorage
localStorage.setItem('device_id', deviceId) // XSS 可读取
localStorage.setItem('token', token) // XSS 可读取
1.3 认证与授权
// ✅ 正确:所有受保护路由使用中间件
adminRoutes.Use(AuthMiddleware.Required())
adminRoutes.Use(AdminOnly())
// ❌ 禁止:硬编码权限检查
if user.Role != "admin" {
c.JSON(403, "forbidden") // 分散的权限检查
}
1.4 SQL 注入防护
// ✅ 正确:使用参数化查询
db.Where("user_id = ?", userID)
db.Where("name LIKE ?", "%"+EscapeLikeWildcard(name)+"%")
// ❌ 禁止:字符串拼接 SQL
db.Where("user_id = " + userID) // SQL 注入风险
1.5 错误信息泄露
// ✅ 正确:分类错误,不返回原始错误
response.Error(c, http.StatusInternalServerError, "服务器内部错误")
// ❌ 禁止:返回原始错误信息给客户端
c.JSON(500, gin.H{"error": err.Error()}) // 可能泄露内部信息
二、并发与性能 (Concurrency & Performance)
2.1 Goroutine 管理
// ✅ 正确:使用 context 控制生命周期
go func() {
select {
case <-ctx.Done():
return
case <-ticker.C:
cleanup()
}
}()
// ❌ 禁止:fire-and-forget goroutine
go publishEvent(ctx, event, data) // 无限制的 goroutine
2.2 Map 并发访问
// ✅ 正确:使用互斥锁保护共享 map
type SSOManager struct {
mu sync.RWMutex
sessions map[string]*SSOSession
}
func (m *SSOManager) Get(key string) *SSOSession {
m.mu.RLock()
defer m.mu.RUnlock()
return m.sessions[key]
}
// ❌ 禁止:map 并发读写
sessions[key] = session // concurrent map write
2.3 数据库查询
// ✅ 正确:使用 JOIN 替代 N+1 查询
func GetUserRolesAndPermissions(ctx, userID) ([]*Role, []*Permission, error) {
// 单次 JOIN 查询
rows := db.Raw(`SELECT ... FROM user_roles ur
JOIN roles r ON ur.role_id = r.id
LEFT JOIN role_permissions rp ON r.id = rp.role_id
LEFT JOIN permissions p ON rp.permission_id = p.id
WHERE ur.user_id = ?`, userID)
}
// ❌ 禁止:循环内单独查询(N+1)
for _, roleID := range roleIDs {
ancestors := repo.GetAncestorIDs(ctx, roleID) // 每 role 执行一次查询
}
2.4 导出与批处理
// ✅ 正确:分批处理 + 最大限制
const MaxExportRecords = 100000
const BatchSize = 5000
for {
batch, hasMore, err := repo.ListBatch(ctx, cursor, BatchSize)
if total >= MaxExportRecords {
break // 防止 OOM
}
// 处理 batch...
}
// ❌ 禁止:无限制加载全表到内存
allRecords := repo.ListAll(ctx) // 百万级记录 OOM
三、API 设计规范 (API Design)
3.1 响应格式
// ✅ 正确:统一包装响应
type APIResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// 成功响应
response.Success(c, data) // {code: 0, message: "success", data: {...}}
response.Paginated(c, items, total, page, pageSize)
// ❌ 禁止:裸 JSON 响应
c.JSON(200, gin.H{"users": users}) // 无统一格式
3.2 错误处理
// ✅ 正确:使用标准错误响应
response.BadRequest(c, "无效的请求参数")
response.Unauthorized(c, "认证已过期,请重新登录")
response.Forbidden(c, "权限不足")
response.NotFound(c, "用户不存在")
response.InternalError(c, "服务器内部错误")
// ❌ 禁止:直接返回错误字符串
c.JSON(400, gin.H{"error": "bad request"})
3.3 分页参数
// ✅ 统一分页格式
GET /users?page=1&page_size=20
// 响应
{
"code": 0,
"message": "success",
"data": {
"items": [...],
"total": 100,
"page": 1,
"page_size": 20,
"pages": 5
}
}
四、代码风格规范 (Code Style)
4.1 错误处理原则
// ✅ 正确:明确处理错误
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrNotFound
}
return nil, fmt.Errorf("query failed: %w", err)
}
// ❌ 禁止:忽略错误
data, _ := json.Marshal(v) // 忽略 marshal 错误
4.2 Context 使用
// ✅ 正确:使用请求 context 或带超时的 context
func HandleRequest(c *gin.Context) {
ctx := c.Request.Context()
// 或
ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
defer cancel()
}
// ❌ 禁止:使用 context.Background()
go func() {
doSomething(context.Background()) // 生命周期不关联
}()
4.3 前端 TypeScript
// ✅ 正确:完整的类型定义
interface User {
id: number
username: string
email: string
}
// ❌ 禁止:滥用 any
function processData(data: any): any {
return data // 类型安全丧失
}
// ✅ 正确:useMemo 缓存 expensive 计算
const columns = useMemo(() => [
{ key: 'name', dataIndex: 'name' },
// ...
], [dependencies])
// ❌ 禁止:每次渲染重新创建
const columns = [ // 每次渲染创建新数组
{ key: 'name', dataIndex: 'name' },
]
五、测试规范 (Testing)
5.1 单元测试
// ✅ 正确:表驱动测试 + 完整断言
func TestLogin(t *testing.T) {
tests := []struct {
name string
req LoginRequest
wantErr bool
}{
{"valid login", LoginRequest{Username: "test", Password: "pass"}, false},
{"invalid password", LoginRequest{Username: "test", Password: "wrong"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Login(tt.req)
if (err != nil) != tt.wantErr {
t.Errorf("Login() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
5.2 集成测试
// ✅ 正确:使用测试数据库,测试后清理
func TestUserCRUD(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
repo := NewUserRepository(db)
user, err := repo.Create(ctx, &User{Username: "test"})
if err != nil {
t.Fatalf("failed to create user: %v", err)
}
got, err := repo.GetByID(ctx, user.ID)
if err != nil {
t.Errorf("GetByID() error = %v", err)
}
if got.Username != user.Username {
t.Errorf("GetByID() = %v, want %v", got.Username, user.Username)
}
}
六、禁止模式 (Prohibited Patterns)
6.1 安全相关
| 禁止模式 | 风险 | 正确做法 |
|---|---|---|
localStorage.setItem('token', token) |
XSS 读取 | 内存存储或 HttpOnly Cookie |
crypto/rand 失败 fallback 到时间戳 |
Token 可预测 | 返回错误 |
c.JSON(500, gin.H{"error": err}) |
内部信息泄露 | 统一错误响应 |
| 用户输入拼接 SQL | SQL 注入 | 参数化查询 |
| 重定向未验证 URL | Open Redirect | 白名单验证 |
6.2 性能相关
| 禁止模式 | 风险 | 正确做法 |
|---|---|---|
for { repo.Query() } 循环内查询 |
N+1 | JOIN 批量查询 |
ListAll() 全量加载 |
OOM | 分批 + 最大限制 |
context.Background() 在 goroutine |
泄漏 | 带超时的 context |
| 共享 map 无锁保护 | panic | sync.RWMutex |
6.3 代码质量
| 禁止模式 | 风险 | 正确做法 |
|---|---|---|
data as SomeType 类型断言 |
运行时 panic | 类型守卫检查 |
| 魔法数字 | 可读性差 | 定义常量 |
| 重复代码 > 3 处 | 维护性差 | 提取函数/模块 |
| 过长函数 > 100 行 | 可读性差 | 拆分为小函数 |
七、审查清单 (Review Checklist)
提交前必须检查
go vet ./...无警告go build ./...编译通过npm run build前端编译通过npm run lint无 error(warning 可接受)- 无
TODO: fixme或FIXME未处理 - 无硬编码密码/密钥/Secret
- 无
console.log生产代码 - 新增 handler 使用
response.Success()而非裸c.JSON - 敏感数据不写入 localStorage/sessionStorage
- 异步操作有超时控制
安全专项检查
- 新增 API 有权限控制
- 用户输入有验证
- SQL 使用参数化查询
- 错误不泄露内部信息
- Token 使用 crypto/rand 生成
八、持续改进
- 每季度进行一次完整的安全审计
- 发现新的反模式及时加入禁止列表
- 定期更新依赖版本(安全补丁)
- 代码覆盖率目标:核心业务 > 80%
附录:已有安全实践
- ✅ Argon2id 密码哈希
- ✅ JWT JTI 黑名单
- ✅ TOTP 两步验证
- ✅ CSRF Token 保护
- ✅ XSS window guard
- ✅ SSRF URL 验证
- ✅ 参数化查询防注入