引言

网络安全是数字化时代的核心挑战。随着攻击手段的不断演进,了解常见漏洞、攻击方法和防御策略变得至关重要。本文将深入分析网络安全领域的核心主题,帮助读者构建更安全的系统。

一、OWASP Top 10深度解析

1.1 注入漏洞

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# ========== SQL注入 ==========

# 不安全的代码示例
def get_user_by_id(user_id):
    """危险:直接拼接SQL"""
    query = f"SELECT * FROM users WHERE id = {user_id}"
    return db.execute(query)

# 攻击示例
# user_id = "1 OR 1=1"
# 实际执行的SQL: SELECT * FROM users WHERE id = 1 OR 1=1
# 结果:返回所有用户数据

# 安全的代码示例
import sqlite3
from typing import Optional

class UserRepository:
    """安全的用户仓库"""

    def __init__(self, db_path: str):
        self.conn = sqlite3.connect(db_path)

    def get_user_by_id(self, user_id: int) -> Optional[dict]:
        """使用参数化查询"""
        cursor = self.conn.cursor()

        # 使用?占位符,数据库驱动会正确转义参数
        query = "SELECT * FROM users WHERE id = ?"
        cursor.execute(query, (user_id,))

        row = cursor.fetchone()
        if row:
            return {
                'id': row[0],
                'username': row[1],
                'email': row[2]
            }
        return None

    def search_users(self, keyword: str) -> list:
        """安全的搜索功能"""
        cursor = self.conn.cursor()

        query = """
            SELECT id, username, email
            FROM users
            WHERE username LIKE ?
            OR email LIKE ?
        """
        # 使用通配符进行模糊搜索
        pattern = f"%{keyword}%"
        cursor.execute(query, (pattern, pattern))

        return cursor.fetchall()

# ========== SQL注入检测与防御 ==========

class SQLInjectionDetector:
    """SQL注入检测器"""

    # 常见SQL注入特征
    INJECTION_PATTERNS = [
        r"(\%27)|(\')",                    # 单引号
        r"(\-\-)|(#)",                     # 注释符
        r"(\bor\b|\band\b).*?=.*?",        # 逻辑运算符
        r"(\bunion\b.*\bselect\b)",       # UNION查询
        r"(\bselect\b.*\bfrom\b)",         # SELECT查询
        r"(\bexec\b|\bexecute\b)",         # 执行命令
        r"(;|\bxp_cmdshell\b)",           # 命令分隔符和存储过程
        r"(\bdrop\b|\bdelete\b|\btruncate\b)", # 危险操作
    ]

    @classmethod
    def detect_injection(cls, input_string: str) -> bool:
        """检测SQL注入"""
        import re

        for pattern in cls.INJECTION_PATTERNS:
            if re.search(pattern, input_string, re.IGNORECASE):
                return True

        return False

    @classmethod
    def sanitize_input(cls, input_string: str) -> str:
        """清理输入"""
        import re

        # 移除危险字符
        sanitized = re.sub(r"[\'\"\;\-\-]", "", input_string)

        # 限制长度
        max_length = 100
        sanitized = sanitized[:max_length]

        return sanitized

# ========== ORM安全使用 ==========

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class User(Base):
    """用户模型"""
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    username = Column(String(50), nullable=False)
    email = Column(String(100), nullable=False)

class SecureUserRepository:
    """使用ORM的安全仓库"""

    def __init__(self, database_url: str):
        self.engine = create_engine(database_url)
        Session = sessionmaker(bind=self.engine)
        self.session = Session()

    def get_user_by_id(self, user_id: int) -> Optional[User]:
        """ORM自动处理参数化"""
        return self.session.query(User).filter(
            User.id == user_id
        ).first()

    def search_users(self, keyword: str) -> list:
        """安全的搜索"""
        return self.session.query(User).filter(
            (User.username.like(f"%{keyword}%")) |
            (User.email.like(f"%{keyword}%"))
        ).all()

1.2 跨站脚本攻击(XSS)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# ========== XSS攻击类型 ==========

"""
1. 反射型XSS (Reflected XSS)
   - 恶意代码通过URL参数反射
   - 例如: http://example.com/search?q=<script>alert('XSS')</script>

2. 存储型XSS (Stored XSS)
   - 恶意代码存储在服务器,每次访问时执行
   - 例如: 评论、用户资料等可存储的内容

3. DOM型XSS (DOM-based XSS)
   - 恶意代码通过DOM操作执行
   - 例如: #<img src=x onerror=alert('XSS')>
"""

