fscan/PLUGIN_BEST_PRACTICES.md
ZacharyZcR 43f210ffc6 feat: 实现新一代插件注册系统完全替代传统手动注册模式
- 重构插件注册架构采用现代工厂模式和自动发现机制
- 新增完整的插件元数据管理系统支持版本能力标签等信息
- 实现智能插件适配器提供向后兼容的桥接功能
- 建立MySQL Redis SSH三个标准插件作为新架构参考实现
- 优化插件扫描逻辑支持按端口按类型的智能查询和过滤
- 添加国际化支持和完善的文档体系
- 代码量减少67%维护成本大幅降低扩展性显著提升

新架构特点:
- 零配置插件注册import即用
- 工厂模式延迟初始化和依赖注入
- 丰富元数据系统和能力声明
- 完全解耦的模块化设计
- 面向未来的可扩展架构

测试验证: MySQL和Redis插件功能完整包括弱密码检测未授权访问检测和自动利用攻击
2025-08-07 11:28:34 +08:00

419 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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项目提供稳定可靠的插件支持。