从零开始构建HIDS主机入侵检测系统:Python Flask全栈开发实战
从零开始构建HIDS主机入侵检测系统:Python Flask全栈开发实战
引言
在当今网络安全威胁日益复杂的环境下,主机入侵检测系统(HIDS)已成为企业安全防护体系中不可或缺的一环。本文将详细介绍如何使用Python Flask框架构建一个功能完整的HIDS系统,涵盖文件完整性监控、网络连接监控、进程监控和智能报警等核心功能。
通过本文的学习,你将掌握:
- Python Flask Web应用开发
- SQLite数据库设计与操作
- 系统监控技术实现
- 前端可视化开发
- 前后端数据交互
- 安全监控算法设计
项目概述
什么是HIDS?
HIDS(Host-based Intrusion Detection System)主机入侵检测系统是一种安装在主机上的安全软件,通过监控主机的文件系统、网络连接、进程行为等来检测入侵行为。与网络入侵检测系统(NIDS)不同,HIDS专注于单个主机的安全状态监控。
系统架构
我们的HIDS系统采用经典的Web应用架构:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Web浏览器 │◄──►│ Flask后端 │◄──►│ SQLite数据库 │
│ Bootstrap │ │ Python │ │ 数据存储 │
│ Charts.js │ │ psutil │ │ 监控数据 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
核心功能模块
- 文件完整性监控模块:实时监控指定目录的文件变化
- 网络连接监控模块:监控主机的网络连接状态
- 进程监控模块:监控系统进程的运行状态
- 智能报警模块:基于规则引擎生成安全报警
- Web管理界面:提供友好的可视化操作界面
开发环境准备
技术栈选择
后端技术栈:
- Python 3.7+:主要开发语言
- Flask 2.0+:Web框架
- SQLite 3:轻量级数据库
- psutil 5.8+:系统监控库
- threading:多线程处理
前端技术栈:
- HTML5 + CSS3:页面结构和样式
- JavaScript ES6+:交互逻辑
- Bootstrap 5:UI框架
- Charts.js:数据可视化
- Ajax:异步数据交互
环境配置
创建项目目录结构:
hids/
├── app.py # Flask主应用
├── database.py # 数据库模块
├── monitor.py # 监控核心模块
├── run.py # 应用启动脚本
├── config.yaml # 配置文件
├── requirements.txt # 依赖包列表
├── static/ # 静态资源
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── app.js
├── templates/ # HTML模板
│ └── index.html
├── data/ # 数据目录
│ └── hids.db
└── logs/ # 日志目录└── hids.log
安装依赖包:
pip install flask psutil pyyaml sqlite3
requirements.txt内容:
Flask==2.3.2
psutil==5.9.5
PyYAML==6.0
Werkzeug==2.3.6
数据库设计实现
数据库架构设计
良好的数据库设计是HIDS系统的基础。我们需要设计三个核心数据表来存储不同类型的监控数据。
创建database.py文件:
import sqlite3
import logging
from datetime import datetime
import os# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler('logs/hids.log'),logging.StreamHandler()]
)
logger = logging.getLogger(__name__)def get_db_connection():"""获取数据库连接"""try:# 确保data目录存在os.makedirs('data', exist_ok=True)conn = sqlite3.connect('data/hids.db', check_same_thread=False)conn.row_factory = sqlite3.Rowreturn connexcept Exception as e:logger.error(f"数据库连接错误: {e}")raisedef init_database():"""初始化数据库表结构"""conn = get_db_connection()cursor = conn.cursor()try:# 网络连接表cursor.execute('''CREATE TABLE IF NOT EXISTS network_connections (id INTEGER PRIMARY KEY AUTOINCREMENT,local_address TEXT,local_port INTEGER,remote_address TEXT,remote_port INTEGER,process_name TEXT,protocol INTEGER,connection_state TEXT,established_time TEXT,created_at TEXT DEFAULT CURRENT_TIMESTAMP,status TEXT DEFAULT 'normal')''')# 文件完整性表cursor.execute('''CREATE TABLE IF NOT EXISTS file_integrity (id INTEGER PRIMARY KEY AUTOINCREMENT,file_path TEXT UNIQUE,file_hash TEXT,file_size INTEGER,created_at TEXT DEFAULT CURRENT_TIMESTAMP,updated_at TEXT DEFAULT CURRENT_TIMESTAMP,status TEXT DEFAULT 'normal')''')# 报警表cursor.execute('''CREATE TABLE IF NOT EXISTS alerts (id INTEGER PRIMARY KEY AUTOINCREMENT,alert_type TEXT,severity TEXT,title TEXT,description TEXT,source_ip TEXT,source_process TEXT,target_file TEXT,created_at TEXT DEFAULT CURRENT_TIMESTAMP,resolved BOOLEAN DEFAULT 0)''')# 创建索引优化查询性能cursor.execute('CREATE INDEX IF NOT EXISTS idx_network_created ON network_connections(created_at)')cursor.execute('CREATE INDEX IF NOT EXISTS idx_file_path ON file_integrity(file_path)')cursor.execute('CREATE INDEX IF NOT EXISTS idx_alert_created ON alerts(created_at)')cursor.execute('CREATE INDEX IF NOT EXISTS idx_alert_resolved ON alerts(resolved)')conn.commit()logger.info("数据库初始化完成")except Exception as e:logger.error(f"数据库初始化错误: {e}")conn.rollback()raisefinally:conn.close()def reset_database():"""重置数据库"""try:if os.path.exists('data/hids.db'):os.remove('data/hids.db')logger.info("数据库文件已删除")init_database()logger.info("数据库重置完成")except Exception as e:logger.error(f"数据库重置错误: {e}")raiseif __name__ == '__main__':init_database()
数据库连接池优化
为了提高数据库访问性能,我们实现连接池管理:
class DatabasePool:"""数据库连接池"""def __init__(self, max_connections=10):self.max_connections = max_connectionsself.connections = []self.in_use = set()def get_connection(self):"""获取数据库连接"""if self.connections:conn = self.connections.pop()self.in_use.add(id(conn))return connelse:conn = get_db_connection()self.in_use.add(id(conn))return conndef return_connection(self, conn):"""归还数据库连接"""conn_id = id(conn)if conn_id in self.in_use:self.in_use.remove(conn_id)if len(self.connections) < self.max_connections:self.connections.append(conn)else:conn.close()def close_all(self):"""关闭所有连接"""for conn in self.connections:conn.close()self.connections.clear()self.in_use.clear()# 全局连接池实例
db_pool = DatabasePool()
监控核心模块开发
网络连接监控实现
网络连接监控是HIDS的核心功能之一。我们使用psutil库获取系统网络连接信息:
import psutil
import socket
import threading
import time
from datetime import datetime
import logging
from database import get_db_connection, db_poollogger = logging.getLogger(__name__)class HIDSMonitor:def __init__(self, config=None):self.config = config or {}self.monitoring = Falseself.monitor_threads = {}# 协议映射self.protocol_map = {socket.SOCK_STREAM: 1, # TCPsocket.SOCK_DGRAM: 2, # UDP}# 连接状态映射self.state_map = {'ESTABLISHED': '已建立','SYN_SENT': '同步已发送','SYN_RECV': '同步已接收','FIN_WAIT1': '结束等待1','FIN_WAIT2': '结束等待2','TIME_WAIT': '时间等待','CLOSE': '已关闭','CLOSE_WAIT': '关闭等待','LAST_ACK': '最后确认','LISTEN': '监听中','CLOSING': '正在关闭','NONE': '无状态'}def monitor_network_connections(self):"""监控网络连接"""logger.info("网络连接监控线程启动")while self.monitoring:try:conn = Noneconn = db_pool.get_connection()cursor = conn.cursor()# 获取当前网络连接connections = psutil.net_connections()for conn_item in connections:try:# 提取连接信息local_addr = ""local_port = 0if hasattr(conn_item, 'laddr') and conn_item.laddr:if len(conn_item.laddr) >= 2:local_addr = str(conn_item.laddr[0])local_port = int(conn_item.laddr[1])remote_addr = ""remote_port = 0if hasattr(conn_item, 'raddr') and conn_item.raddr:if len(conn_item.raddr) >= 2:remote_addr = str(conn_item.raddr[0])remote_port = int(conn_item.raddr[1])# 获取进程名process_name = "Unknown"try:if conn_item.pid:process = psutil.Process(conn_item.pid)process_name = process.name()except (psutil.NoSuchProcess, psutil.AccessDenied):pass# 协议识别protocol = self.protocol_map.get(conn_item.type, 0)# 状态转换connection_state = conn_item.status or 'NONE'# 建立时间established_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')# 检查是否已存在相同连接cursor.execute('''SELECT id FROM network_connections WHERE local_address = ? AND local_port = ? AND remote_address = ? AND remote_port = ? AND process_name = ?ORDER BY created_at DESC LIMIT 1''', (local_addr, local_port, remote_addr, remote_port, process_name))existing = cursor.fetchone()if not existing:# 插入新连接记录cursor.execute('''INSERT INTO network_connections (local_address, local_port, remote_address, remote_port, process_name, protocol, connection_state, established_time)VALUES (?, ?, ?, ?, ?, ?, ?, ?)''', (local_addr, local_port, remote_addr, remote_port,process_name, protocol, connection_state, established_time))except Exception as e:logger.warning(f"处理网络连接时出错: {e}")continueconn.commit()logger.debug(f"网络连接监控完成,处理了 {len(connections)} 个连接")except Exception as e:logger.error(f"网络连接监控错误: {e}")if conn:conn.rollback()finally:if conn:db_pool.return_connection(conn)# 等待下次扫描time.sleep(self.config.get('network_scan_interval', 60))def monitor_file_integrity(self):"""监控文件完整性"""logger.info("文件完整性监控线程启动")monitored_dirs = self.config.get('monitored_directories', ['C:\\Windows\\System32','C:\\Program Files'])while self.monitoring:try:for directory in monitored_dirs:if os.path.exists(directory):self.scan_directory(directory)logger.debug("文件完整性扫描完成")except Exception as e:logger.error(f"文件完整性监控错误: {e}")# 等待下次扫描time.sleep(self.config.get('file_scan_interval', 300))def scan_directory(self, directory):"""扫描目录"""conn = Nonetry:conn = db_pool.get_connection()cursor = conn.cursor()for root, dirs, files in os.walk(directory):for file in files:file_path = os.path.join(root, file)try:# 计算文件哈希file_hash = self.calculate_file_hash(file_path)file_size = os.path.getsize(file_path)# 检查文件是否已存在cursor.execute('''SELECT * FROM file_integrity WHERE file_path = ?''', (file_path,))existing = cursor.fetchone()if existing:# 检查文件是否发生变化if existing['file_hash'] != file_hash:cursor.execute('''UPDATE file_integrity SET file_hash = ?, file_size = ?, updated_at = ?, status = 'changed'WHERE file_path = ?''', (file_hash, file_size, datetime.now().strftime('%Y-%m-%d %H:%M:%S'), file_path))# 生成报警self.create_alert('file_integrity', 'MEDIUM', '文件被修改', f'文件 {file_path} 内容发生变化',target_file=file_path)logger.warning(f"文件被修改: {file_path}")else:# 插入新文件记录cursor.execute('''INSERT INTO file_integrity (file_path, file_hash, file_size)VALUES (?, ?, ?)''', (file_path, file_hash, file_size))logger.info(f"发现新文件: {file_path}")except Exception as e:logger.warning(f"扫描文件错误 {file_path}: {e}")continueconn.commit()except Exception as e:logger.error(f"目录扫描错误 {directory}: {e}")if conn:conn.rollback()finally:if conn:db_pool.return_connection(conn)def calculate_file_hash(self, file_path):"""计算文件SHA-256哈希值"""try:sha256_hash = hashlib.sha256()with open(file_path, "rb") as f:for byte_block in iter(lambda: f.read(4096), b""):sha256_hash.update(byte_block)return sha256_hash.hexdigest()except Exception as e:logger.error(f"计算文件哈希错误 {file_path}: {e}")return ""def monitor_processes(self):"""监控进程"""logger.info("进程监控线程启动")while self.monitoring:try:processes = []for proc in psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_percent', 'create_time', 'username', 'cmdline']):try:proc_info = proc.infoprocesses.append(proc_info)except (psutil.NoSuchProcess, psutil.AccessDenied):continue# 检测可疑进程suspicious_processes = self.detect_suspicious_processes(processes)for process in suspicious_processes:self.create_alert('process', 'HIGH', '检测到可疑进程', f'发现可疑进程: {process["name"]} (PID: {process["pid"]})',source_process=process['name'])logger.debug(f"进程监控完成,扫描了 {len(processes)} 个进程")except Exception as e:logger.error(f"进程监控错误: {e}")# 等待下次扫描time.sleep(self.config.get('process_scan_interval', 30))def detect_suspicious_processes(self, processes):"""检测可疑进程"""suspicious = []# 简单的可疑进程检测规则suspicious_names = ['mimikatz', 'pwdump', 'hashdump', 'meterpreter','backdoor', 'trojan', 'keylogger', 'rootkit']for process in processes:process_name = process.get('name', '').lower()# 检查进程名是否包含可疑关键词for suspicious_name in suspicious_names:if suspicious_name in process_name:suspicious.append(process)break# 检查CPU使用率异常cpu_percent = process.get('cpu_percent', 0)if cpu_percent > 80:suspicious.append(process)# 检查内存使用率异常memory_percent = process.get('memory_percent', 0)if memory_percent > 80:suspicious.append(process)return suspiciousdef create_alert(self, alert_type, severity, title, description, source_ip=None, source_process=None, target_file=None):"""创建报警记录"""conn = Nonetry:conn = db_pool.get_connection()cursor = conn.cursor()cursor.execute('''INSERT INTO alerts (alert_type, severity, title, description, source_ip, source_process, target_file)VALUES (?, ?, ?, ?, ?, ?, ?)''', (alert_type, severity, title, description,source_ip, source_process, target_file))conn.commit()logger.warning(f"生成报警: {title} - {description}")except Exception as e:logger.error(f"创建报警错误: {e}")if conn:conn.rollback()finally:if conn:db_pool.return_connection(conn)def start_monitoring(self):"""启动所有监控模块"""if self.monitoring:logger.warning("监控已在运行中")returnself.monitoring = Truelogger.info("启动HIDS监控")# 启动网络监控线程network_thread = threading.Thread(target=self.monitor_network_connections,name="NetworkMonitor")network_thread.daemon = Truenetwork_thread.start()self.monitor_threads['network'] = network_thread# 启动文件监控线程file_thread = threading.Thread(target=self.monitor_file_integrity,name="FileMonitor")file_thread.daemon = Truefile_thread.start()self.monitor_threads['file'] = file_thread# 启动进程监控线程process_thread = threading.Thread(target=self.monitor_processes,name="ProcessMonitor")process_thread.daemon = Trueprocess_thread.start()self.monitor_threads['process'] = process_threadlogger.info("所有监控模块已启动")def stop_monitoring(self):"""停止所有监控模块"""self.monitoring = Falselogger.info("停止HIDS监控")# 等待所有线程结束for name, thread in self.monitor_threads.items():if thread.is_alive():thread.join(timeout=5)logger.info(f"{name}监控线程已停止")self.monitor_threads.clear()logger.info("所有监控模块已停止")def get_monitoring_status(self):"""获取监控状态"""return {'monitoring': self.monitoring,'threads': {name: thread.is_alive() for name, thread in self.monitor_threads.items()}}
Flask Web应用开发
主应用架构设计
现在我们来开发Flask主应用,提供RESTful API接口和Web界面:
from flask import Flask, render_template, jsonify, request
import sqlite3
import logging
from datetime import datetime
from monitor import HIDSMonitor
from database import get_db_connection, init_database
import yaml
import os# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler('logs/hids.log'),logging.StreamHandler()]
)
logger = logging.getLogger(__name__)# 创建Flask应用
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hids-secret-key-2024'# 全局监控器实例
monitor = None# 配置文件路径
CONFIG_FILE = 'config.yaml'def load_config():"""加载配置文件"""try:if os.path.exists(CONFIG_FILE):with open(CONFIG_FILE, 'r', encoding='utf-8') as f:return yaml.safe_load(f)else:# 默认配置return {'monitoring': {'file_integrity': {'enabled': True,'scan_interval': 300,'monitored_directories': ['C:\\Windows\\System32','C:\\Program Files']},'network': {'enabled': True,'scan_interval': 60,'max_connections': 1000},'process': {'enabled': True,'scan_interval': 30}},'web': {'host': '0.0.0.0','port': 5000,'debug': False}}except Exception as e:logger.error(f"加载配置错误: {e}")return {}@app.route('/')
def index():"""主页"""return render_template('index.html')@app.route('/api/stats')
def get_stats():"""获取统计信息"""try:conn = get_db_connection()cursor = conn.cursor()# 获取网络连接统计cursor.execute('SELECT COUNT(*) as total FROM network_connections')network_total = cursor.fetchone()['total']cursor.execute('SELECT COUNT(*) as tcp FROM network_connections WHERE protocol = 1')tcp_count = cursor.fetchone()['tcp']cursor.execute('SELECT COUNT(*) as udp FROM network_connections WHERE protocol = 2')udp_count = cursor.fetchone()['udp']# 获取文件完整性统计cursor.execute('SELECT COUNT(*) as total FROM file_integrity')file_total = cursor.fetchone()['total']cursor.execute('SELECT COUNT(*) as changed FROM file_integrity WHERE status = "changed"')file_changed = cursor.fetchone()['changed']# 获取报警统计cursor.execute('SELECT COUNT(*) as total FROM alerts')alert_total = cursor.fetchone()['total']cursor.execute('SELECT COUNT(*) as unresolved FROM alerts WHERE resolved = 0')alert_unresolved = cursor.fetchone()['unresolved']# 获取进程统计(从最近的监控数据)cursor.execute('SELECT COUNT(*) as total FROM alerts WHERE alert_type = "process"')process_alerts = cursor.fetchone()['total']conn.close()return jsonify({'network_stats': {'total': network_total,'tcp_count': tcp_count,'udp_count': udp_count,'listen_count': 0 # 可以扩展实现},'file_stats': {'total': file_total,'changed': file_changed,'normal': file_total - file_changed},'alert_stats': {'total': alert_total,'unresolved': alert_unresolved,'resolved': alert_total - alert_unresolved},'process_stats': {'alerts': process_alerts}})except Exception as e:logger.error(f"获取统计信息错误: {e}")return jsonify({'error': str(e)}), 500@app.route('/api/network')
def get_network():"""获取网络连接数据"""try:limit = request.args.get('limit', 100, type=int)protocol_filter = request.args.get('protocol', type=str)conn = get_db_connection()cursor = conn.cursor()# 构建查询条件query = '''SELECT id, local_address, local_port, remote_address, remote_port,process_name, protocol, connection_state, established_time,created_at, statusFROM network_connections WHERE 1=1'''params = []if protocol_filter:if protocol_filter.upper() == 'TCP':query += ' AND protocol = 1'elif protocol_filter.upper() == 'UDP':query += ' AND protocol = 2'query += ' ORDER BY created_at DESC LIMIT ?'params.append(limit)cursor.execute(query, params)connections = []for row in cursor.fetchall():connection_data = dict(row)# 协议显示名称映射if str(connection_data.get('protocol')) == '1':connection_data['protocol_display'] = 'TCP'elif str(connection_data.get('protocol')) == '2':connection_data['protocol_display'] = 'UDP'else:connection_data['protocol_display'] = f"协议{connection_data.get('protocol', '未知')}"# 连接状态显示映射state = connection_data.get('connection_state', 'NONE')state_display = {'ESTABLISHED': '已建立','LISTEN': '监听中','CLOSE_WAIT': '关闭等待','TIME_WAIT': '时间等待','SYN_SENT': '同步已发送','SYN_RECV': '同步已接收','FIN_WAIT1': '结束等待1','FIN_WAIT2': '结束等待2','CLOSING': '正在关闭','LAST_ACK': '最后确认','NONE': '无状态'}.get(state, state)connection_data['connection_state_display'] = state_displayconnections.append(connection_data)conn.close()# 返回标准格式,包含value和Count字段return jsonify({"value": connections,"Count": len(connections)})except Exception as e:logger.error(f"获取网络连接数据错误: {e}")return jsonify({'error': str(e)}), 500@app.route('/api/files')
def get_files():"""获取文件完整性数据"""try:limit = request.args.get('limit', 100, type=int)status_filter = request.args.get('status', type=str)conn = get_db_connection()cursor = conn.cursor()query = 'SELECT * FROM file_integrity WHERE 1=1'params = []if status_filter:query += ' AND status = ?'params.append(status_filter)query += ' ORDER BY updated_at DESC LIMIT ?'params.append(limit)cursor.execute(query, params)files = [dict(row) for row in cursor.fetchall()]conn.close()return jsonify(files)except Exception as e:logger.error(f"获取文件完整性数据错误: {e}")return jsonify({'error': str(e)}), 500@app.route('/api/processes')
def get_processes():"""获取进程数据"""try:limit = request.args.get('limit', 50, type=int)conn = get_db_connection()cursor = conn.cursor()# 从报警表中获取进程相关报警cursor.execute('''SELECT * FROM alerts WHERE alert_type = 'process' ORDER BY created_at DESC LIMIT ?''', (limit,))processes = []for row in cursor.fetchall():process_data = dict(row)processes.append(process_data)conn.close()return jsonify(processes)except Exception as e:logger.error(f"获取进程数据错误: {e}")return jsonify({'error': str(e)}), 500@app.route('/api/alerts')
def get_alerts():"""获取报警数据"""try:limit = request.args.get('limit', 100, type=int)severity_filter = request.args.get('severity', type=str)resolved_filter = request.args.get('resolved', type=str)conn = get_db_connection()cursor = conn.cursor()query = 'SELECT * FROM alerts WHERE 1=1'params = []if severity_filter:query += ' AND severity = ?'params.append(severity_filter)if resolved_filter is not None:query += ' AND resolved = ?'params.append(1 if resolved_filter.lower() == 'true' else 0)query += ' ORDER BY created_at DESC LIMIT ?'params.append(limit)cursor.execute(query, params)alerts = [dict(row) for row in cursor.fetchall()]conn.close()return jsonify(alerts)except Exception as e:logger.error(f"获取报警数据错误: {e}")return jsonify({'error': str(e)}), 500@app.route('/api/monitoring_status')
def get_monitoring_status():"""获取监控状态"""try:if monitor:status = monitor.get_monitoring_status()return jsonify(status)else:return jsonify({'monitoring': False, 'threads': {}})except Exception as e:logger.error(f"获取监控状态错误: {e}")return jsonify({'error': str(e)}), 500@app.route('/api/start_monitoring', methods=['POST'])
def start_monitoring():"""启动监控"""try:if not monitor:return jsonify({'error': '监控器未初始化'}), 500if monitor.monitoring:return jsonify({'message': '监控已在运行中'})monitor.start_monitoring()return jsonify({'message': '监控启动成功'})except Exception as e:logger.error(f"启动监控错误: {e}")return jsonify({'error': str(e)}), 500@app.route('/api/stop_monitoring', methods=['POST'])
def stop_monitoring():"""停止监控"""try:if not monitor:return jsonify({'error': '监控器未初始化'}), 500if not monitor.monitoring:return jsonify({'message': '监控未运行'})monitor.stop_monitoring()return jsonify({'message': '监控停止成功'})except Exception as e:logger.error(f"停止监控错误: {e}")return jsonify({'error': str(e)}), 500@app.route('/api/resolve_alert/<int:alert_id>', methods=['POST'])
def resolve_alert(alert_id):"""标记报警为已解决"""try:conn = get_db_connection()cursor = conn.cursor()cursor.execute('UPDATE alerts SET resolved = 1 WHERE id = ?', (alert_id,))conn.commit()affected = cursor.rowcountconn.close()if affected > 0:return jsonify({'message': '报警已标记为已解决'})else:return jsonify({'error': '报警不存在'}), 404except Exception as e:logger.error(f"标记报警错误: {e}")return jsonify({'error': str(e)}), 500@app.route('/api/clear_alerts', methods=['POST'])
def clear_alerts():"""清空所有报警"""try:conn = get_db_connection()cursor = conn.cursor()cursor.execute('DELETE FROM alerts')conn.commit()affected = cursor.rowcountconn.close()return jsonify({'message': f'已清空 {affected} 条报警记录'})except Exception as e:logger.error(f"清空报警错误: {e}")return jsonify({'error': str(e)}), 500@app.route('/api/delete_alert/<int:alert_id>', methods=['DELETE'])
def delete_alert(alert_id):"""删除报警"""try:conn = get_db_connection()cursor = conn.cursor()cursor.execute('DELETE FROM alerts WHERE id = ?', (alert_id,))conn.commit()affected = cursor.rowcountconn.close()if affected > 0:return jsonify({'message': '报警已删除'})else:return jsonify({'error': '报警不存在'}), 404except Exception as e:logger.error(f"删除报警错误: {e}")return jsonify({'error': str(e)}), 500@app.errorhandler(404)
def not_found(error):"""404错误处理"""if request.path.startswith('/api/'):return jsonify({'error': '接口不存在'}), 404return render_template('404.html'), 404@app.errorhandler(500)
def internal_error(error):"""500错误处理"""logger.error(f"服务器内部错误: {error}")if request.path.startswith('/api/'):return jsonify({'error': '服务器内部错误'}), 500return render_template('500.html'), 500def create_app():"""应用工厂函数"""global monitor# 初始化数据库init_database()# 加载配置config = load_config()# 创建监控器实例monitor_config = config.get('monitoring', {})monitor = HIDSMonitor(monitor_config)logger.info("HIDS应用初始化完成")return appif __name__ == '__main__':# 创建应用app = create_app()# 获取Web配置config = load_config()web_config = config.get('web', {})# 启动应用app.run(host=web_config.get('host', '0.0.0.0'),port=web_config.get('port', 5000),debug=web_config.get('debug', False),threaded=True)
前端界面开发
HTML模板设计
创建templates/index.html文件:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>HIDS - 主机入侵检测系统</title><!-- Bootstrap CSS --><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"><!-- Font Awesome --><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"><!-- Charts.js --><script src="https://cdn.jsdelivr.net/npm/chart.js"></script><!-- 自定义CSS --><link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
</head>
<body><!-- 导航栏 --><nav class="navbar navbar-expand-lg navbar-dark bg-dark"><div class="container-fluid"><a class="navbar-brand" href="#"><i class="fas fa-shield-alt"></i> HIDS 主机入侵检测系统</a><button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarNav"><ul class="navbar-nav ms-auto"><li class="nav-item"><a class="nav-link" href="#dashboard">仪表板</a></li><li class="nav-item"><a class="nav-link" href="#network">网络监控</a></li><li class="nav-item"><a class="nav-link" href="#files">文件监控</a></li><li class="nav-item"><a class="nav-link" href="#alerts">报警管理</a></li></ul></div></div></nav><div class="container-fluid mt-3"><!-- 监控控制面板 --><div class="row mb-4"><div class="col-12"><div class="card"><div class="card-header bg-primary text-white"><i class="fas fa-cogs"></i> 监控控制</div><div class="card-body"><div class="row"><div class="col-md-6"><div class="d-flex align-items-center"><span class="me-3">监控状态:</span><span id="monitoringStatus" class="badge bg-secondary">检测中...</span></div></div><div class="col-md-6 text-end"><button id="startMonitoring" class="btn btn-success btn-sm me-2"><i class="fas fa-play"></i> 启动监控</button><button id="stopMonitoring" class="btn btn-danger btn-sm me-2"><i class="fas fa-stop"></i> 停止监控</button><button id="refreshData" class="btn btn-primary btn-sm"><i class="fas fa-sync-alt"></i> 刷新数据</button></div></div></div></div></div></div><!-- 统计概览 --><div class="row mb-4" id="dashboard"><div class="col-md-3"><div class="card text-white bg-info"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="card-title">网络连接</h6><h3 id="totalConnections">0</h3></div><div class="align-self-center"><i class="fas fa-network-wired fa-2x"></i></div></div></div></div></div><div class="col-md-3"><div class="card text-white bg-warning"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="card-title">文件变化</h6><h3 id="fileChanges">0</h3></div><div class="align-self-center"><i class="fas fa-file-alt fa-2x"></i></div></div></div></div></div><div class="col-md-3"><div class="card text-white bg-success"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="card-title">进程警报</h6><h3 id="processAlerts">0</h3></div><div class="align-self-center"><i class="fas fa-tasks fa-2x"></i></div></div></div></div></div><div class="col-md-3"><div class="card text-white bg-danger"><div class="card-body"><div class="d-flex justify-content-between"><div><h6 class="card-title">未解决报警</h6><h3 id="unresolvedAlerts">0</h3></div><div class="align-self-center"><i class="fas fa-exclamation-triangle fa-2x"></i></div></div></div></div></div></div><!-- 图表区域 --><div class="row mb-4"><div class="col-md-4"><div class="card"><div class="card-header"><i class="fas fa-chart-pie"></i> 网络连接分布</div><div class="card-body"><canvas id="networkChart" width="200" height="200"></canvas></div></div></div><div class="col-md-4"><div class="card"><div class="card-header"><i class="fas fa-chart-bar"></i> 文件状态分布</div><div class="card-body"><canvas id="fileChart" width="200" height="200"></canvas></div></div></div><div class="col-md-4"><div class="card"><div class="card-header"><i class="fas fa-chart-line"></i> 报警趋势</div><div class="card-body"><canvas id="alertChart" width="200" height="200"></canvas></div></div></div></div><!-- 网络连接监控 --><div class="row mb-4" id="network"><div class="col-12"><div class="card"><div class="card-header"><i class="fas fa-network-wired"></i> 网络连接监控<div class="float-end"><select id="protocolFilter" class="form-select form-select-sm d-inline-block w-auto"><option value="">所有协议</option><option value="TCP">TCP</option><option value="UDP">UDP</option></select></div></div><div class="card-body"><div class="table-responsive"><table class="table table-striped table-hover"><thead class="table-dark"><tr><th>协议</th><th>本地地址</th><th>远程地址</th><th>连接状态</th><th>进程</th><th>建立时间</th><th>状态</th></tr></thead><tbody id="networkListBody"><tr><td colspan="7" class="text-center"><div class="spinner-border" role="status"><span class="visually-hidden">加载中...</span></div></td></tr></tbody></table></div></div></div></div></div><!-- 文件完整性监控 --><div class="row mb-4" id="files"><div class="col-12"><div class="card"><div class="card-header"><i class="fas fa-file-alt"></i> 文件完整性监控<div class="float-end"><select id="fileStatusFilter" class="form-select form-select-sm d-inline-block w-auto"><option value="">所有状态</option><option value="normal">正常</option><option value="changed">已变化</option></select></div></div><div class="card-body"><div class="table-responsive"><table class="table table-striped table-hover"><thead class="table-dark"><tr><th>文件路径</th><th>文件大小</th><th>哈希值</th><th>创建时间</th><th>更新时间</th><th>状态</th></tr></thead><tbody id="fileListBody"><tr><td colspan="6" class="text-center"><div class="spinner-border" role="status"><span class="visually-hidden">加载中...</span></div></td></tr></tbody></table></div></div></div></div></div><!-- 报警管理 --><div class="row mb-4" id="alerts"><div class="col-12"><div class="card"><div class="card-header"><i class="fas fa-exclamation-triangle"></i> 报警管理<div class="float-end"><select id="severityFilter" class="form-select form-select-sm d-inline-block w-auto me-2"><option value="">所有级别</option><option value="HIGH">高危</option><option value="MEDIUM">中危</option><option value="LOW">低危</option></select><select id="resolvedFilter" class="form-select form-select-sm d-inline-block w-auto me-2"><option value="">所有状态</option><option value="false">未解决</option><option value="true">已解决</option></select><button id="clearAlerts" class="btn btn-danger btn-sm"><i class="fas fa-trash"></i> 清空报警</button></div></div><div class="card-body"><div class="table-responsive"><table class="table table-striped table-hover"><thead class="table-dark"><tr><th>级别</th><th>类型</th><th>标题</th><th>描述</th><th>源IP</th><th>源进程</th><th>目标文件</th><th>创建时间</th><th>状态</th><th>操作</th></tr></thead><tbody id="alertListBody"><tr><td colspan="10" class="text-center"><div class="spinner-border" role="status"><span class="visually-hidden">加载中...</span></div></td></tr></tbody></table></div></div></div></div></div></div><!-- Bootstrap JS --><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script><!-- 自定义JavaScript --><script src="{{ url_for('static', filename='js/app.js') }}"></script>
</body>
</html>
JavaScript交互逻辑
创建static/js/app.js文件:
// HIDS前端主逻辑
class HIDSDashboard {constructor() {this.refreshIntervals = {};this.charts = {};this.init();}init() {this.bindEvents();this.loadInitialData();this.startAutoRefresh();}bindEvents() {// 监控控制按钮document.getElementById('startMonitoring').addEventListener('click', () => this.startMonitoring());document.getElementById('stopMonitoring').addEventListener('click', () => this.stopMonitoring());document.getElementById('refreshData').addEventListener('click', () => this.refreshAllData());// 过滤器document.getElementById('protocolFilter').addEventListener('change', () => this.loadNetwork());document.getElementById('fileStatusFilter').addEventListener('change', () => this.loadFiles());document.getElementById('severityFilter').addEventListener('change', () => this.loadAlerts());document.getElementById('resolvedFilter').addEventListener('change', () => this.loadAlerts());// 报警管理document.getElementById('clearAlerts').addEventListener('click', () => this.clearAlerts());}async loadInitialData() {try {await this.checkMonitoringStatus();await this.loadStats();await this.loadNetwork();await this.loadFiles();await this.loadAlerts();this.initCharts();} catch (error) {console.error('初始化数据加载失败:', error);this.showAlert('数据加载失败', 'error');}}startAutoRefresh() {// 设置定时刷新this.refreshIntervals.stats = setInterval(() => this.loadStats(), 30000);this.refreshIntervals.network = setInterval(() => this.loadNetwork(), 5000);this.refreshIntervals.files = setInterval(() => this.loadFiles(), 60000);this.refreshIntervals.alerts = setInterval(() => this.loadAlerts(), 15000);this.refreshIntervals.monitoring = setInterval(() => this.checkMonitoringStatus(), 10000);}async checkMonitoringStatus() {try {const response = await fetch('/api/monitoring_status');const data = await response.json();const statusElement = document.getElementById('monitoringStatus');if (data.monitoring) {statusElement.className = 'badge bg-success';statusElement.textContent = '监控运行中';// 更新按钮状态document.getElementById('startMonitoring').disabled = true;document.getElementById('stopMonitoring').disabled = false;} else {statusElement.className = 'badge bg-danger';statusElement.textContent = '监控已停止';// 更新按钮状态document.getElementById('startMonitoring').disabled = false;document.getElementById('stopMonitoring').disabled = true;}} catch (error) {console.error('检查监控状态失败:', error);const statusElement = document.getElementById('monitoringStatus');statusElement.className = 'badge bg-secondary';statusElement.textContent = '状态未知';}}async startMonitoring() {try {const response = await fetch('/api/start_monitoring', {method: 'POST'});const data = await response.json();if (response.ok) {this.showAlert('监控启动成功', 'success');await this.checkMonitoringStatus();} else {this.showAlert(data.error || '启动监控失败', 'error');}} catch (error) {console.error('启动监控失败:', error);this.showAlert('启动监控失败', 'error');}}async stopMonitoring() {try {const response = await fetch('/api/stop_monitoring', {method: 'POST'});const data = await response.json();if (response.ok) {this.showAlert('监控停止成功', 'success');await this.checkMonitoringStatus();} else {this.showAlert(data.error || '停止监控失败', 'error');}} catch (error) {console.error('停止监控失败:', error);this.showAlert('停止监控失败', 'error');}}async loadStats() {try {const response = await fetch('/api/stats');const data = await response.json();// 更新统计数字document.getElementById('totalConnections').textContent = data.network_stats?.total || 0;document.getElementById('fileChanges').textContent = data.file_stats?.changed || 0;document.getElementById('processAlerts').textContent = data.process_stats?.alerts || 0;document.getElementById('unresolvedAlerts').textContent = data.alert_stats?.unresolved || 0;// 更新图表this.updateCharts(data);} catch (error) {console.error('加载统计数据失败:', error);}}async loadNetwork() {try {const protocolFilter = document.getElementById('protocolFilter').value;const params = new URLSearchParams({limit: 50});if (protocolFilter) {params.append('protocol', protocolFilter);}const response = await fetch(`/api/network?${params}`);const data = await response.json();const tbody = document.getElementById('networkListBody');if (data.error) {tbody.innerHTML = `<tr><td colspan="7" class="text-center text-danger">${data.error}</td></tr>`;return;}const connections = data.value || data;if (connections.length === 0) {tbody.innerHTML = '<tr><td colspan="7" class="text-center text-muted">暂无网络连接数据</td></tr>';return;}tbody.innerHTML = connections.map(conn => `<tr><td><span class="badge bg-${conn.protocol_display === 'TCP' ? 'primary' : 'success'}">${conn.protocol_display}</span></td><td>${conn.local_address}:${conn.local_port}</td><td>${conn.remote_address}:${conn.remote_port}</td><td>${conn.connection_state_display}</td><td>${conn.process_name || 'N/A'}</td><td>${this.formatDateTime(conn.established_time)}</td><td><span class="badge bg-${this.getConnectionStatusClass(conn.status)}">${this.getConnectionStatusText(conn.status)}</span></td></tr>`).join('');} catch (error) {console.error('加载网络连接数据失败:', error);document.getElementById('networkListBody').innerHTML = '<tr><td colspan="7" class="text-center text-danger">数据加载失败</td></tr>';}}async loadFiles() {try {const statusFilter = document.getElementById('fileStatusFilter').value;const params = new URLSearchParams({limit: 50});if (statusFilter) {params.append('status', statusFilter);}const response = await fetch(`/api/files?${params}`);const files = await response.json();const tbody = document.getElementById('fileListBody');if (files.error) {tbody.innerHTML = `<tr><td colspan="6" class="text-center text-danger">${files.error}</td></tr>`;return;}if (files.length === 0) {tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted">暂无文件数据</td></tr>';return;}tbody.innerHTML = files.map(file => `<tr><td><small>${file.file_path}</small></td><td>${this.formatFileSize(file.file_size)}</td><td><small>${file.file_hash.substring(0, 16)}...</small></td><td>${this.formatDateTime(file.created_at)}</td><td>${this.formatDateTime(file.updated_at)}</td><td><span class="badge bg-${file.status === 'changed' ? 'warning' : 'success'}">${file.status === 'changed' ? '已变化' : '正常'}</span></td></tr>`).join('');} catch (error) {console.error('加载文件数据失败:', error);document.getElementById('fileListBody').innerHTML = '<tr><td colspan="6" class="text-center text-danger">数据加载失败</td></tr>';}}async loadAlerts() {try {const severityFilter = document.getElementById('severityFilter').value;const resolvedFilter = document.getElementById('resolvedFilter').value;const params = new URLSearchParams({limit: 100});if (severityFilter) {params.append('severity', severityFilter);}if (resolvedFilter !== '') {params.append('resolved', resolvedFilter);}const response = await fetch(`/api/alerts?${params}`);const alerts = await response.json();const tbody = document.getElementById('alertListBody');if (alerts.error) {tbody.innerHTML = `<tr><td colspan="10" class="text-center text-danger">${alerts.error}</td></tr>`;return;}if (alerts.length === 0) {tbody.innerHTML = '<tr><td colspan="10" class="text-center text-muted">暂无报警数据</td></tr>';return;}tbody.innerHTML = alerts.map(alert => `<tr><td><span class="badge bg-${this.getSeverityClass(alert.severity)}">${alert.severity}</span></td><td>${alert.alert_type}</td><td>${alert.title}</td><td><small>${alert.description}</small></td><td>${alert.source_ip || 'N/A'}</td><td>${alert.source_process || 'N/A'}</td><td><small>${alert.target_file || 'N/A'}</small></td><td>${this.formatDateTime(alert.created_at)}</td><td><span class="badge bg-${alert.resolved ? 'success' : 'warning'}">${alert.resolved ? '已解决' : '未解决'}</span></td><td>${!alert.resolved ? `<button class="btn btn-success btn-sm" onclick="hids.resolveAlert(${alert.id})">解决</button>` : '<span class="text-success">✓</span>'}<button class="btn btn-danger btn-sm" onclick="hids.deleteAlert(${alert.id})">删除</button></td></tr>`).join('');} catch (error) {console.error('加载报警数据失败:', error);document.getElementById('alertListBody').innerHTML = '<tr><td colspan="10" class="text-center text-danger">数据加载失败</td></tr>';}}initCharts() {// 网络连接分布图const networkCtx = document.getElementById('networkChart').getContext('2d');this.charts.network = new Chart(networkCtx, {type: 'doughnut',data: {labels: ['TCP连接', 'UDP连接', '监听端口'],datasets: [{data: [0, 0, 0],backgroundColor: ['#4e73df', '#1cc88a', '#36b9cc']}]},options: {responsive: true,maintainAspectRatio: false,plugins: {legend: {position: 'bottom'}}}});// 文件状态分布图const fileCtx = document.getElementById('fileChart').getContext('2d');this.charts.file = new Chart(fileCtx, {type: 'doughnut',data: {labels: ['正常文件', '变化文件'],datasets: [{data: [0, 0],backgroundColor: ['#28a745', '#ffc107']}]},options: {responsive: true,maintainAspectRatio: false,plugins: {legend: {position: 'bottom'}}}});// 报警趋势图const alertCtx = document.getElementById('alertChart').getContext('2d');this.charts.alert = new Chart(alertCtx, {type: 'line',data: {labels: [],datasets: [{label: '报警数量',data: [],borderColor: '#dc3545',backgroundColor: 'rgba(220, 53, 69, 0.1)',tension: 0.4}]},options: {responsive: true,maintainAspectRatio: false,scales: {y: {beginAtZero: true}}}});}updateCharts(data) {// 更新网络连接图表if (this.charts.network && data.network_stats) {this.charts.network.data.datasets[0].data = [data.network_stats.tcp_count || 0,data.network_stats.udp_count || 0,data.network_stats.listen_count || 0];this.charts.network.update();}// 更新文件状态图表if (this.charts.file && data.file_stats) {this.charts.file.data.datasets[0].data = [(data.file_stats.total - data.file_stats.changed) || 0,data.file_stats.changed || 0];this.charts.file.update();}}async resolveAlert(alertId) {try {const response = await fetch(`/api/resolve_alert/${alertId}`, {method: 'POST'});const data = await response.json();if (response.ok) {this.showAlert('报警已标记为已解决', 'success');this.loadAlerts();this.loadStats();} else {this.showAlert(data.error || '操作失败', 'error');}} catch (error) {console.error('解决报警失败:', error);this.showAlert('操作失败', 'error');}}async deleteAlert(alertId) {if (!confirm('确定要删除这条报警吗?')) {return;}try {const response = await fetch(`/api/delete_alert/${alertId}`, {method: 'DELETE'});const data = await response.json();if (response.ok) {this.showAlert('报警已删除', 'success');this.loadAlerts();this.loadStats();} else {this.showAlert(data.error || '删除失败', 'error');}} catch (error) {console.error('删除报警失败:', error);this.showAlert('删除失败', 'error');}}async clearAlerts() {if (!confirm('确定要清空所有报警吗?此操作不可恢复。')) {return;}try {const response = await fetch('/api/clear_alerts', {method: 'POST'});const data = await response.json();if (response.ok) {this.showAlert(data.message, 'success');this.loadAlerts();this.loadStats();} else {this.showAlert(data.error || '清空失败', 'error');}} catch (error) {console.error('清空报警失败:', error);this.showAlert('清空失败', 'error');}}refreshAllData() {this.loadStats();this.loadNetwork();this.loadFiles();this.loadAlerts();this.checkMonitoringStatus();}// 工具函数formatDateTime(dateTime) {if (!dateTime) return 'N/A';return new Date(dateTime).toLocaleString('zh-CN');}formatFileSize(bytes) {if (bytes === 0) return '0 B';const k = 1024;const sizes = ['B', 'KB', 'MB', 'GB'];const i = Math.floor(Math.log(bytes) / Math.log(k));return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];}getSeverityClass(severity) {const classMap = {'HIGH': 'danger','MEDIUM': 'warning','LOW': 'info'};return classMap[severity] || 'secondary';}getConnectionStatusClass(status) {const classMap = {'normal': 'success','warning': 'warning','danger': 'danger'};return classMap[status] || 'secondary';}getConnectionStatusText(status) {const textMap = {'normal': '正常','warning': '警告','danger': '危险'};return textMap[status] || '未知';}showAlert(message, type = 'info') {// 创建临时提示const alertDiv = document.createElement('div');alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed`;alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';alertDiv.innerHTML = `${message}<button type="button" class="btn-close" data-bs-dismiss="alert"></button>`;document.body.appendChild(alertDiv);// 3秒后自动消失setTimeout(() => {if (alertDiv.parentNode) {alertDiv.parentNode.removeChild(alertDiv);}
```css
/* static/css/style.css *//* 全局样式 */
body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;background-color: #f8f9fa;
}/* 导航栏样式 */
.navbar-brand {font-weight: bold;font-size: 1.2rem;
}/* 卡片样式 */
.card {border: none;box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15);border-radius: 0.35rem;
}.card-header {font-weight: 600;border-bottom: 1px solid #e3e6f0;
}/* 统计卡片 */
.card.text-white {transition: transform 0.2s;
}.card.text-white:hover {transform: translateY(-2px);
}/* 表格样式 */
.table {font-size: 0.9rem;
}.table-dark {background-color: #5a5c69;
}.table-hover tbody tr:hover {background-color: rgba(0, 0, 0, 0.02);
}/* 按钮样式 */
.btn {border-radius: 0.35rem;font-weight: 500;
}.btn-sm {padding: 0.25rem 0.5rem;font-size: 0.8rem;
}/* 徽章样式 */
.badge {font-weight: 500;padding: 0.375rem 0.75rem;
}/* 图表容器 */
.chart-container {position: relative;height: 300px;
}/* 响应式设计 */
@media (max-width: 768px) {.table-responsive {font-size: 0.8rem;}.card-header .float-end {float: none !important;margin-top: 0.5rem;}
}/* 加载动画 */
.spinner-border {width: 1rem;height: 1rem;
}/* 状态指示器 */
.status-indicator {display: inline-block;width: 0.5rem;height: 0.5rem;border-radius: 50%;margin-right: 0.25rem;
}.status-indicator.online {background-color: #28a745;
}.status-indicator.offline {background-color: #dc3545;
}/* 自定义滚动条 */
.table-responsive::-webkit-scrollbar {height: 0.5rem;
}.table-responsive::-webkit-scrollbar-track {background: #f1f1f1;
}.table-responsive::-webkit-scrollbar-thumb {background: #888;border-radius: 0.25rem;
}.table-responsive::-webkit-scrollbar-thumb:hover {background: #555;
}/* 动画效果 */
.fade-in {animation: fadeIn 0.5s ease-in;
}@keyframes fadeIn {from { opacity: 0; transform: translateY(10px); }to { opacity: 1; transform: translateY(0); }
}/* 工具提示 */
.tooltip {font-size: 0.8rem;
}/* 表单样式 */
.form-select-sm {font-size: 0.8rem;
}/* 警告框 */
.alert {border: none;border-radius: 0.35rem;
}.alert-fixed {position: fixed;top: 20px;right: 20px;z-index: 9999;min-width: 300px;
}
系统部署与运行
环境准备
-
Python环境要求
- Python 3.7 或更高版本
- pip 包管理器
- 虚拟环境(推荐)
-
系统依赖
- Windows 10/11 或 Linux 系统
- 至少 2GB 可用内存
- 100MB 可用磁盘空间
安装步骤
- 克隆项目代码
git clone https://github.com/yourusername/hids-project.git
cd hids-project
- 创建虚拟环境
# Windows
python -m venv venv
venv\Scripts\activate# Linux/Mac
python3 -m venv venv
source venv/bin/activate
- 安装依赖包
pip install -r requirements.txt
- 初始化数据库
python database.py
- 启动应用
python run.py
配置文件详解
create config.yaml:
# HIDS系统配置文件monitoring:file_integrity:enabled: truescan_interval: 300 # 文件扫描间隔(秒)monitored_directories:- 'C:\\Windows\\System32'- 'C:\\Program Files'- 'C:\\Program Files (x86)'network:enabled: truescan_interval: 60 # 网络扫描间隔(秒)max_connections: 1000 # 最大连接数限制process:enabled: truescan_interval: 30 # 进程扫描间隔(秒)web:host: '0.0.0.0' # 监听地址port: 5000 # 监听端口debug: false # 调试模式logging:level: 'INFO' # 日志级别file: 'logs/hids.log' # 日志文件路径max_size: 10MB # 日志文件最大大小backup_count: 5 # 日志备份数量
启动脚本开发
create run.py:
#!/usr/bin/env python3
"""
HIDS系统启动脚本
"""import os
import sys
import argparse
import signal
import time
from app import create_app
from database import init_database, reset_database
import logging# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)def signal_handler(signum, frame):"""信号处理函数"""logger.info(f"接收到信号 {signum},正在关闭应用...")sys.exit(0)def main():"""主函数"""parser = argparse.ArgumentParser(description='HIDS主机入侵检测系统')parser.add_argument('--host', default='0.0.0.0', help='监听地址')parser.add_argument('--port', type=int, default=5000, help='监听端口')parser.add_argument('--debug', action='store_true', help='调试模式')parser.add_argument('--reset-db', action='store_true', help='重置数据库')parser.add_argument('--init-db', action='store_true', help='初始化数据库')args = parser.parse_args()# 注册信号处理signal.signal(signal.SIGINT, signal_handler)signal.signal(signal.SIGTERM, signal_handler)try:# 确保必要的目录存在os.makedirs('data', exist_ok=True)os.makedirs('logs', exist_ok=True)os.makedirs('static/css', exist_ok=True)os.makedirs('static/js', exist_ok=True)os.makedirs('templates', exist_ok=True)# 数据库操作if args.reset_db:logger.info("正在重置数据库...")reset_database()logger.info("数据库重置完成")elif args.init_db:logger.info("正在初始化数据库...")init_database()logger.info("数据库初始化完成")else:# 自动初始化数据库(如果不存在)if not os.path.exists('data/hids.db'):logger.info("检测到新安装,正在初始化数据库...")init_database()# 创建应用app = create_app()logger.info(f"启动HIDS监控系统...")logger.info(f"Web界面地址: http://{args.host}:{args.port}")logger.info(f"调试模式: {'开启' if args.debug else '关闭'}")# 启动应用app.run(host=args.host,port=args.port,debug=args.debug,threaded=True)except KeyboardInterrupt:logger.info("应用被用户中断")except Exception as e:logger.error(f"应用启动失败: {e}")sys.exit(1)if __name__ == '__main__':main()
Docker容器化部署
create Dockerfile:
# 使用Python 3.9官方镜像
FROM python:3.9-slim# 设置工作目录
WORKDIR /app# 安装系统依赖
RUN apt-get update && apt-get install -y \gcc \&& rm -rf /var/lib/apt/lists/*# 复制依赖文件
COPY requirements.txt .# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt# 复制应用代码
COPY . .# 创建必要的目录
RUN mkdir -p data logs static/css static/js templates# 暴露端口
EXPOSE 5000# 设置环境变量
ENV PYTHONUNBUFFERED=1
ENV FLASK_APP=app.py# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \CMD curl -f http://localhost:5000/api/stats || exit 1# 启动命令
CMD ["python", "run.py", "--host", "0.0.0.0", "--port", "5000"]
create docker-compose.yml:
version: '3.8'services:hids:build: .ports:- "5000:5000"volumes:- ./data:/app/data- ./logs:/app/logs- ./config.yaml:/app/config.yamlenvironment:- PYTHONUNBUFFERED=1restart: unless-stoppedhealthcheck:test: ["CMD", "curl", "-f", "http://localhost:5000/api/stats"]interval: 30stimeout: 10sretries: 3start_period: 40s
性能优化策略
-
数据库优化
- 创建合适的索引
- 定期清理历史数据
- 使用连接池管理
-
内存优化
- 限制监控数据缓存大小
- 及时释放无用对象
- 使用生成器处理大数据
-
网络优化
- 启用数据压缩
- 使用CDN加速静态资源
- 实现请求缓存
-
监控优化
- 调整扫描频率
- 实现智能扫描策略
- 使用异步处理
功能测试与验证
API接口测试
create test_network_api.py:
#!/usr/bin/env python3
"""
HIDS API测试脚本
"""import requests
import json
from datetime import datetimedef test_network_api():"""测试网络连接API"""print("=== 网络连接API测试 ===")try:# 测试基本请求response = requests.get('http://localhost:5000/api/network?limit=20')print(f"状态码: {response.status_code}")if response.status_code == 200:data = response.json()print(f"数据类型: {type(data)}")# 检查返回格式if isinstance(data, dict) and 'value' in data:connections = data['value']print(f"总连接数: {data.get('Count', 0)}")print(f"连接数组长度: {len(connections)}")# 统计协议分布tcp_count = 0udp_count = 0udp_samples = []for conn in connections:protocol = conn.get('protocol_display', '未知')if protocol == 'TCP':tcp_count += 1elif protocol == 'UDP':udp_count += 1if len(udp_samples) < 3:udp_samples.append(conn)print(f"TCP连接: {tcp_count}")print(f"UDP连接: {udp_count}")# 显示UDP样本print("\nUDP连接样本:")for i, sample in enumerate(udp_samples, 1):print(f" {i}. ID: {sample.get('id')}, 进程: {sample.get('process_name')}, "f"本地: {sample.get('local_address')}:{sample.get('local_port')}, "f"远程: {sample.get('remote_address')}:{sample.get('remote_port')}")# 验证协议显示逻辑print(f"\n协议显示验证:")print(f"TCP样本协议值: {[conn.get('protocol') for conn in connections[:5] if conn.get('protocol_display') == 'TCP']}")print(f"UDP样本协议值: {[conn.get('protocol') for conn in connections[:5] if conn.get('protocol_display') == 'UDP']}")else:print("错误: API返回格式不正确")print(f"返回数据: {json.dumps(data, indent=2, ensure_ascii=False)}")else:print(f"API请求失败: {response.status_code}")print(f"错误信息: {response.text}")except requests.exceptions.ConnectionError:print("错误: 无法连接到HIDS服务器")print("请确保服务器正在运行: python run.py")except Exception as e:print(f"测试过程中发生错误: {e}")def test_stats_api():"""测试统计API"""print("\n=== 统计API测试 ===")try:response = requests.get('http://localhost:5000/api/stats')print(f"状态码: {response.status_code}")if response.status_code == 200:data = response.json()print("统计信息:")print(f" 网络连接总数: {data.get('network_stats', {}).get('total', 0)}")print(f" TCP连接数: {data.get('network_stats', {}).get('tcp_count', 0)}")print(f" UDP连接数: {data.get('network_stats', {}).get('udp_count', 0)}")print(f" 文件总数: {data.get('file_stats', {}).get('total', 0)}")print(f" 文件变化数: {data.get('file_stats', {}).get('changed', 0)}")print(f" 报警总数: {data.get('alert_stats', {}).get('total', 0)}")print(f" 未解决报警: {data.get('alert_stats', {}).get('unresolved', 0)}")else:print(f"统计API请求失败: {response.status_code}")except Exception as e:print(f"统计API测试失败: {e}")def test_monitoring_status():"""测试监控状态API"""print("\n=== 监控状态API测试 ===")try:response = requests.get('http://localhost:5000/api/monitoring_status')print(f"状态码: {response.status_code}")if response.status_code == 200:data = response.json()print(f"监控状态: {'运行中' if data.get('monitoring') else '已停止'}")print(f"线程状态: {data.get('threads', {})}")else:print(f"监控状态API请求失败: {response.status_code}")except Exception as e:print(f"监控状态API测试失败: {e}")if __name__ == '__main__':print("HIDS API测试工具")print("=" * 50)print(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")print()# 运行所有测试test_network_api()test_stats_api()test_monitoring_status()print("\n" + "=" * 50)print("测试完成")
功能验证测试
运行测试脚本验证系统功能:
# 启动HIDS服务器
python run.py# 在另一个终端运行测试
python test_network_api.py
预期输出:
=== 网络连接API测试 ===
状态码: 200
数据类型: <class 'dict'>
总连接数: 20
连接数组长度: 20
TCP连接: 15
UDP连接: 5UDP连接样本:1. ID: 219640, 进程: svchost.exe, 本地: 0.0.0.0:3702, 远程: 0.0.0.0:02. ID: 219641, 进程: svchost.exe, 本地: [::]:3702, 远程: [::]:03. ID: 219642, 进程: chrome.exe, 本地: 192.168.1.100:52341, 远程: 142.250.185.78:443协议显示验证:
TCP样本协议值: ['1', '1', '1', '1', '1']
UDP样本协议值: ['2', '2', '2', '2', '2']
性能优化与扩展
性能监控指标
-
响应时间监控
- API接口响应时间 < 500ms
- 页面加载时间 < 2秒
- 数据库查询时间 < 100ms
-
资源使用监控
- CPU使用率 < 80%
- 内存使用率 < 70%
- 磁盘I/O < 50%
-
监控频率优化
- 网络连接:60秒
- 文件完整性:300秒
- 进程监控:30秒
扩展功能规划
-
机器学习集成
- 异常行为检测
- 威胁情报关联
- 预测性分析
-
高级报警机制
- 邮件通知
- 短信告警
- WebHook集成
-
可视化增强
- 实时图表
- 地理分布图
- 时间序列分析
-
多平台支持
- Linux系统适配
- macOS系统适配
- 移动端监控
安全考虑
安全最佳实践
-
认证授权
- 实现用户认证系统
- 基于角色的访问控制
- API访问令牌
-
数据安全
- 敏感数据加密
- 数据库连接加密
- 日志脱敏处理
-
系统安全
- 最小权限原则
- 输入验证
- SQL注入防护
-
网络安全
- HTTPS通信
- CORS配置
- 请求频率限制
合规性考虑
-
数据保护
- GDPR合规
- 数据本地化
- 隐私保护
-
审计要求
- 操作日志
- 访问记录
- 变更追踪
项目总结与展望
项目成果
通过本文的详细介绍,我们成功构建了一个功能完整的HIDS主机入侵检测系统,主要成果包括:
-
核心功能实现
- 网络连接实时监控
- 文件完整性检测
- 进程行为监控
- 智能报警系统
-
技术架构优势
- 模块化设计
- 可扩展架构
- 高性能实现
- 用户友好界面
-
开发实践价值
- 完整的开发流程
- 最佳实践应用
- 测试验证充分
- 文档齐全
技术收获
-
后端开发技能
- Flask框架深度应用
- 数据库设计与优化
- 多线程编程
- RESTful API设计
-
前端开发技能
- 现代JavaScript开发
- Bootstrap响应式设计
- Charts.js数据可视化
- Ajax异步交互
-
系统监控技术
- psutil库应用
- 系统资源监控
- 安全检测算法
- 日志管理
未来发展方向
-
智能化升级
- 集成机器学习算法
- 实现自适应检测
- 威胁情报关联
-
云原生适配
- 容器化部署
- 微服务架构
- 云监控集成
-
企业级功能
- 多主机管理
- 集中式控制台
- 高级报表功能
-
社区生态
- 开源社区建设
- 插件系统开发
- 用户贡献机制
学习建议
对于想要深入学习HIDS系统开发的读者,建议:
-
理论基础
- 学习网络安全基础知识
- 了解入侵检测原理
- 掌握操作系统原理
-
实践项目
- 从简单功能开始
- 逐步增加复杂度
- 注重测试验证
-
技术栈扩展
- 学习其他Web框架
- 掌握多种数据库
- 了解云平台服务
-
社区参与
- 参与开源项目
- 分享技术经验
- 持续学习更新
结语
HIDS主机入侵检测系统的开发是一个综合性很强的项目,涉及网络安全、系统编程、Web开发、数据库设计等多个技术领域。通过本文的详细讲解,相信读者已经掌握了构建此类系统的核心技术和方法。
网络安全是一个持续演进的领域,新的威胁和攻击手段不断涌现。作为安全从业者,我们需要保持学习的热情,不断更新知识体系,提升技术能力。希望这个项目能够成为你技术成长道路上的一个有益实践。