# ========== XSS防御 ==========

import html
import re
from typing import Dict, Any

class XSSProtection:
    """XSS防护"""

    @staticmethod
    def escape_html(text: str) -> str:
        """HTML转义"""
        return html.escape(text, quote=True)

    @staticmethod
    def escape_js(text: str) -> str:
        """JavaScript字符串转义"""
        escape_map = {
            '\\': '\\\\',
            '"': '\\"',
            "'": "\\'",
            '\n': '\\n',
            '\r': '\\r',
            '\t': '\\t',
            '\b': '\\b',
            '\f': '\\f',
        }

        for char, escaped in escape_map.items():
            text = text.replace(char, escaped)

        return text

    @staticmethod
    def sanitize_html(content: str, allowed_tags: list = None) -> str:
        """HTML净化,只允许安全的标签"""
        if allowed_tags is None:
            allowed_tags = ['p', 'br', 'strong', 'em', 'u', 'a']

        import bleach

        return bleach.clean(
            content,
            tags=allowed_tags,
            attributes={
                'a': ['href', 'title'],
                '*': ['class']
            },
            strip=True
        )

    @staticmethod
    def validate_url(url: str) -> bool:
        """验证URL安全性"""
        import urllib.parse

        try:
            parsed = urllib.parse.urlparse(url)

            # 检查协议
            if parsed.scheme not in ['http', 'https']:
                return False

            # 检查是否为JavaScript伪协议
            if parsed.scheme.lower() == 'javascript':
                return False

            return True

        except Exception:
            return False

# Web框架集成示例
from flask import Flask, request, render_template_string

app = Flask(__name__)

# 不安全的示例
@app.route('/unsafe')
def unsafe_search():
    query = request.args.get('q', '')
    # 危险:直接渲染用户输入
    return render_template_string(f'<p>搜索结果: {query}</p>')

# 安全的示例
@app.route('/safe')
def safe_search():
    query = request.args.get('q', '')

    # 转义用户输入
    safe_query = XSSProtection.escape_html(query)

    # 或使用模板引擎的自动转义
    return render_template_string(
        '<p>搜索结果: {{ query }}</p>',
        query=safe_query
    )

# Content Security Policy (CSP)
@app.after_request
def add_security_headers(response):
    """添加安全头"""
    csp = (
        "default-src 'self'; "
        "script-src 'self' 'unsafe-inline' https://cdn.example.com; "
        "style-src 'self' 'unsafe-inline'; "
        "img-src 'self' data: https:; "
        "font-src 'self' data:; "
        "connect-src 'self'; "
        "frame-ancestors 'none';"
    )

    response.headers['Content-Security-Policy'] = csp
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-XSS-Protection'] = '1; mode=block'

    return response

# ========== 输入验证 ==========

class InputValidator:
    """输入验证器"""

    @staticmethod
    def validate_username(username: str) -> bool:
        """验证用户名"""
        # 长度检查
        if len(username) < 3 or len(username) > 20:
            return False

        # 字符检查:只允许字母、数字、下划线
        import re
        pattern = r'^[a-zA-Z0-9_]+$'

        return bool(re.match(pattern, username))

    @staticmethod
    def validate_email(email: str) -> bool:
        """验证邮箱"""
        import re

        pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        return bool(re.match(pattern, email))

    @staticmethod
    def validate_phone(phone: str) -> bool:
        """验证手机号"""
        import re

        pattern = r'^1[3-9]\d{9}$'
        return bool(re.match(pattern, phone))

1.3 CSRF防护

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# ========== CSRF攻击原理 ==========

"""
CSRF (Cross-Site Request Forgery) 跨站请求伪造

攻击场景:
1. 用户登录了银行网站 bank.com
2. 用户访问了恶意网站 evil.com
3. evil.com 包含指向 bank.com 的请求
   <img src="http://bank.com/transfer?to=attacker&amount=10000">
4. 浏览器自动携带 bank.com 的 cookie
5. 银行网站处理转账请求

防御措施:
1. CSRF Token
2. SameSite Cookie属性
3. 验证Referer/Origin头
4. 双重提交Cookie
"""

# ========== CSRF防护实现 ==========

import secrets
from flask import Flask, request, session, render_template

app = Flask(__name__)
app.secret_key = secrets.token_hex(32)

