Python安全最佳实践:代码规范与防御指南
Python安全与最佳实践:从代码规范到安全防御
1. 代码安全
1.1 注入防御
SQL注入防御
SQL注入是最常见的安全漏洞之一,攻击者通过在输入中嵌入恶意SQL代码来操纵数据库查询。
危险示例:
# 不安全的写法
user_input = input("请输入用户ID: ")
query = f"SELECT * FROM users WHERE id = {user_input}"
cursor.execute(query)
防御方法:
# 使用参数化查询(推荐)
user_input = input("请输入用户ID: ")
query = "SELECT * FROM users WHERE id = %s"
cursor.execute(query, (user_input,))
# 使用ORM(如SQLAlchemy)
user = session.query(User).filter(User.id == user_input).first()
实践建议:
- 永远不要直接拼接SQL语句
- 使用ORM框架可以大大降低SQL注入风险
- 对用户输入进行白名单验证
XSS防御
跨站脚本攻击(XSS)允许攻击者在受害者的浏览器中执行恶意脚本。
危险示例:
# Flask中不安全的渲染
@app.route('/search')
def search():
query = request.args.get('q', '')
return f"<h1>搜索结果: {query}</h1>"
防御方法:
# 使用模板引擎自动转义(Flask/Jinja2默认开启)
@app.route('/search')
def search():
query = request.args.get('q', '')
return render_template('search.html', query=query)
# 手动转义
from markupsafe import escape
safe_query = escape(query)
实践建议:
- 所有动态内容在输出到HTML前都应转义
- 使用现代框架(如Django/Flask)的模板系统
- 设置Content Security Policy (CSP)头
1.2 敏感信息处理
环境变量管理
不安全做法:
# 直接在代码中硬编码敏感信息
DB_PASSWORD = "supersecret123"
推荐做法:
# 使用python-dotenv管理环境变量
from dotenv import load_dotenv
import os
load_dotenv() # 从.env文件加载环境变量
DB_PASSWORD = os.getenv("DB_PASSWORD")
.env文件示例:
DB_PASSWORD=supersecret123
API_KEY=your_api_key_here
实践建议:
- 永远不要将.env文件提交到版本控制
- 在生产环境中使用真正的环境变量而非文件
- 使用不同的环境变量文件用于开发、测试和生产
加密库使用
Python标准库中的hashlib
和cryptography
是处理加密的安全选择。
密码哈希示例:
import hashlib
import os
def hash_password(password):
# 生成随机盐值
salt = os.urandom(32)
# 创建PBKDF2哈希
key = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
100000 # 迭代次数
)
return salt + key
def verify_password(stored_password, provided_password):
salt = stored_password[:32]
stored_key = stored_password[32:]
new_key = hashlib.pbkdf2_hmac(
'sha256',
provided_password.encode('utf-8'),
salt,
100000
)
return new_key == stored_key
实践建议:
- 不要自己实现加密算法,使用标准库或知名第三方库
- 对于密码存储,使用专门的库如
passlib
- 定期更新加密库以获取安全补丁
2. 代码规范
2.1 PEP 8规范
PEP 8是Python官方的代码风格指南,主要内容包括:
命名约定:
- 变量和函数:
lower_case_with_underscores
- 类名:
CapitalizedWords
- 常量:
ALL_CAPS
- 变量和函数:
- 缩进:使用4个空格(非Tab)
- 行长度:不超过79个字符(文档字符串/注释不超过72)
导入顺序:
- 标准库导入
- 相关第三方库导入
- 本地应用/库导入
示例:
# 正确的导入顺序
import os
import sys
from typing import Dict, List
import flask
import pandas as pd
from myapp.utils import helper_function
2.2 文档字符串
Google风格示例
def calculate_statistics(data: List[float]) -> Dict[str, float]:
"""计算数据的描述性统计量。
Args:
data: 包含数值的列表
Returns:
包含以下键的字典:
mean - 平均值
std - 标准差
max - 最大值
min - 最小值
"""
# 实现代码...
NumPy风格示例
def calculate_statistics(data):
"""计算数据的描述性统计量。
Parameters
----------
data : list of float
包含数值的列表
Returns
-------
dict
包含以下键的字典:
- mean : float
平均值
- std : float
标准差
- max : float
最大值
- min : float
最小值
"""
# 实现代码...
实践建议:
- 为所有公共模块、函数、类和方法编写文档字符串
- 保持风格一致(整个项目使用同一种风格)
- 使用类型注解提高代码可读性
2.3 异常处理最佳实践
基础模式:
try:
# 可能出错的代码
result = perform_operation()
except ValueError as e:
# 处理特定异常
logger.error(f"值错误: {e}")
raise CustomError("操作失败") from e
except (TypeError, IndexError) as e:
# 处理多个异常
logger.error(f"类型或索引错误: {e}")
except Exception as e:
# 宽泛的异常捕获(谨慎使用)
logger.exception("未预期的错误")
raise
else:
# 没有异常时执行
process_result(result)
finally:
# 无论是否异常都执行
cleanup_resources()
实践建议:
- 具体优于宽泛:捕获特定异常而非所有异常
- 异常链:使用
raise ... from ...
保留原始异常上下文 - 日志记录:记录异常详细信息但不要暴露敏感信息
- 资源清理:使用
finally
或上下文管理器确保资源释放 - 自定义异常:为领域特定错误创建自定义异常类
上下文管理器示例:
class DatabaseConnection:
def __enter__(self):
self.conn = connect_to_db()
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
self.conn.close()
if exc_type is not None:
logger.error("数据库操作出错", exc_info=(exc_type, exc_val, exc_tb))
# 使用方式
with DatabaseConnection() as conn:
conn.execute_query("SELECT * FROM users")
安全开发检查清单
- [ ] 使用参数化查询防御SQL注入
- [ ] 对所有输出到HTML的内容进行转义
- [ ] 敏感信息存储在环境变量中
- [ ] 使用强密码哈希算法存储密码
- [ ] 遵循PEP 8代码风格指南
- [ ] 为所有公共接口编写文档字符串
- [ ] 实现细粒度的异常处理
- [ ] 定期更新依赖库版本
通过遵循这些安全实践和代码规范,您可以显著提高Python代码的质量和安全性,减少潜在漏洞的风险。