mcpField
P
py-security

py-security

github.com/l-mb安全5650 次安装34 周前更新
代码仓库

关于 py-security

py-security 是用于发现并修复 Python 代码安全风险的 Claude skill。它指导使用 bandit 和 Ruff 扫描,涵盖 SQL 注入、硬编码密钥、弱加密和不安全 shell 命令等问题。

主要功能

  1. 01使用 bandit 和 Ruff 安全规则扫描
  2. 02识别 SQL 注入风险
  3. 03查找硬编码密钥
  4. 04检查加密与 shell 命令风险
  5. 05采用安全编码模式修复

适用场景

  1. 01审计 Python 仓库中的漏洞
  2. 02将硬编码凭据迁移到环境变量
  3. 03发布前检查 SQL 查询与 shell 命令

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/l-mb/python-refactoring-skills --skill py-security

安装后在 Claude Code 中输入/py-security确认已加载。

详细介绍

Python 安全分析与修复

遵循工程章程的安全原则,发现并修复 Python 代码中的安全漏洞。

目标

  1. 使用自动化扫描器检测安全漏洞
  2. 识别 SQL 注入风险
  3. 查找硬编码的机密和凭据
  4. 检测薄弱的加密实践
  5. 遵循安全编码模式修复漏洞
  6. 防止机密被提交到 git

所需工具

添加到 [dependency-groups] dev:"bandit"、"ruff"

  • bandit:基于 AST 的安全扫描器
  • ruff --select S:内置的 Bandit 规则(更快速的替代方案)

权限:先运行 py-quality-setup,配置 .claude/settings.local.json 并授予所有所需工具权限。

发现阶段

运行安全扫描器
# Using bandit (comprehensive reports)
bandit -r . -f json -o bandit_report.json
bandit -r . -ll  # Show only medium/high severity

# Using ruff (faster, integrates with existing tooling)
ruff check . --select S
ruff check . --select S --output-format=json > security_report.json
常见漏洞类别

高严重性(立即修复):

  • S608:通过字符串格式化造成 SQL 注入
  • S105, S106, S107:硬编码的密码、机密和密钥
  • S501, S506:薄弱的 SSL/TLS 配置
  • S324:不安全的哈希函数(MD5、SHA1)
  • S602, S603, S604, S605, S606, S607:Shell 注入风险

中严重性:

  • S311:不安全的随机数生成
  • S301, S302, S303:不安全的反序列化(pickle)
  • S701, S702:Jinja2 自动转义问题

低严重性(结合上下文考虑):

  • S101:使用 assert(在生产环境中会被禁用)
  • S104:绑定到所有网络接口(0.0.0.0)

修复模式

防止 SQL 注入
# BEFORE - Vulnerable to SQL injection
def get_user(user_id: str) -> dict:
    cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
    return cursor.fetchone()

# AFTER - Use parametrized queries
def get_user(user_id: str) -> dict:
    cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
    return cursor.fetchone()

# For dynamic table/column names (cannot be parametrized)
from psycopg2 import sql

def get_data(table_name: str, column: str) -> list:
    # Validate inputs first!
    allowed_tables = {"users", "orders", "products"}
    if table_name not in allowed_tables:
        raise ValueError(f"Invalid table: {table_name}")

    query = sql.SQL("SELECT {column} FROM {table}").format(
        column=sql.Identifier(column),
        table=sql.Identifier(table_name)
    )
    cursor.execute(query)
    return cursor.fetchall()
移除硬编码机密
# BEFORE - Hardcoded secrets (NEVER commit these)
API_KEY = "sk-1234567890abcdef"
DATABASE_URL = "postgres://user:password@localhost/db"
SECRET_TOKEN = "my-secret-token-123"

# AFTER - Use environment variables
import os

API_KEY = os.environ["API_KEY"]  # Raises if not set
DATABASE_URL = os.getenv("DATABASE_URL", "postgres://localhost/db")  # With default
SECRET_TOKEN = os.environ.get("SECRET_TOKEN")  # Returns None if not set

# For development, use python-dotenv
from dotenv import load_dotenv
load_dotenv()  # Loads from .env file (add .env to .gitignore!)
API_KEY = os.environ["API_KEY"]
安全地生成随机数
# BEFORE - Insecure for security-sensitive operations
import random

session_token = random.randint(100000, 999999)
api_key = ''.join(random.choices('abcdef0123456789', k=32))

# AFTER - Use secrets module for cryptographic randomness
import secrets

session_token = secrets.randbelow(900000) + 100000
api_key = secrets.token_hex(16)  # 32 hex characters
secure_password = secrets.token_urlsafe(32)  # URL-safe token
安全加密
# BEFORE - Weak hash functions
import hashlib

password_hash = hashlib.md5(password.encode()).hexdigest()  # Weak!
checksum = hashlib.sha1(data).hexdigest()  # Weak for security!

# AFTER - Use strong hash functions
import hashlib

# For passwords - use bcrypt or argon2
from argon2 import PasswordHasher
ph = PasswordHasher()
password_hash = ph.hash(password)
ph.verify(password_hash, password)  # Verify

# For data integrity - use SHA256 or better
checksum = hashlib.sha256(data).hexdigest()
secure_hash = hashlib.blake2b(data).hexdigest()
防止 Shell 命令注入
# BEFORE - Shell injection vulnerability
import os
import subprocess

filename = user_input
os.system(f"cat {filename}")  # Dangerous!
subprocess.call(f"ls {directory}", shell=True)  # Dangerous!

# AFTER - Use subprocess with list arguments (no shell)
import subprocess
from pathlib import Path

# Validate input first
filename = user_input
if not Path(filename).exists():
    raise ValueError("File not found")

# Use list form, no shell=True
subprocess.run(["cat", filename], check=True)
subprocess.run(["ls", directory], check=True)

# Even better - use Python libraries instead of shell commands
with open(filename) as f:
    content = f.read()

集成 Git Hooks

将机密检测添加到 git hooks 中(作为 py-git-hooks 的补充):

# In .git/hooks/pre-commit, add before other checks:

# Detect potential secrets
if git diff --cached | grep -qiE '(password|secret|api[_-]?key|token|credentials)'; then
    echo "⚠️  Warning: Potential secrets detected in staged changes"
    echo "Review carefully before committing"
    read -p "Continue? (y/n) " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        exit 1
    fi
fi

验证清单

  • ruff check . --select S 未报告安全问题
  • bandit -r . -ll 未报告中等或高严重性问题
  • 代码库中没有硬编码机密(使用 grep 搜索 password、secret、api_key、token)
  • 从环境变量或 .env 文件加载机密
  • .env 文件已加入 .gitignore
  • 所有 SQL 查询均使用参数化查询
  • 修复安全问题后,所有测试均通过

示例

示例:安全审计工作流

1. Scan: ruff check . --select S; bandit -r . -ll
2. Found: SQL injection (S608), hardcoded secret (S105)
3. Fix SQL: Use parametrized query with %s placeholders
4. Fix secret: Move to .env, use os.environ["API_KEY"]
5. Verify: Scans clean, tests pass

相关技能

  • 前置条件:py-quality-setup(配置 ruff 以进行安全检查)
  • 强制措施:py-git-hooks(将安全扫描添加到提交前检查)
  • 另请参阅:py-code-health(安全修复后的清理工作)

评论

支持 Markdown