class CSRFProtection:
    """CSRF防护"""

    @staticmethod
    def generate_token() -> str:
        """生成CSRF Token"""
        return secrets.token_hex(32)

    @staticmethod
    def validate_token(token: str, stored_token: str) -> bool:
        """验证Token"""
        return secrets.compare_digest(token, stored_token)

# 在Flask中使用CSRF保护
@app.before_request
def csrf_protect():
    """CSRF保护中间件"""

    # 豁免安全的方法
    if request.method in ['GET', 'HEAD', 'OPTIONS', 'TRACE']:
        return

    # 检查CSRF Token
    token = request.headers.get('X-CSRF-Token') or request.form.get('csrf_token')

    if not token or token != session.get('csrf_token'):
        return 'Invalid CSRF token', 403

@app.route('/form')
def show_form():
    """显示表单"""
    # 生成并存储CSRF Token
    csrf_token = CSRFProtection.generate_token()
    session['csrf_token'] = csrf_token

    return f'''
        <form method="POST" action="/submit">
            <input type="hidden" name="csrf_token" value="{csrf_token}">
            <input type="text" name="username">
            <button type="submit">提交</button>
        </form>
    '''

@app.route('/submit', methods=['POST'])
def handle_submit():
    """处理表单提交"""

    # Token已在中间件验证

    username = request.form.get('username')

    # 业务逻辑
    return f'Hello, {username}!'

# ========== SameSite Cookie ==========

@app.route('/login', methods=['POST'])
def login():
    """登录"""

    # 设置SameSite属性
    response = app.make_response('Login successful')

    # Strict: 严格模式,只在同站点请求中发送
    response.set_cookie(
        'session_id',
        value='session_data',
        httponly=True,
        secure=True,
        samesite='Strict'
    )

    return response

# ========== 双重Cookie验证 ==========

class DoubleSubmitCookie:
    """双重Cookie CSRF防护"""

    @staticmethod
    def generate_token():
        """生成Token并设置到Cookie"""
        token = secrets.token_hex(32)

        @app.after_this_request
        def add_cookie(response):
            response.set_cookie(
                'csrf_token',
                token,
                httponly=True,
                secure=True,
                samesite='Strict'
            )
            return response

        return token

    @staticmethod
    def validate(request):
        """验证Token"""
        cookie_token = request.cookies.get('csrf_token')
        request_token = request.form.get('csrf_token') or \
                       request.headers.get('X-CSRF-Token')

        return secrets.compare_digest(cookie_token, request_token)

二、其他常见Web漏洞

2.1 文件上传漏洞

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# ========== 文件上传安全 ==========

import os
import magic
from werkzeug.utils import secure_filename
from typing import Tuple

class SecureFileUpload:
    """安全的文件上传"""

    # 允许的文件类型
    ALLOWED_EXTENSIONS = {
        'image': {'jpg', 'jpeg', 'png', 'gif', 'webp'},
        'document': {'pdf', 'doc', 'docx', 'txt'},
        'media': {'mp4', 'mp3', 'wav'}
    }

    # 允许的MIME类型
    ALLOWED_MIME_TYPES = {
        'image/jpeg', 'image/png', 'image/gif', 'image/webp',
        'application/pdf',
        'video/mp4', 'audio/mpeg'
    }

    # 最大文件大小 (10MB)
    MAX_FILE_SIZE = 10 * 1024 * 1024

    def __init__(self, upload_dir: str):
        self.upload_dir = upload_dir
        os.makedirs(upload_dir, exist_ok=True)

    def validate_file(self, file) -> Tuple[bool, str]:
        """验证文件"""

        # 检查文件名
        filename = secure_filename(file.filename)

        if not filename:
            return False, "Invalid filename"

        # 检查文件扩展名
        ext = filename.rsplit('.', 1)[1].lower() if '.' in filename else ''

        if ext not in set().union(*self.ALLOWED_EXTENSIONS.values()):
            return False, f"File extension '{ext}' not allowed"

        # 检查文件大小
        file.seek(0, os.SEEK_END)
        file_size = file.tell()
        file.seek(0)

        if file_size > self.MAX_FILE_SIZE:
            return False, f"File size exceeds {self.MAX_FILE_SIZE} bytes"

        # 检查MIME类型
        file_content = file.read()
        mime_type = magic.from_buffer(file_content, mime=True)

        if mime_type not in self.ALLOWED_MIME_TYPES:
            return False, f"MIME type '{mime_type}' not allowed"

        # 验证扩展名和MIME类型是否匹配
        ext_mime_map = {
            'jpg': 'image/jpeg',
            'jpeg': 'image/jpeg',
            'png': 'image/png',
            'gif': 'image/gif',
            'pdf': 'application/pdf'
        }

        if ext in ext_mime_map and ext_mime_map[ext] != mime_type:
            return False, "File extension does not match content"

        return True, "Valid file"

    def save_file(self, file) -> Tuple[bool, str]:
        """保存文件"""

        # 验证文件
        is_valid, message = self.validate_file(file)

        if not is_valid:
            return False, message

        # 生成安全的文件名
        import uuid

        original_filename = secure_filename(file.filename)
        ext = original_filename.rsplit('.', 1)[1].lower()
        safe_filename = f"{uuid.uuid4().hex}.{ext}"

        # 保存文件
        filepath = os.path.join(self.upload_dir, safe_filename)

        file.seek(0)
        file.save(filepath)

        # 设置文件权限
        os.chmod(filepath, 0o644)

        return True, safe_filename

