bch1212/queryshield

GitHub: bch1212/queryshield

QueryShield 是一个位于 AI agent 与企业数据库之间的安全 SQL 代理,通过 AST 校验、行级安全和审计日志保障数据访问的合规与安全。

Stars: 1 | Forks: 0

# QueryShield [![测试](https://static.pigsec.cn/wp-content/uploads/repos/cas/6b/6b52945adbf8d9e421fe243515ae54cfbd3da263f16b1eabda37cdc0b797b8eb.svg)](https://github.com/bch1212/queryshield/actions/workflows/test.yml) AI agent 与企业数据库之间的安全 SQL 代理。 Agent 以纯英文(或结构化 SQL)调用单个 endpoint。QueryShield 会: 1. 通过 Claude 结合 prompt 缓存,将自然语言翻译为 → SQL。 2. 在 AST 级别验证每一条查询 —— 仅允许 `SELECT`,不允许 堆叠语句、禁用函数,且必须包含 LIMIT。 3. 应用针对每个 agent 的行级安全控制:schema/table 白名单以及 `WHERE` 子句注入。 4. 针对客户 DB 执行查询并返回数据行。 5. 将每次尝试记录到只允许追加的审计表中 —— 仅记录元数据, 绝不记录行内容。 Agent 永远不会看到连接字符串。 ## 快速开始 ``` pip install -r requirements.txt cp .env.example .env # 设置 ANTHROPIC_API_KEY、DATABASE_URL、VAULT_KEY(见下文) python -m queryshield.start ``` 为 `VAULT_KEY` 生成一次 Fernet 密钥,并且切勿丢失: ``` python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" ``` ## 端到端流程 (curl) ``` # 1) 启动一个 tenant。返回 admin API key — 请将其复制。 curl -X POST localhost:8000/v1/tenants?name=Acme # 2) 注册客户 DB。连接字符串在静止状态下是加密的。 curl -X POST localhost:8000/v1/databases \ -H 'X-Admin-Key: qs_...' \ -H 'Content-Type: application/json' \ -d '{ "alias": "prod", "db_type": "postgresql", "connection_string": "postgresql://reader:secret@db.acme.internal:5432/app", "allowed_tables": ["users", "orders"] }' # 3) 为你的 AI app 提供一个 scoped agent(不同于 admin)。 curl -X POST localhost:8000/v1/agents \ -H 'X-Admin-Key: qs_...' \ -H 'Content-Type: application/json' \ -d '{ "name": "reporting", "tenant_id": "" }' # 4) 设置该 agent 的 RLS 策略。 curl -X POST localhost:8000/v1/policies \ -H 'X-Admin-Key: qs_...' \ -H 'Content-Type: application/json' \ -d '{ "agent_id": "", "database_alias": "prod", "allowed_tables": ["users", "orders"], "row_filters": { "users": "tenant_id = 42" } }' # 5) 该 agent 进行查询。 curl -X POST localhost:8000/v1/query \ -H 'X-API-Key: qs_...' \ -H 'Content-Type: application/json' \ -d '{ "database_alias": "prod", "query": "how many active users do we have?", "mode": "nl", "max_rows": 10 }' ``` ## MCP 集成 **已在[官方 MCP Registry](https://registry.modelcontextprotocol.io/v0/servers?search=queryshield) 中收录,名称为 `io.github.bch1212/queryshield`。** 安装客户端: ``` pip install queryshield-mcp ``` 然后将以下内容放入你的 Claude Desktop / Cursor / agent 配置中: ``` { "queryshield": { "command": "queryshield-mcp", "env": { "QUERYSHIELD_API_KEY": "qs_..." } } } ``` 独立的 PyPI package 源码位于 `packages/queryshield-mcp/`。 对于 Glama 等 MCP 目录评估器,仓库根目录还包含一个轻量级的 `Dockerfile`,用于启动已发布的 `queryshield-mcp` stdio server 以进行工具内省。该容器在 MCP 初始化/工具发现阶段不需要 `QUERYSHIELD_API_KEY`;仅当实际调用发现的工具以访问 QueryShield API 租户时,才需要此密钥。 ## MCP 集成(旧版) 将以下内容放入任何支持 MCP 的客户端中(Claude Desktop、Cursor、自定义 agent): ``` { "queryshield": { "command": "python", "args": ["-m", "queryshield.mcp_server"], "env": { "QUERYSHIELD_API_KEY": "qs_...", "QUERYSHIELD_BASE_URL": "https://api.queryshield.io" } } } ``` 暴露的工具: - `query_database(database_alias, question, max_rows)` — 自然语言查询 - `query_database_sql(database_alias, sql, max_rows)` — 预构建的 SELECT - `get_audit_log(limit)` — 调用 agent 的最近尝试记录 ## 安全模型 | 威胁 | 防御手段 | | ---------------------------------------------- | ------------------------------------------ | | Agent 构造 `DROP TABLE` | sqlglot AST 拒绝非 SELECT 语句 | | Agent 偷偷插入 `;` 和第二条语句 | 解析器拒绝 `len(statements) > 1` | | Agent 使用 `pg_sleep`, `xp_cmdshell`, ... | AST 节点级别的函数拒绝列表 | | Agent 读取其权限范围外的表 | RLS schema + table 白名单 | | Agent 读取其他租户的数据行 | 通过 AST `.where()` 注入 `row_filters` | | 连接字符串通过 stack trace 泄露 | Fernet 加密,绝不通过任何 API 返回 | | 审计日志成为数据泄露向量 | 仅存储元数据 —— 绝不包含数据行 | | `VAULT_KEY` 轮换 | 在新密钥下重新加密数据行(脚本驱动) | `safety.py` 是最重要的模块。其中合并的每一项额外 检查,都应随附 `tests/test_safety.py` 中的测试。 ## 定价 | 层级 | 月费 | 数据库 | 查询 / 月 | 备注 | | ----------- | ------- | --------- | --------------- | ---------------------- | | Starter | $500 | 3 | 1,000,000 | | | Pro | $1,500 | 10 | 10,000,000 | 审计导出 | | Enterprise | $3,500 | 不限 | 不限 | SSO, SIEM webhook | 目标为 `$32.5K MRR @ 15 customers (10 Pro + 5 Enterprise)`。 ## 部署 该仓库已适配 Railway。`python -m queryshield.start` 是入口点 (通过 `os.getenv` 读取 `PORT`,因为 Railway 执行 start 命令时不带 shell)。从 Railway 市场配置 Postgres +(可选)Redis, 其余部分均通过环境变量设置。 ``` railway up ``` `/health` 是存活检查。如果 control-plane DB 无法连接,`/ready` 将返回 503。 ## 测试 ``` pip install pytest python -m pytest tests/ ``` 42 个测试涵盖: - AST 安全(24 个用例 —— 直接 DDL、注释、编码关键字、多条 语句、禁用函数、缺少 LIMIT) - RLS 引擎(6 个用例 —— 白名单强制执行、WHERE 注入、与 现有谓词的连接) - 针对 SQLite “客户 DB” 的代理端到端测试(5 个用例 —— 正常路径、 被拦截的 DML、RLS 行过滤、表白名单、缓存命中) - 通过 FastAPI TestClient 进行 HTTP 集成(7 个用例 —— 完整的配置 → 查询流程、带 RLS 的受限 agent、授权失败)
标签:SQL代理, Streamlit, 安全规则引擎, 搜索引擎查询, 测试用例, 访问控制, 请求拦截, 逆向工具