安全SQL MCP服务器

一个具有严格表/列策略控制的只读SQL MCP服务器,适用于需要安全访问数据库的场景。

  • by jrhuerta
  • Python
  • 本地部署
  • 2 星标
  • 更新于
在 GitHub 查看 jrhuerta/secure-sql-mcp

使用说明

安全SQL MCP服务器

具有严格表/列策略控制的只读SQL MCP服务器。

MCP客户端配置

要将此服务器与Cursor、Claude Desktop或其他MCP客户端一起使用,请将其添加到您的MCP配置中:

光标 (.cursor/mcp.json 或光标设置→ MCP):

{
  "mcpServers": {
    "secure-sql": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "--env-file", "/path/to/your/secrets",
        "-v", "/path/to/your/policy:/run/policy:ro",
        "ghcr.io/jrhuerta/secure-sql-mcp:latest"
      ]
    }
  }
}

克劳德桌面版 (claude_desktop_config.json):结构相同 mcpServers.

这 --env-file 应指向包含以下内容的文件 DATABASE_URL 和 ALLOWED_POLICY_FILE=/run/policy/allowed_policy.txt (请参阅下面的环境变量)。该卷以只读方式装载策略目录。先拉图片: docker pull ghcr.io/jrhuerta/secure-sql-mcp:latest

安全模型

  • 数据库凭据保持在服务器端(env-vars),从不出现在提示中。
  • 只允许读取查询。
  • 政策严格且基于文件:
    • 一个必需的文件: ALLOWED_POLICY_FILE
    • 每条线都是 table:col1,col2,col3 或 table:*
  • 如果表/列未被明确允许,则会被阻止。

已实施的安全控制

  • 查询形状强制
    • 每个请求只允许一个SQL语句。
    • 非读取操作被阻止(INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, TRUNCATE, GRANT, REVOKE, MERGE以及相关的命令表达式)。
  • 严格的访问策略执行
    • 默认情况下拒绝表和列。
    • 访问检查适用于直接查询和组合查询(JOIN, UNION、子查询、别名)。
    • SELECT * 除非表策略为 table:*.
    • 在严格模式下,多表查询中的不合格列将被拒绝。
  • 运行时安全控制
    • 查询超时和行上限在服务器端强制执行。
    • 行上限截断在响应有效载荷中是明确的。
  • 安全错误行为
    • 验证和策略失败会返回可操作的补救提示。
    • 对数据库执行失败进行清理,以避免泄露敏感的内部详细信息。

环境变量

变量 必填 默认 描述
DATABASE_URL 是 -- 数据库URL。裸露 postgresql://, mysql://,以及 sqlite:// URL被接受并自动升级为异步驱动程序(+asyncpg, +aiomysql, +aiosqlite).
ALLOWED_POLICY_FILE 是 -- 策略文件的路径
MAX_ROWS 否 100 每个查询返回的最大行数(1-10000)
QUERY_TIMEOUT 否 30 查询超时(秒)(1–300)
LOG_LEVEL 否 信息 日志记录级别(调试、信息、警告、错误)

策略文件格式

allowed_policy.txt:

# table:columns
customers:id,email
orders:*

规则:

  • table:* 允许该表中的所有列。
  • # 允许注释和空白行。
  • 匹配不区分大小写。

代理可发现性

MCP服务器公开:

  • list_tables():
    • 策略允许的表
    • 每个表允许的列数(* 或明确列表)
    • 元数据验证状态(如果可以进行数据库自检)
  • describe_table(table):
    • 策略中允许该表的列
    • 数据库中的模式元数据(如果可用)
  • query(sql):
    • 仅当查询为只读且在表/列策略范围内时执行

快速启动(uv)

git clone https://github.com/jrhuerta/secure-sql-mcp.git
cd secure-sql-mcp

# Optional: use a custom package index for uv/pip (e.g. corporate PyPI mirror)
# export PYTHON_INDEX_URL="https://<your-index>/simple"

cat > .env <<'EOF'
DATABASE_URL=sqlite+aiosqlite:///./example.db
ALLOWED_POLICY_FILE=./policy/allowed_policy.txt
MAX_ROWS=100
QUERY_TIMEOUT=30
LOG_LEVEL=INFO
EOF

mkdir -p policy
cat > policy/allowed_policy.txt <<'EOF'
customers:id,email
orders:*
EOF

# Create tables for local testing (optional)
sqlite3 example.db <<'SQL'
CREATE TABLE IF NOT EXISTS customers (id INTEGER PRIMARY KEY, email TEXT NOT NULL, ssn TEXT);
CREATE TABLE IF NOT EXISTS orders (id INTEGER PRIMARY KEY, total NUMERIC);
INSERT OR IGNORE INTO customers (id, email) VALUES (1, 'test@example.com');
INSERT OR IGNORE INTO orders (id, total) VALUES (1, 19.99);
SQL

uv venv
source .venv/bin/activate
uv pip install -e .
python -m secure_sql_mcp.server

快速入门(Docker)

git clone https://github.com/jrhuerta/secure-sql-mcp.git
cd secure-sql-mcp