# Flask路由示例
from flask import Flask, request, jsonify

app = Flask(__name__)
uploader = SecureFileUpload('/var/uploads')

@app.route('/upload', methods=['POST'])
def upload_file():
    """文件上传接口"""

    if 'file' not in request.files:
        return jsonify({'error': 'No file provided'}), 400

    file = request.files['file']

    if file.filename == '':
        return jsonify({'error': 'Empty filename'}), 400

    # 保存文件
    success, message = uploader.save_file(file)

    if success:
        return jsonify({
            'success': True,
            'filename': message
        })
    else:
        return jsonify({
            'success': False,
            'error': message
        }), 400

2.2 权限绕过

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# ========== 权限控制 ==========

from functools import wraps
from flask import session, request, jsonify

class PermissionChecker:
    """权限检查器"""

    # 权限定义
    PERMISSIONS = {
        'user:read': '查看用户信息',
        'user:write': '修改用户信息',
        'user:delete': '删除用户',
        'admin:panel': '访问管理面板',
        'system:config': '修改系统配置'
    }

    # 角色权限映射
    ROLE_PERMISSIONS = {
        'guest': ['user:read'],
        'user': ['user:read', 'user:write'],
        'moderator': ['user:read', 'user:write', 'user:delete'],
        'admin': ['admin:panel', 'system:config'],
        'superadmin': ['*']  # 所有权限
    }

    @classmethod
    def has_permission(cls, role: str, required_permission: str) -> bool:
        """检查角色是否有权限"""

        if role not in cls.ROLE_PERMISSIONS:
            return False

        permissions = cls.ROLE_PERMISSIONS[role]

        # 超级管理员拥有所有权限
        if '*' in permissions:
            return True

        return required_permission in permissions

# 装饰器实现权限控制
def require_permission(permission: str):
    """权限检查装饰器"""
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            # 从session获取用户角色
            role = session.get('role', 'guest')

            # 检查权限
            if not PermissionChecker.has_permission(role, permission):
                return jsonify({
                    'error': 'Permission denied',
                    'required': permission
                }), 403

            return f(*args, **kwargs)

        return decorated_function
    return decorator

# 资源所有者检查
def require_owner_or_admin(model_name: str, id_param: str = 'id'):
    """资源所有者或管理员权限检查"""
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            user_id = session.get('user_id')
            role = session.get('role', 'guest')

            # 管理员跳过检查
            if role == 'superadmin':
                return f(*args, **kwargs)

            # 获取资源ID
            resource_id = kwargs.get(id_param) or request.view_args.get(id_param)

            # 检查资源所有者
            from database import get_db

            db = get_db()
            resource = db.query(model_name).get(resource_id)

            if resource and resource.owner_id == user_id:
                return f(*args, **kwargs)

            return jsonify({'error': 'Resource access denied'}), 403

        return decorated_function
    return decorator

# 使用示例
@app.route('/users/<int:id>', methods=['GET'])
@require_permission('user:read')
def get_user(id):
    """获取用户信息"""
    return jsonify({'user': f'User {id}'})

@app.route('/users/<int:id>', methods=['PUT'])
@require_permission('user:write')
@require_owner_or_admin('User', 'id')
def update_user(id):
    """更新用户信息"""
    return jsonify({'success': True})

@app.route('/users/<int:id>', methods=['DELETE'])
@require_permission('user:delete')
def delete_user(id):
    """删除用户"""
    return jsonify({'success': True})

# ========== 水平越权防护 ==========

