# Fscan 插件架构最佳实践 ## 插件系统设计原则 ### 1. 接口统一性 所有插件必须遵循 `base.Plugin` 接口规范,确保: - 统一的初始化流程 - 标准化的扫描接口 - 一致的错误处理 - 规范的结果返回 ### 2. 模块化设计 插件采用分层架构: ``` Plugin (业务逻辑层) ↓ ServicePlugin (服务抽象层) ↓ ServiceConnector (连接实现层) ``` ### 3. 关注点分离 - **Connector**: 专注网络连接和认证 - **Plugin**: 专注业务逻辑和工作流 - **Exploiter**: 专注安全利用和攻击 ## 代码质量标准 ### 1. 命名规范 #### 包命名 ```go // 服务插件包名使用小写服务名 package mysql package redis package postgres ``` #### 结构体命名 ```go // 连接器使用[Service]Connector格式 type MySQLConnector struct {} type RedisConnector struct {} // 插件使用[Service]Plugin格式 type MySQLPlugin struct {} type RedisPlugin struct {} // 利用器使用[Service]Exploiter格式 type MySQLExploiter struct {} type RedisExploiter struct {} ``` #### 方法命名 ```go // 工厂函数使用New[Type]格式 func NewMySQLConnector() *MySQLConnector func NewMySQLPlugin() *MySQLPlugin // 注册函数使用Register[Service]Plugin格式 func RegisterMySQLPlugin() ``` ### 2. 注释标准 #### 包级别注释 ```go // MySQL插件:新一代插件架构的完整实现示例 // 展示了如何正确实现服务扫描、凭据爆破、自动利用等功能 // 本插件可作为其他数据库插件迁移的标准参考模板 ``` #### 结构体注释 ```go // MySQLConnector 实现MySQL数据库服务连接器 // 遵循 base.ServiceConnector 接口规范,提供标准化的MySQL连接和认证功能 type MySQLConnector struct { timeout time.Duration // 连接超时时间 host string // 目标主机地址 port int // 目标端口号 } ``` #### 方法注释 ```go // NewMySQLConnector 创建新的MySQL连接器实例 // 自动注册SOCKS代理支持,配置适当的超时时间 func NewMySQLConnector() *MySQLConnector {} // Authenticate 使用凭据对MySQL服务进行身份认证 // 实现 base.ServiceConnector 接口的 Authenticate 方法 // 关键优化:使用独立的Context避免上游超时问题 func (c *MySQLConnector) Authenticate(ctx context.Context, conn interface{}, cred *base.Credential) error {} ``` ### 3. 错误处理 #### 错误分类 ```go // 网络连接错误 return fmt.Errorf("连接失败: %v", err) // 认证失败错误 return fmt.Errorf("认证失败: %v", err) // 配置错误 return fmt.Errorf("无效的端口号: %s", info.Ports) ``` #### 错误日志 ```go // Debug级别:详细信息,用于调试 common.LogDebug(fmt.Sprintf("MySQL尝试认证: %s@%s:%d", cred.Username, c.host, c.port)) // Error级别:错误信息,用于排查问题 common.LogError("MySQL插件新架构不可用,请检查插件注册") // Success级别:成功信息,用户可见 common.LogSuccess(i18n.GetText("mysql_scan_success", target, cred.Username, cred.Password)) ``` ## 性能优化指导 ### 1. Context管理 #### 问题:上游Context超时 新架构中传递的Context可能存在超时问题,导致认证立即失败。 #### 解决方案:独立Context ```go func (c *Connector) Authenticate(ctx context.Context, conn interface{}, cred *base.Credential) error { // 创建独立的超时上下文,避免上游Context超时问题 // 这是解决新架构Context传递问题的关键修复 timeout := time.Duration(common.Timeout) * time.Second authCtx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() // 使用authCtx进行所有操作 } ``` ### 2. 连接池管理 #### 合理的连接池配置 ```go // 设置合理的连接生命周期 db.SetConnMaxLifetime(c.timeout) db.SetConnMaxIdleTime(c.timeout) db.SetMaxIdleConns(0) // 对于扫描工具,不保持空闲连接 ``` ### 3. 内存管理 #### 及时释放资源 ```go defer db.Close() // 数据库连接 defer conn.Close() // 网络连接 defer cancel() // Context取消 ``` #### 避免内存泄露 ```go // 使用有限大小的channel resultChan := make(chan Result, 1) // 确保goroutine正确退出 select { case result := <-resultChan: return result case <-ctx.Done(): return ctx.Err() } ``` ## 国际化实践 ### 1. 消息定义 #### 标准消息格式 ```go var PluginMessages = map[string]map[string]string{ "mysql_scan_success": { LangZH: "MySQL弱密码扫描成功: %s [%s:%s]", LangEN: "MySQL weak password scan successful: %s [%s:%s]", }, "mysql_auth_failed": { LangZH: "MySQL认证失败: %s", LangEN: "MySQL authentication failed: %s", }, } ``` #### 消息使用 ```go // 在插件中使用i18n消息 common.LogSuccess(i18n.GetText("mysql_scan_success", target, username, password)) common.LogError(i18n.GetText("mysql_auth_failed", err.Error())) ``` ### 2. 参数占位符 使用标准的printf格式化占位符: - `%s` - 字符串 - `%d` - 整数 - `%v` - 任意类型 ## 安全考虑 ### 1. 敏感信息处理 #### 日志安全 ```go // 错误:在生产日志中输出密码 common.LogInfo(fmt.Sprintf("尝试密码: %s", password)) // 正确:在debug日志中输出,生产环境可关闭 common.LogDebug(fmt.Sprintf("尝试密码: %s", password)) ``` #### 内存安全 ```go // 使用完毕后清理敏感数据 defer func() { if password != "" { password = "" } }() ``` ### 2. 网络安全 #### 超时控制 ```go // 设置合理的超时时间,避免DoS timeout := time.Duration(common.Timeout) * time.Second ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() ``` #### 连接复用 ```go // 避免过多的并发连接 db.SetMaxOpenConns(1) // 对于扫描工具限制并发连接 ``` ## 扩展性设计 ### 1. 插件能力声明 #### 标准能力集 ```go plugin.SetCapabilities([]base.Capability{ base.CapWeakPassword, // 弱密码检测 base.CapUnauthorized, // 未授权访问检测 base.CapDataExtraction, // 数据提取 base.CapFileWrite, // 文件写入 base.CapCommandExecution, // 命令执行 base.CapSQLInjection, // SQL注入 base.CapInformationLeak, // 信息泄露 }) ``` ### 2. 自定义扫描逻辑 #### 重写Scan方法 ```go // 重写扫描方法实现自定义逻辑(如未授权访问检测) func (p *RedisPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { // 先检查未授权访问 if result := p.checkUnauthorizedAccess(ctx, info); result != nil { return result, nil } // 再执行标准弱密码扫描 return p.ServicePlugin.Scan(ctx, info) } ``` ### 3. 利用模块集成 #### 可选的利用功能 ```go // 自动利用功能(可通过-nobr参数禁用) if result.Success && len(result.Credentials) > 0 && !common.DisableBrute { // 异步执行利用攻击,避免阻塞扫描进程 go p.autoExploit(context.Background(), info, result.Credentials[0]) } ``` ## 测试策略 ### 1. 单元测试 #### 连接器测试 ```go func TestMySQLConnector_Connect(t *testing.T) { connector := NewMySQLConnector() info := &common.HostInfo{ Host: "127.0.0.1", Ports: "3306", } conn, err := connector.Connect(context.Background(), info) if err != nil { t.Errorf("Connect failed: %v", err) } defer connector.Close(conn) } ``` #### 认证测试 ```go func TestMySQLConnector_Authenticate(t *testing.T) { // 测试正确凭据 cred := &base.Credential{Username: "root", Password: "123456"} err := connector.Authenticate(context.Background(), conn, cred) if err != nil { t.Errorf("Authentication failed: %v", err) } // 测试错误凭据 invalidCred := &base.Credential{Username: "invalid", Password: "invalid"} err = connector.Authenticate(context.Background(), conn, invalidCred) if err == nil { t.Error("Expected authentication to fail") } } ``` ### 2. 集成测试 #### 完整扫描流程 ```go func TestMySQLPlugin_FullScan(t *testing.T) { plugin := NewMySQLPlugin() info := &common.HostInfo{ Host: "127.0.0.1", Ports: "3306", } result, err := plugin.Scan(context.Background(), info) if err != nil { t.Errorf("Scan failed: %v", err) } if !result.Success { t.Error("Expected scan to succeed") } } ``` ### 3. 性能测试 #### 并发扫描测试 ```go func BenchmarkMySQLPlugin_Scan(b *testing.B) { plugin := NewMySQLPlugin() info := &common.HostInfo{Host: "127.0.0.1", Ports: "3306"} b.RunParallel(func(pb *testing.PB) { for pb.Next() { plugin.Scan(context.Background(), info) } }) } ``` ## 部署检查清单 ### 1. 代码质量 - [ ] 所有公共方法都有文档注释 - [ ] 错误处理完整且合理 - [ ] 无明显的内存泄露风险 - [ ] 遵循Go代码规范 ### 2. 功能完整性 - [ ] 基础连接功能正常 - [ ] 认证逻辑正确实现 - [ ] 错误场景处理完善 - [ ] 国际化消息完整 ### 3. 性能表现 - [ ] 超时控制合理 - [ ] 并发性能良好 - [ ] 资源使用合理 - [ ] 无性能瓶颈 ### 4. 安全性 - [ ] 不在日志中暴露敏感信息 - [ ] 超时控制防止DoS - [ ] 输入验证充分 - [ ] 权限控制适当 ### 5. 兼容性 - [ ] 支持SOCKS代理 - [ ] 与老版本行为兼容 - [ ] 多语言环境工作正常 - [ ] 跨平台兼容性 ## 维护指南 ### 1. 版本管理 - 使用语义化版本号 - 在元数据中更新版本信息 - 维护变更日志 ### 2. 文档更新 - 同步更新代码注释 - 更新用户使用文档 - 维护最佳实践文档 ### 3. 社区贡献 - 遵循项目贡献指南 - 提供清晰的PR描述 - 包含必要的测试用例 --- 通过遵循这些最佳实践,可以确保插件的质量、性能和可维护性,为fscan项目提供稳定可靠的插件支持。