4.4 KiB
4.4 KiB
MCP Read-Only Security Guide
Current Security Measures
The mssql-mcp-readonly.js file implements multiple layers of read-only enforcement:
1. Query Validation (Application Layer)
- ✅ Only SELECT queries allowed
- ✅ Blocks INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, TRUNCATE
- ✅ Blocks EXEC/EXECUTE (prevents stored procedure execution)
- ✅ Blocks SP_/XP_ (prevents system stored procedures)
- ✅ Blocks MERGE, GRANT, REVOKE, DENY
- ✅ Prevents query chaining with semicolons
2. Recommended: Database User Permissions (Database Layer)
For maximum security, create a read-only database user:
-- Create a read-only user
CREATE LOGIN mcp_readonly WITH PASSWORD = 'YourSecurePassword123!';
USE Infra;
CREATE USER mcp_readonly FOR LOGIN mcp_readonly;
-- Grant only SELECT permission
GRANT SELECT ON SCHEMA::dbo TO mcp_readonly;
-- Or grant SELECT on specific tables only:
GRANT SELECT ON dbo.SDS_Teile TO mcp_readonly;
GRANT SELECT ON dbo.SDS_Teile_CA TO mcp_readonly;
GRANT SELECT ON dbo.SDS_Teile_CS TO mcp_readonly;
GRANT SELECT ON dbo.SDS_Teile_LC TO mcp_readonly;
GRANT SELECT ON dbo.SDS_Teile_ME TO mcp_readonly;
GRANT SELECT ON dbo.SDS_Teile_MK TO mcp_readonly;
GRANT SELECT ON dbo.SDS_Teile_MS TO mcp_readonly;
GRANT SELECT ON dbo.SDS_Teile_WT TO mcp_readonly;
GRANT SELECT ON dbo.SDS_Teile_EL TO mcp_readonly;
-- Explicitly deny write permissions (extra safety)
DENY INSERT, UPDATE, DELETE, ALTER ON SCHEMA::dbo TO mcp_readonly;
3. Update MCP Configuration
After creating the read-only user, update ~/.cursor/mcp.json:
{
"mcpServers": {
"mssql-mcp-readonly": {
"command": "node ",
"args": ["/home/sdsadmin/infraviewer/mssql-mcp-readonly.js"],
"env": {
"MSSQL_SERVER": "192.168.120.88",
"MSSQL_PORT": "1433",
"MSSQL_USER": "mcp_readonly",
"MSSQL_PASSWORD": "YourSecurePassword123!",
"MSSQL_DATABASE": "Infra",
"MSSQL_ENCRYPT": "true",
"MSSQL_TRUST_SERVER_CERTIFICATE": "true"
}
}
}
}
Security Layers
| Layer | Protection | Bypassed if... |
|---|---|---|
| Application Validation | Keyword blocking | Code has bugs |
| Database Permissions | SQL Server enforced | Wrong credentials used |
| Both Combined | Double protection | Nearly impossible ✅ |
Testing Read-Only Access
Test 1: Valid SELECT (should work)
SELECT TOP 10 * FROM SDS_Teile
Test 2: Invalid INSERT (should fail)
INSERT INTO SDS_Teile (Teil) VALUES ('TEST')
Expected: Error: "Forbidden keyword detected: INSERT"
Test 3: Query Chaining (should fail)
SELECT 1; DROP TABLE SDS_Teile
Expected: Error: "Multiple statements not allowed"
Test 4: Stored Procedure (should fail)
EXEC sp_helpdb
Expected: Error: "Forbidden keyword detected: EXEC"
Current vs Recommended Setup
Current Setup (Application Layer Only)
[AI] → [mssql-mcp-readonly.js] → [SQL Server]
↑ Validates queries ↑ Full permissions
Risk: If validation code has a bug, data could be modified
Recommended Setup (Defense in Depth)
[AI] → [mssql-mcp-readonly.js] → [SQL Server with read-only user]
↑ Validates queries ↑ SELECT-only permissions
Benefit: Even if validation fails, SQL Server will block write operations
Monitoring
Check MCP server logs for blocked attempts:
# If running as systemd service
journalctl -u mcp-readonly -f
# If running manually
tail -f /var/log/mcp-readonly.log
Best Practices
- ✅ Use dedicated read-only database user
- ✅ Keep validation keywords up to date
- ✅ Monitor for failed query attempts
- ✅ Regularly audit database permissions
- ✅ Use strong passwords for database users
- ✅ Enable SQL Server audit logging
- ✅ Limit network access to database server
- ✅ Keep mssql package updated (
npm update mssql)
Additional Hardening
1. Add Query Size Limits
Prevent resource exhaustion:
function validateQuery(query) {
if (query.length > 10000) {
throw new Error("Query too long!");
}
// ... rest of validation
}
2. Add Rate Limiting
Prevent abuse:
const rateLimit = require('express-rate-limit');
// Limit to 100 queries per minute
3. Add Query Logging
Track all queries:
console.log(`[${new Date().toISOString()}] Query: ${query.substring(0, 100)}...`);