class AccessControl:
    """访问控制"""

    @staticmethod
    def check_resource_access(user_id: int, resource_id: int, resource_type: str) -> bool:
        """检查资源访问权限"""

        from database import get_db

        db = get_db()

        # 检查资源是否存在
        resource = db.query(resource_type).get(resource_id)

        if not resource:
            return False

        # 检查资源所有者
        if resource.owner_id == user_id:
            return True

        # 检查共享权限
        if hasattr(resource, 'shared_with'):
            if user_id in resource.shared_with:
                return True

        return False

    @staticmethod
    def check_id_or_id_list(user_input, max_length: int = 1000) -> bool:
        """检查ID参数,防止ID遍历攻击"""

        # 单个ID
        if isinstance(user_input, int):
            return 0 < user_input < max_length

        # ID列表
        if isinstance(user_input, list):
            if len(user_input) > 100:  # 限制列表长度
                return False

            return all(
                isinstance(id, int) and 0 < id < max_length
                for id in user_input
            )

        return False

三、渗透测试与漏洞挖掘

3.1 信息收集

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# ========== 信息收集工具 ==========

import requests
import dns.resolver
import socket
from typing import List, Dict, Set
from urllib.parse import urlparse
import re

class InformationGathering:
    """信息收集"""

    def __init__(self, target: str):
        self.target = target
        self.results = {
            'subdomains': set(),
            'open_ports': [],
            'technologies': [],
            'sensitive_files': [],
            'emails': set(),
            'social_accounts': set()
        }

    def enumerate_subdomains(self) -> Set[str]:
        """子域名枚举"""

        wordlist = [
            'www', 'mail', 'ftp', 'admin', 'blog', 'api',
            'dev', 'staging', 'test', 'app', 'mobile'
        ]

        for subdomain in wordlist:
            domain = f"{subdomain}.{self.target}"

            try:
                # DNS查询
                dns.resolver.resolve(domain, 'A')
                self.results['subdomains'].add(domain)

            except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
                continue

        return self.results['subdomains']

    def scan_ports(self, ports: List[int] = None) -> List[int]:
        """端口扫描"""

        if ports is None:
            ports = [21, 22, 23, 25, 53, 80, 110, 443, 445, 3306, 3389, 8080]

        open_ports = []

        for port in ports:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(1)

            try:
                sock.connect((self.target, port))
                open_ports.append(port)

            except socket.error:
                pass

            finally:
                sock.close()

        self.results['open_ports'] = open_ports
        return open_ports

    def identify_technologies(self) -> List[str]:
        """技术栈识别"""

        url = f"http://{self.target}"

        try:
            response = requests.get(url, timeout=10)

            # 检查响应头
            headers = response.headers

            technologies = []

            # Server头
            if 'Server' in headers:
                technologies.append(f"Server: {headers['Server']}")

            # X-Powered-By头
            if 'X-Powered-By' in headers:
                technologies.append(f"PoweredBy: {headers['X-Powered-By']}")

            # 检查HTML中的技术特征
            content = response.text

            # WordPress
            if 'wp-content' in content:
                technologies.append("WordPress")

            # jQuery
            if 'jquery' in content.lower():
                technologies.append("jQuery")

            # React
            if 'react' in content.lower():
                technologies.append("React")

            # Vue
            if 'vue' in content.lower():
                technologies.append("Vue")

            self.results['technologies'] = technologies

        except requests.RequestException:
            pass

        return self.results['technologies']

    def find_sensitive_files(self) -> List[str]:
        """敏感文件扫描"""

        sensitive_paths = [
            '/robots.txt',
            '/.git/config',
            '/.env',
            '/wp-config.php',
            '/web.config',
            '/.htaccess',
            '/admin',
            '/phpmyadmin',
            '/backup.zip',
            '/database.sql'
        ]

        found_files = []

        for path in sensitive_paths:
            url = f"http://{self.target}{path}"

            try:
                response = requests.get(url, timeout=5)

                if response.status_code in [200, 301, 302]:
                    found_files.append(path)

            except requests.RequestException:
                continue

        self.results['sensitive_files'] = found_files
        return found_files

    def extract_emails(self) -> Set[str]:
        """提取邮箱地址"""

        url = f"http://{self.target}"

        try:
            response = requests.get(url, timeout=10)
            content = response.text

            # 邮箱正则
            email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'

            emails = set(re.findall(email_pattern, content, re.IGNORECASE))

            self.results['emails'] = emails

        except requests.RequestException:
            pass

        return self.results['emails']

    def generate_report(self) -> Dict:
        """生成报告"""

        return {
            'target': self.target,
            'subdomains': list(self.results['subdomains']),
            'open_ports': self.results['open_ports'],
            'technologies': self.results['technologies'],
            'sensitive_files': self.results['sensitive_files'],
            'emails': list(self.results['emails']),
            'social_accounts': list(self.results['social_accounts'])
        }

