入侵防御系统
IPS(Intrusion Prevention System)即入侵防御系统,主要用于实时检查和阻止网络入侵。与入侵检测系统(IDS)不同,IPS通常部署在网络的关键路径上,实时监控网络流量,并在发现攻击时立即采取响应措施,如丢弃恶意数据包、封锁攻击源IP地址等。
IPS的工作原理
IPS通过直接嵌入到网络流量中实现主动防御。它通过一个网络端口接收来自外部系统的流量,经过检查确认其中不包含异常活动或可疑内容后,再通过另一个端口传送到内部系统中。有问题的数据包以及所有来自同一数据流的后续数据包都会在IPS设备中被清除掉。
GO实现IPS
下面我们用 Go 语言编写,一个可以在命令行中获取并打印本地 IP 地址(基于常见网络接口)的示例程序,这里主要是获取 IPv4 地址进行展示,示例代码使用了标准库中的 net
包:
获取本地网络接口上绑定的 IP
package main
import (
"fmt"
"net"
)
func main() {
addrs, err := net.InterfaceAddrs()
if err!= nil {
fmt.Printf("获取网络接口地址失败: %v\n", err)
return
}
for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet)
if ok &&!ipNet.IP.IsLoopback() && ipNet.IP.To4()!= nil {
fmt.Println("IP地址:", ipNet.IP.String())
}
}
}
运行
也可编译后运行
go build ips.go
然后运行生成的可执行文件(在 Windows 上是 .exe
后缀的文件,在 Linux、macOS 等系统上就是普通的二进制文件)也能看到相应的 IP 地址输出。
请注意:这里只是简单获取本地网络接口上绑定的 IP 示例
对于IPS而言,他要检测的是网络中的流量(比如探测网络中其他设备 IP 等)这需要进一步扩展代码,例如利用 net.Dial
等去做网络连接测试、IP 扫描等操作。
探测网络中其他设备 IP
这里只是通过简单的 ICMP
即 ping
操作概念类似的简单探测
package main
import (
"fmt"
"net"
"os"
"sync"
"time"
)
func PingIP(ip string, wg *sync.WaitGroup, result chan<- string) {
defer wg.Done()
conn, err := net.DialTimeout("ip4:icmp", ip, time.Second*2)
if err == nil {
conn.Close()
result <- ip
}
}
func main() {
if len(os.Args)!= 2 {
fmt.Println("请输入要扫描的网段,例如 192.168.1.0/24")
return
}
_, ipnet, err := net.ParseCIDR(os.Args[1])
if err!= nil {
fmt.Printf("解析网段失败: %v\n", err)
return
}
var wg sync.WaitGroup
result := make(chan string)
for ip := ipnet.IP.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
wg.Add(1)
go PingIP(ip.String(), &wg, result)
}
go func() {
wg.Wait()
close(result)
}()
for aliveIP := range result {
fmt.Println("存活IP:", aliveIP)
}
}
func inc(ip net.IP) {
for j := len(ip) - 1; j >= 0; j-- {
ip[j]++
if ip[j] > 0 {
break
}
}
}
在命令行中使用这个扩展版本时,可以像这样运行(假设编译后的可执行文件名为 ips
):
./ips 192.168.1.0/24
增加规则文件(rules.json)
首先我们设定一个规则文件,用json的方式来保存规则
[
{
"id": "1001",
"description": "SSH暴力破解检测",
"protocol": "tcp",
"dst_port": 22,
"action": "block",
"threshold": 5,
"time_window": 60
},
{
"id": "1002",
"description": "RDP暴力破解检测",
"protocol": "tcp",
"dst_port": 3389,
"action": "block",
"threshold": 5,
"time_window": 60
},
{
"id": "1003",
"description": "MySQL暴力破解检测",
"protocol": "tcp",
"dst_port": 3306,
"action": "block",
"threshold": 5,
"time_window": 60
}
]
然后我们写一个go文件,来检测 IP 对相应端口的访问是否达到规则中定义的暴力破解阈值情况
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"sync"
"time"
)
// Rule结构体表示一条安全规则
type Rule struct {
ID string `json:"id"`
Description string `json:"description"`
Protocol string `json:"protocol"`
DstPort int `json:"dst_port"`
Action string `json:"action"`
Threshold int `json:"threshold"`
TimeWindow int `json:"time_window"`
}
// LoadRules从文件加载安全规则
func LoadRules(filePath string) ([]*Rule, error) {
content, err := ioutil.ReadFile(filePath)
if err!= nil {
return nil, fmt.Errorf("读取规则文件失败: %v", err)
}
var rules []*Rule
err = json.Unmarshal(content, &rules)
if err!= nil {
return nil, fmt.Errorf("解析规则文件失败: %v", err)
}
return rules, nil
}
// ConnectionRecord记录IP的连接记录(简化示例,仅记录次数)
type ConnectionRecord struct {
mu sync.Mutex
connections map[string]int
}
func NewConnectionRecord() *ConnectionRecord {
return &ConnectionRecord{
connections: make(map[string]int),
}
}
func (cr *ConnectionRecord) Record(ip net.IP, port int) {
cr.mu.Lock()
key := fmt.Sprintf("%s:%d", ip.String(), port)
cr.connections[key]++
cr.mu.Unlock()
}
func (cr *ConnectionRecord) CheckViolation(rules []*Rule) []*Rule {
var violatedRules []*Rule
cr.mu.Lock()
now := time.Now()
for _, rule := range rules {
for key, count := range cr.connections {
parts := strings.Split(key, ":")
ip := net.ParseIP(parts[0])
port := atoi(parts[1])
if port == rule.DstPort && count >= rule.Threshold {
// 这里简单判断次数达到阈值就算违规,实际可能需考虑时间窗口等更复杂逻辑细化
violatedRules = append(violatedRules, rule)
}
}
}
cr.mu.Unlock()
return violatedRules
}
func atoi(s string) int {
n, _ := fmt.Sscanf(s, "%d", &n)
return n
}
func main() {
// 获取当前目录下的rules.json作为配置文件路径(可根据实际调整)
currentDir, err := os.Getwd()
if err!= nil {
fmt.Printf("获取当前目录失败: %v\n", err)
return
}
configFilePath := filepath.Join(currentDir, "rules.json")
rules, err := LoadRules(configFilePath)
if err!= nil {
fmt.Printf("加载规则失败: %v\n", err)
return
}
targetIPNet := &net.IPNet{}
_, targetIPNet, err = net.ParseCIDR("192.168.1.0/24")
if err!= nil {
fmt.Printf("解析目标IP网段失败: %v\n", err)
return
}
record := NewConnectionRecord()
// 模拟IP对内网端口的连接访问(这里简单循环增加连接次数示例)
for i := 0; i < 10; i++ {
for ip := targetIPNet.IP.Mask(targetIPNet.Mask); targetIPNet.Contains(ip); inc(ip) {
// 简单模拟多次访问各规则关注的端口,实际场景会根据真实网络连接情况记录
for _, rule := range rules {
record.Record(ip, rule.DstPort)
}
}
}
violatedRules := record.CheckViolation(rules)
for _, violatedRule := range violatedRules {
fmt.Printf("IP段内存在疑似违反规则 %s 的情况,规则描述:%s\n", violatedRule.ID, violatedRule.Description)
}
}
func inc(ip net.IP) {
for j := len(ip) - 1; j >= 0; j-- {
ip[j]++
if ip[j] > 0 {
break
}
}
}
- 首先定义了
Rule
结构体,其字段与 JSON 格式的规则文件中的各个属性相对应,用于表示一条安全规则。 LoadRules
函数用于读取指定路径的 JSON 规则文件,并将其解析为Rule
结构体的切片,若读取或解析过程出现错误则返回相应的错误信息。
ConnectionRecord
结构体用于记录 IP 地址对不同端口的连接情况,内部使用一个互斥锁(sync.Mutex
)来保证并发安全,通过Record
方法记录每个 IP 和端口的连接次数,使用CheckViolation
方法来检查当前的连接记录是否违反了给定的规则(这里只是简单根据连接次数是否达到阈值判断,实际可进一步完善考虑时间窗口等更多细节)。
- 先获取当前目录下规则配置文件(
rules.json
)的路径(可按需修改为其他路径),调用LoadRules
函数加载规则列表。 - 接着解析要检测的目标 IP 网段(示例中为
192.168.1.0/24
,可以根据实际情况更改),创建一个ConnectionRecord
实例来记录连接情况。 - 然后通过循环模拟 IP 对内网各端口的访问(这里只是简单循环增加访问次数示意,实际需要结合真实的网络连接情况来记录),并调用
ConnectionRecord
的Record
方法记录每次的访问情况。 - 最后调用
ConnectionRecord
的CheckViolation
方法检查是否有违反规则的情况,若有则打印出相应的违规规则信息。
注意
这里的代码仅是抛砖引玉,其中的规则检查逻辑,尤其是针对时间窗口的处理比较简化,在实际应用场景中,你可能需要精确地记录每个连接的时间戳,并按照时间窗口准确判断是否在指定时间段内达到了阈值等情况,以实现更符合实际需求的暴力破解检测逻辑。
模拟的 IP 访问场景也非常简单,只是为了演示功能而进行的简单循环增加访问次数,实际需要对接真实的网络流量监控等相关机制来准确记录 IP 的访问情况。