mkdir -p policy
cat > policy/allowed_policy.txt <<'EOF'
customers:id,email
orders:*
EOF

cat > .env <<'EOF'
DATABASE_URL=sqlite+aiosqlite:///./example.db
ALLOWED_POLICY_FILE=/run/policy/allowed_policy.txt
MAX_ROWS=100
QUERY_TIMEOUT=30
LOG_LEVEL=INFO
EOF

docker build -t secure-sql-mcp .
docker run -i --rm \
  --env-file .env \
  -v "$(pwd)/policy:/run/policy:ro" \
  secure-sql-mcp

快速入门(GHCR图像)

创建GitHub Release时会发布图像。每个版本都会同时推送版本标签(例如。 v0.1.0)以及 latest:

docker pull ghcr.io/jrhuerta/secure-sql-mcp:latest

使用env文件和只读挂载策略运行:

docker run -i --rm \
  --env-file .env \
  -v "$(pwd)/policy:/run/policy:ro" \
  ghcr.io/jrhuerta/secure-sql-mcp:latest

或者使用Docker Compose(从本地Dockerfile构建):

docker compose up --build

秘密最佳实践

  • 仅将凭据放入 .env (或你的秘密经理),永远不要在提示中。
  • 避免在shell历史记录中硬编码凭据。
  • 以只读方式装载策略文件(:ro)在Docker中。
  • 保持 .env 以及不受版本控制的策略文件。

开发工具

python -m pip install -e ".[dev]"   # or: uv pip install -e ".[dev]"
pre-commit install
pre-commit run --all-files
ruff check .
ruff format .
ty check
python -m pytest -q

安全测试套件

直接运行以安全为重点的套件:

python -m pytest -q \
  tests/test_mcp_interface.py \
  tests/test_query_validator_security.py \
  tests/test_mcp_stdio_security.py

这些套件验证了什么:

  • 变异/特权SQL操作的只读强制
  • 单语句验证和解析器强化
  • 默认情况下严格拒绝表/列ACL检查,包括联接/联合/子查询路径
  • MCP stdio传输上的协议级行为
  • 超时、行上限截断和非泄漏的可操作数据库错误响应

CI安全门期望

对于受保护的分支,将这些检查视为合并阻止程序:

ruff check .
ty check
python -m pytest -q \
  tests/test_mcp_interface.py \
  tests/test_query_validator_security.py \
  tests/test_mcp_stdio_security.py

建议政策:

  • 在上述安全套件中出现任何故障时进行块合并
  • 更改查询验证、策略解析或MCP工具响应时需要测试更新
  • 保持安全测试夹具的确定性(默认情况下没有共享状态,没有外部数据库依赖性)

贡献

  • 阅读 贡献.md 在打开PR之前。
  • 社区行为期望 代码_OF_CONDUCT.md.
  • 许可条款在 许可证.
  • 审查期望在以下方面得到执行 main:
    • 需要拉取请求
    • 至少1次批准审查
    • 所需的CI检查(Lint, Type, Test 和 Docker Build)
    • 需要线性历史记录
  • 安全报告应转到 安全.md 而不是公共问题。

安全快速审核清单

在合并安全敏感更改之前,请验证:

  • 查询验证仍然对每个请求强制执行一条语句
  • 变异/DDL/特权SQL操作被可操作的消息阻止
  • 默认情况下,表和列访问仍为拒绝 ALLOWED_POLICY_FILE
  • SELECT * 除非策略明确允许,否则将被拒绝 table:*
  • 多表查询仍然拒绝不合格的列,并强制执行别名感知ACL
  • 超时和行上限保护仍处于活动状态并经过测试
  • 数据库错误响应保持干净,不会暴露凭据/内部连接详细信息
  • 安全套件通行证:
    • tests/test_mcp_interface.py
    • tests/test_query_validator_security.py
    • tests/test_mcp_stdio_security.py

公开推出验证清单

合并工作流/文档更改后,请验证:

  • 存储库可见性为 Public
  • main 分支保护处于活动状态,需要:
    • 基于PR的合并
    • 1审批审核
    • 必要的检查 Lint, Type, Test 和 Docker Build
    • 线性历史,无强制推送,无删除
  • CI工作流在PR和推送上运行 main
  • 当GitHub发布时,GHCR镜像发布成功
  • GHCR牵引工作:
    • docker pull ghcr.io/jrhuerta/secure-sql-mcp:latest
  • 社区文档存在:
    • CONTRIBUTING.md
    • CODE_OF_CONDUCT.md
    • SECURITY.md
    • .github/ISSUE_TEMPLATE/*
    • .github/PULL_REQUEST_TEMPLATE.md

阻止消息示例

  • 突变被阻断:
    • This server is configured for read-only access. The operation 'UPDATE' is not permitted. If you need to modify data, please escalate to a human operator.
  • 策略阻止表:
    • Access to table 'secrets' is restricted by the server access policy. ...
  • 策略阻止列:
    • Access to column(s) ssn on table 'customers' is restricted. ...