3.2 漏洞扫描

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# ========== 漏洞扫描器 ==========

import requests
from typing import Dict, List
import re

class VulnerabilityScanner:
    """漏洞扫描器"""

    def __init__(self, target: str):
        self.target = target
        self.vulnerabilities = []

    def scan_sql_injection(self, urls: List[str]) -> List[Dict]:
        """SQL注入扫描"""

        payloads = [
            "'",
            "' OR '1'='1",
            "' OR '1'='1'--",
            "admin'--",
            "1' ORDER BY 1--",
            "1' UNION SELECT NULL--"
        ]

        found = []

        for url in urls:
            for payload in payloads:
                try:
                    # 测试GET参数
                    if '?' in url:
                        test_url = f"{url}{payload}"

                        response = requests.get(test_url, timeout=5)

                        # 检查SQL错误特征
                        sql_errors = [
                            "You have an error in your SQL syntax",
                            "Warning: mysql_fetch_array()",
                            "ORA-01756: quoted string not properly terminated",
                            "Unclosed quotation mark after the character string"
                        ]

                        if any(error in response.text for error in sql_errors):
                            found.append({
                                'type': 'SQL Injection',
                                'url': url,
                                'payload': payload,
                                'severity': 'High'
                            })

                except requests.RequestException:
                    continue

        self.vulnerabilities.extend(found)
        return found

    def scan_xss(self, urls: List[str]) -> List[Dict]:
        """XSS扫描"""

        payloads = [
            "<script>alert('XSS')</script>",
            "<img src=x onerror=alert('XSS')>",
            "<svg onload=alert('XSS')>",
            "javascript:alert('XSS')",
            "'><script>alert('XSS')</script>"
        ]

        found = []

        for url in urls:
            for payload in payloads:
                try:
                    # URL编码payload
                    import urllib.parse
                    encoded_payload = urllib.parse.quote(payload)

                    test_url = f"{url}{encoded_payload}"

                    response = requests.get(test_url, timeout=5)

                    # 检查payload是否被反射
                    if payload in response.text or encoded_payload in response.text:
                        found.append({
                            'type': 'XSS',
                            'url': url,
                            'payload': payload,
                            'severity': 'High'
                        })

                except requests.RequestException:
                    continue

        self.vulnerabilities.extend(found)
        return found

    def check_security_headers(self, url: str) -> Dict:
        """安全头检查"""

        try:
            response = requests.get(url, timeout=10)
            headers = response.headers

            missing_headers = []

            # 检查必要的安全头
            security_headers = {
                'X-Frame-Options': '防止点击劫持',
                'X-Content-Type-Options': '防止MIME类型嗅探',
                'X-XSS-Protection': 'XSS过滤',
                'Content-Security-Policy': '内容安全策略',
                'Strict-Transport-Security': '强制HTTPS'
            }

            for header, description in security_headers.items():
                if header not in headers:
                    missing_headers.append({
                        'header': header,
                        'description': description
                    })

            return {
                'url': url,
                'missing_headers': missing_headers,
                'severity': 'Medium' if missing_headers else 'Low'
            }

        except requests.RequestException:
            return {'error': 'Failed to check headers'}

    def scan_directory_traversal(self, base_url: str) -> List[Dict]:
        """目录遍历扫描"""

        payloads = [
            '../../../etc/passwd',
            '..\\..\\..\\windows\\win.ini',
            '....//....//....//etc/passwd',
            '%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd'
        ]

        found = []

        # 常见的可测试端点
        test_paths = [
            '/download',
            '/file',
            '/image',
            '/document'
        ]

        for path in test_paths:
            for payload in payloads:
                try:
                    test_url = f"{base_url}{path}?file={payload}"

                    response = requests.get(test_url, timeout=5)

                    # 检查响应内容
                    if 'root:' in response.text or '[extensions]' in response.text:
                        found.append({
                            'type': 'Directory Traversal',
                            'url': test_url,
                            'payload': payload,
                            'severity': 'High'
                        })

                except requests.RequestException:
                    continue

        self.vulnerabilities.extend(found)
        return found

    def generate_report(self) -> Dict:
        """生成扫描报告"""

        # 统计漏洞
        vuln_count = {
            'Critical': 0,
            'High': 0,
            'Medium': 0,
            'Low': 0
        }

        for vuln in self.vulnerabilities:
            severity = vuln.get('severity', 'Low')
            vuln_count[severity] = vuln_count.get(severity, 0) + 1

        return {
            'target': self.target,
            'total_vulnerabilities': len(self.vulnerabilities),
            'by_severity': vuln_count,
            'vulnerabilities': self.vulnerabilities
        }

