mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00

- 重构插件注册架构采用现代工厂模式和自动发现机制 - 新增完整的插件元数据管理系统支持版本能力标签等信息 - 实现智能插件适配器提供向后兼容的桥接功能 - 建立MySQL Redis SSH三个标准插件作为新架构参考实现 - 优化插件扫描逻辑支持按端口按类型的智能查询和过滤 - 添加国际化支持和完善的文档体系 - 代码量减少67%维护成本大幅降低扩展性显著提升 新架构特点: - 零配置插件注册import即用 - 工厂模式延迟初始化和依赖注入 - 丰富元数据系统和能力声明 - 完全解耦的模块化设计 - 面向未来的可扩展架构 测试验证: MySQL和Redis插件功能完整包括弱密码检测未授权访问检测和自动利用攻击
419 lines
9.9 KiB
Markdown
419 lines
9.9 KiB
Markdown
# 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项目提供稳定可靠的插件支持。 |