本文档描述了 Markdown 搜索引擎实施的安全措施和最佳实践。
系统实施了以下安全加固措施:
- 查询字符串长度限制 - 防止过长查询导致的性能问题
- 路径遍历防护 - 防止访问文档根目录外的文件
- CORS 策略配置 - 控制跨域资源共享
- 数据库文件权限 - 限制数据库文件访问权限
防止恶意用户提交超长查询字符串,导致:
- 数据库查询性能下降
- 内存占用过高
- 潜在的拒绝服务攻击
- 最大查询长度:500 字符
- 验证位置:API 端点
/search - 超长查询返回:
422 Unprocessable Entity
# 有效查询
GET /search?q=markdown
# 无效查询(超过 500 字符)
GET /search?q=aaaa...(501+ 字符)
# 返回: 422 - "Query string too long. Maximum length is 500 characters."查询长度限制在 app/security.py 中定义:
MAX_QUERY_LENGTH = 500 # 可根据需要调整防止攻击者通过路径遍历访问系统中的敏感文件,例如:
/etc/passwd../../../config/secrets.yml- 其他系统文件
- 所有文件路径必须在配置的
MD_ROOT目录内 - 使用
Path.resolve()解析真实路径 - 使用
Path.relative_to()验证路径在允许范围内 - 最大路径长度:1000 字符
def validate_path_traversal(file_path: str) -> Path:
"""验证文件路径,防止路径遍历攻击"""
md_root = Path(settings.md_root).resolve()
full_path = (md_root / file_path).resolve()
# 检查路径是否在允许的根目录内
try:
full_path.relative_to(md_root)
except ValueError:
raise SecurityError("Access denied: Path is outside the allowed directory")
return full_path# 有效路径
validate_path_traversal("docs/readme.md") # ✓ 通过
# 无效路径
validate_path_traversal("../../../etc/passwd") # ✗ 拒绝
validate_path_traversal("/etc/passwd") # ✗ 拒绝- 文档检索:
search_service.render_document_html() - 文件索引:
indexer.index_file()
控制哪些域名可以访问 API,防止:
- 跨站请求伪造(CSRF)
- 未授权的跨域访问
- 数据泄露
# 开发环境(默认)
CORS_ORIGINS=* # 允许所有源
# 生产环境(推荐)
CORS_ORIGINS=https://example.com,https://app.example.com# app/main.py
cors_origins = os.getenv("CORS_ORIGINS", "").split(",")
app.add_middleware(
CORSMiddleware,
allow_origins=cors_origins,
allow_credentials=True,
allow_methods=["GET", "POST", "OPTIONS"], # 只允许必要的方法
allow_headers=["Content-Type", "Authorization"],
max_age=600 # 预检请求缓存 10 分钟
)export CORS_ORIGINS="https://example.com,https://app.example.com"# .env
CORS_ORIGINS=https://example.com,https://app.example.com- 明确指定允许的源,不要使用
* - 使用 HTTPS 协议
- 限制允许的 HTTP 方法(已实现:GET, POST, OPTIONS)
- 限制允许的请求头(已实现:Content-Type, Authorization)
保护数据库文件不被未授权访问或修改:
- 防止其他用户读取数据库内容
- 防止数据库被篡改
- 符合最小权限原则
- Unix/Linux 系统:自动设置权限为
600(仅所有者可读写) - Windows 系统:跳过 Unix 权限设置(使用 NTFS 权限)
# Unix 权限 600 表示:
# - 所有者:读写(rw-)
# - 组:无权限(---)
# - 其他:无权限(---)
-rw------- 1 user user 1024 Nov 23 10:00 md_search.db系统启动时自动检查和设置数据库文件权限:
# app/main.py - startup event
set_database_permissions() # 设置权限
check_database_permissions() # 验证权限# Unix/Linux
chmod 600 data/md_search.db
# 验证权限
ls -l data/md_search.db防止错误信息泄露敏感信息:
- 内部文件路径
- 数据库结构
- 系统配置
def sanitize_error_message(error_message: str, is_production: bool = True) -> str:
"""清理错误信息,避免泄露敏感信息"""
if not is_production:
return error_message # 开发环境返回完整信息
# 生产环境返回通用错误信息
sensitive_keywords = [
str(settings.md_root),
str(settings.db_path),
"sqlite", "database", "traceback"
]
for keyword in sensitive_keywords:
if keyword.lower() in error_message.lower():
return "An internal error occurred. Please contact the administrator."
return error_message-
查询字符串
- 不能为空或仅包含空白字符
- 长度不超过 500 字符
-
分页参数
limit: 1-100 之间offset: 非负整数
-
文档 ID
- 必须是正整数
-
文件路径
- 必须在 MD_ROOT 目录内
- 长度不超过 1000 字符
# nginx.conf
server {
listen 443 ssl;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8000;
}
}# 只允许必要的端口
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable# 设置 MD_ROOT 目录权限
chmod 755 /path/to/markdown/docs
chown -R appuser:appuser /path/to/markdown/docs
# 设置数据库目录权限
chmod 700 /path/to/data
chown appuser:appuser /path/to/data# 创建专用用户
useradd -r -s /bin/false mdsearch
# 使用 systemd 以专用用户运行
[Service]
User=mdsearch
Group=mdsearch# 使用 slowapi 限制请求频率
from slowapi import Limiter
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
@app.get("/search")
@limiter.limit("10/minute")
async def search(...):
...系统会记录以下安全相关事件:
- 路径遍历尝试
- 超长查询字符串
- 无效的 API 请求
- 数据库权限问题
# 生产环境建议使用 INFO 或 WARNING
LOG_LEVEL=INFO定期检查日志中的安全警告:
# 查找路径遍历尝试
grep "Path traversal attempt" /var/log/mdsearch.log
# 查找超长查询
grep "Query string too long" /var/log/mdsearch.log部署前请确认:
- 已配置具体的 CORS_ORIGINS(不使用
*) - 数据库文件权限设置为 600
- MD_ROOT 目录权限正确设置
- 使用 HTTPS 协议
- 应用以非 root 用户运行
- 防火墙规则已配置
- 日志级别设置为 INFO 或 WARNING
- 定期备份数据库文件
- 监控系统日志中的安全警告
-
SQLite 并发限制
- SQLite 支持多读单写
- 高并发写入可能导致锁等待
- 建议:使用连接池或考虑 PostgreSQL
-
文件系统监控
- 依赖 watchdog 库
- 大量文件变化可能导致延迟
- 建议:定期全量重建索引
-
中文分词
- 使用字符级分词
- 不支持智能分词
- 建议:考虑集成 jieba 等分词库
如果发现安全漏洞,请:
- 不要公开披露
- 发送邮件至安全团队
- 提供详细的复现步骤
- 等待安全团队响应
- 2024-11-23: 初始版本
- 实现查询长度限制
- 实现路径遍历防护
- 配置 CORS 策略
- 设置数据库文件权限