四、安全编码最佳实践

4.1 密码安全

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# ========== 密码哈希与验证 ==========

import bcrypt
import secrets
import hashlib
from typing import Tuple

class PasswordSecurity:
    """密码安全"""

    @staticmethod
    def hash_password(password: str) -> str:
        """哈希密码"""

        # 生成盐值
        salt = bcrypt.gensalt(rounds=12)

        # 哈希密码
        hashed = bcrypt.hashpw(password.encode('utf-8'), salt)

        return hashed.decode('utf-8')

    @staticmethod
    def verify_password(password: str, hashed: str) -> bool:
        """验证密码"""

        try:
            return bcrypt.checkpw(
                password.encode('utf-8'),
                hashed.encode('utf-8')
            )
        except Exception:
            return False

    @staticmethod
    def validate_password_strength(password: str) -> Tuple[bool, list]:
        """验证密码强度"""

        errors = []

        # 长度检查
        if len(password) < 8:
            errors.append("密码长度至少8位")

        if len(password) > 128:
            errors.append("密码长度不超过128位")

        # 复杂度检查
        has_upper = any(c.isupper() for c in password)
        has_lower = any(c.islower() for c in password)
        has_digit = any(c.isdigit() for c in password)
        has_special = any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password)

        if not (has_upper and has_lower):
            errors.append("密码必须包含大小写字母")

        if not has_digit:
            errors.append("密码必须包含数字")

        if not has_special:
            errors.append("密码必须包含特殊字符")

        # 常见弱密码检查
        common_passwords = [
            'password', '12345678', 'qwerty', 'abc123',
            'admin', 'welcome', 'password123'
        ]

        if password.lower() in common_passwords:
            errors.append("密码过于常见")

        return len(errors) == 0, errors

    @staticmethod
    def generate_reset_token() -> str:
        """生成密码重置令牌"""

        return secrets.token_urlsafe(32)

    @staticmethod
    def hash_reset_token(token: str) -> str:
        """哈希重置令牌用于存储"""

        return hashlib.sha256(token.encode()).hexdigest()

# ========== 密码策略 ==========

class PasswordPolicy:
    """密码策略"""

    def __init__(
        self,
        min_length: int = 8,
        max_length: int = 128,
        require_uppercase: bool = True,
        require_lowercase: bool = True,
        require_digit: bool = True,
        require_special: bool = True,
        expire_days: int = 90,
        prevent_reuse: int = 5
    ):
        self.min_length = min_length
        self.max_length = max_length
        self.require_uppercase = require_uppercase
        self.require_lowercase = require_lowercase
        self.require_digit = require_digit
        self.require_special = require_special
        self.expire_days = expire_days
        self.prevent_reuse = prevent_reuse

    def validate(self, password: str, user_history: list = None) -> Tuple[bool, list]:
        """验证密码"""

        errors = []

        # 长度
        if len(password) < self.min_length:
            errors.append(f"密码长度至少{self.min_length}位")

        if len(password) > self.max_length:
            errors.append(f"密码长度不超过{self.max_length}位")

        # 复杂度
        if self.require_uppercase and not any(c.isupper() for c in password):
            errors.append("密码必须包含大写字母")

        if self.require_lowercase and not any(c.islower() for c in password):
            errors.append("密码必须包含小写字母")

        if self.require_digit and not any(c.isdigit() for c in password):
            errors.append("密码必须包含数字")

        if self.require_special and not any(
            c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password
        ):
            errors.append("密码必须包含特殊字符")

        # 历史密码检查
        if user_history and self.prevent_reuse:
            recent_passwords = user_history[:self.prevent_reuse]

            for old_hashed in recent_passwords:
                if PasswordSecurity.verify_password(password, old_hashed):
                    errors.append(f"不能使用最近{self.prevent_reuse}次使用过的密码")
                    break

        return len(errors) == 0, errors

4.2 敏感数据保护

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# ========== 敏感数据加密 ==========

from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2
import os
import base64
from typing import Any
import json

class DataEncryption:
    """数据加密"""

    @staticmethod
    def generate_key() -> bytes:
        """生成加密密钥"""
        return Fernet.generate_key()

    @staticmethod
    def encrypt_data(data: str, key: bytes) -> str:
        """加密数据"""
        f = Fernet(key)
        encrypted = f.encrypt(data.encode())
        return base64.b64encode(encrypted).decode()

    @staticmethod
    def decrypt_data(encrypted_data: str, key: bytes) -> str:
        """解密数据"""
        f = Fernet(key)
        encrypted = base64.b64decode(encrypted_data.encode())
        decrypted = f.decrypt(encrypted)
        return decrypted.decode()

    @staticmethod
    def encrypt_dict(data: dict, key: bytes) -> str:
        """加密字典数据"""
        json_str = json.dumps(data)
        return DataEncryption.encrypt_data(json_str, key)

    @staticmethod
    def decrypt_dict(encrypted_data: str, key: bytes) -> dict:
        """解密字典数据"""
        json_str = DataEncryption.decrypt_data(encrypted_data, key)
        return json.loads(json_str)

class SecureStorage:
    """安全存储"""

    def __init__(self, encryption_key: bytes):
        self.encryption_key = encryption_key

    def store_sensitive_data(self, key: str, data: Any):
        """存储敏感数据"""

        # 序列化数据
        if isinstance(data, dict):
            encrypted = DataEncryption.encrypt_dict(data, self.encryption_key)
        else:
            encrypted = DataEncryption.encrypt_data(str(data), self.encryption_key)

        # 存储到数据库或文件
        # 这里简化为内存存储
        self.storage[key] = encrypted

    def retrieve_sensitive_data(self, key: str) -> Any:
        """检索敏感数据"""

        encrypted = self.storage.get(key)

        if not encrypted:
            return None

        # 尝试解密为字典
        try:
            return DataEncryption.decrypt_dict(encrypted, self.encryption_key)
        except:
            return DataEncryption.decrypt_data(encrypted, self.encryption_key)

# ========== 日志脱敏 ==========

import re

class LogSanitizer:
    """日志脱敏"""

    # 敏感信息正则模式
    PATTERNS = {
        'email': r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
        'phone': r'\b1[3-9]\d{9}\b',
        'id_card': r'\b\d{17}[\dXx]\b',
        'credit_card': r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b',
        'password': r'(password|pwd|passwd)\s*[=:]\s*\S+',
        'token': r'(token|api_key|secret)\s*[=:]\s*\S+',
        'ip': r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
    }

    @classmethod
    def sanitize(cls, log_message: str) -> str:
        """脱敏日志"""

        sanitized = log_message

        for pattern_type, pattern in cls.PATTERNS.items():
            if pattern_type == 'email':
                sanitized = re.sub(
                    pattern,
                    lambda m: f"{m.group(0)[0]}***@{m.group(0).split('@')[1]}",
                    sanitized
                )
            elif pattern_type == 'phone':
                sanitized = re.sub(
                    pattern,
                    lambda m: f"{m.group(0)[:3]}****{m.group(0)[7:]}",
                    sanitized
                )
            elif pattern_type == 'id_card':
                sanitized = re.sub(
                    pattern,
                    lambda m: f"{m.group(0)[:6]}********{m.group(0)[14:]}",
                    sanitized
                )
            elif pattern_type in ['password', 'token', 'credit_card']:
                sanitized = re.sub(
                    pattern,
                    lambda m: f"{m.group(0).split('=')[0].split(':')[0]}=***",
                    sanitized
                )
            elif pattern_type == 'ip':
                sanitized = re.sub(
                    pattern,
                    lambda m: f"{m.group(0).rsplit('.', 1)[0]}.*",
                    sanitized
                )

        return sanitized

    @classmethod
    def sanitize_dict(cls, data: dict) -> dict:
        """脱敏字典数据"""

        sanitized = {}

        for key, value in data.items():
            if isinstance(value, str):
                sanitized[key] = cls.sanitize(value)
            elif isinstance(value, dict):
                sanitized[key] = cls.sanitize_dict(value)
            else:
                sanitized[key] = value

        return sanitized

总结

网络安全是一个持续的过程,需要:

  1. 安全编码:遵循安全编码规范,从源头减少漏洞
  2. 定期测试:进行渗透测试和漏洞扫描
  3. 多层防御:实施纵深防御策略
  4. 持续监控:建立安全监控和响应机制
  5. 安全培训:提高团队安全意识

相关工具推荐