Bảo mật MCP Server — Authentication, Authorization và Best Practices
Điểm nổi bật
Nhấn để đến mục tương ứng
- 1 Idempotency: Tool ghi dữ liệu nên idempotent — gọi nhiều lần cho cùng kết quả.
- 2 Bài viết này trình bày mô hình bảo mật MCP và các biện pháp cần áp dụng cho mọi môi trường, đặc biệt là production.
- 3 Output filtering: Loại bỏ dữ liệu nhạy cảm khỏi kết quả trước khi trả về Claude.
- 4 Backup audit logs và verify khả năng restore Bước tiếp theo Bảo mật MCP Server là quá trình liên tục, không phải cấu hình một lần.
- 5 Một MCP Server thiếu bảo mật có thể trở thành cánh cửa cho tấn công: rò rỉ dữ liệu, thực thi mã độc, leo thang quyền.
Model Context Protocol (MCP) mở ra khả năng mạnh mẽ cho Claude khi kết nối với hệ thống bên ngoài — database, file system, API, dịch vụ cloud. Nhưng sức mạnh đó đi kèm rủi ro bảo mật nghiêm trọng nếu không được kiểm soát. Một MCP Server thiếu bảo mật có thể trở thành cánh cửa cho tấn công: rò rỉ dữ liệu, thực thi mã độc, leo thang quyền. Bài viết này trình bày mô hình bảo mật MCP và các biện pháp cần áp dụng cho mọi môi trường, đặc biệt là production.
Mô hình bảo mật MCP
MCP sử dụng mô hình bảo mật nhiều lớp:
- Transport layer: Bảo mật kênh truyền dữ liệu giữa MCP client (Claude) và MCP server
- Authentication: Xác minh danh tính của client kết nối đến server
- Authorization: Kiểm soát client được phép làm gì (tool nào, resource nào)
- Tool sandboxing: Giới hạn phạm vi tác động của từng tool
- Data protection: Bảo vệ dữ liệu nhạy cảm không bị rò rỉ qua LLM
Threat model cho MCP
Trước khi triển khai bảo mật, cần hiểu các mối đe dọa chính:
- Prompt injection: Kẻ tấn công chèn hướng dẫn độc hại vào dữ liệu mà Claude xử lý, khiến Claude gọi tool với tham số nguy hiểm
- Data exfiltration: Dữ liệu nhạy cảm từ hệ thống nội bộ bị gửi ra ngoài qua phản hồi của LLM
- Privilege escalation: Kẻ tấn công lợi dụng quyền của MCP Server để truy cập tài nguyên vượt phạm vi cho phép
- Denial of service: Query hoặc thao tác nặng làm sập hệ thống backend
- Man-in-the-middle: Chặn bắt dữ liệu truyền giữa client và server khi dùng transport không mã hóa
Bảo mật Transport: stdio vs SSE vs Streamable HTTP
MCP hỗ trợ nhiều phương thức truyền tải (transport). Mỗi phương thức có đặc điểm bảo mật khác nhau:
stdio (Standard I/O)
MCP Server chạy như một process con, giao tiếp qua stdin/stdout.
- Ưu điểm: Bảo mật cao nhất vì dữ liệu không đi qua mạng. Không cần cấu hình TLS. Process chạy với quyền của user hiện tại.
- Hạn chế: Chỉ hoạt động trên cùng máy. Không chia sẻ được giữa nhiều client.
- Phù hợp: Development, sử dụng cá nhân trên máy local
SSE (Server-Sent Events)
Giao tiếp qua HTTP với SSE cho streaming.
- Ưu điểm: Hỗ trợ remote, nhiều client kết nối cùng lúc
- Hạn chế: Cần cấu hình HTTPS/TLS. Cần authentication riêng. SSE chỉ hỗ trợ giao tiếp một chiều (server-to-client), cần kết hợp với HTTP POST cho chiều ngược lại.
- Phù hợp: Triển khai nội bộ trong mạng tin cậy
Streamable HTTP
Transport mới nhất, sử dụng HTTP thuần với khả năng streaming.
- Ưu điểm: Linh hoạt nhất, hoạt động qua firewall và proxy. Hỗ trợ HTTP/2 và giao tiếp hai chiều. Dễ tích hợp với hạ tầng web hiện có (load balancer, CDN, WAF).
- Hạn chế: Cần cấu hình bảo mật đầy đủ: TLS, authentication, CORS
- Phù hợp: Production, triển khai từ xa, multi-tenant
Cấu hình TLS cho remote transport
// MCP Server với HTTPS (Streamable HTTP)
const https = require("https");
const fs = require("fs");
const { McpServer } = require("@modelcontextprotocol/sdk/server/mcp.js");
const { StreamableHTTPServerTransport } =
require("@modelcontextprotocol/sdk/server/streamableHttp.js");
const server = new McpServer({
name: "secure-mcp",
version: "1.0.0"
});
// TLS configuration
const httpsOptions = {
key: fs.readFileSync("/path/to/private-key.pem"),
cert: fs.readFileSync("/path/to/certificate.pem"),
ca: fs.readFileSync("/path/to/ca-bundle.pem"),
minVersion: "TLSv1.3" // Chỉ cho phép TLS 1.3
};
const httpsServer = https.createServer(httpsOptions, app);
httpsServer.listen(8443);
Authentication: Xác thực danh tính
API Key Authentication
Phương pháp đơn giản nhất, phù hợp cho sử dụng nội bộ hoặc ít client:
// Middleware xác thực API key
function authenticateApiKey(req, res, next) {
const apiKey = req.headers["x-api-key"];
if (!apiKey) {
return res.status(401).json({ error: "Missing API key" });
}
// So sánh với danh sách key hợp lệ
// Lưu ý: dùng timing-safe comparison để chống timing attack
const crypto = require("crypto");
const validKeys = process.env.MCP_API_KEYS.split(",");
const isValid = validKeys.some(key =>
crypto.timingSafeEqual(
Buffer.from(apiKey),
Buffer.from(key)
)
);
if (!isValid) {
return res.status(403).json({ error: "Invalid API key" });
}
// Gán thông tin client cho request
req.clientId = getClientIdFromKey(apiKey);
next();
}
OAuth 2.1 Authentication
MCP specification khuyến nghị OAuth 2.1 cho xác thực trong môi trường production, đặc biệt với remote transport. OAuth 2.1 cải tiến so với OAuth 2.0:
- Bắt buộc PKCE (Proof Key for Code Exchange) cho mọi client
- Loại bỏ implicit grant flow (kém bảo mật)
- Bắt buộc exact redirect URI matching
- Refresh token phải là one-time use hoặc sender-constrained
// OAuth 2.1 middleware cho MCP Server
const { verifyAccessToken } = require("./auth/oauth");
async function authenticateOAuth(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res.status(401).json({
error: "unauthorized",
error_description: "Bearer token required"
});
}
const token = authHeader.split(" ")[1];
try {
const tokenInfo = await verifyAccessToken(token);
// Kiểm tra token chưa hết hạn
if (tokenInfo.exp < Date.now() / 1000) {
return res.status(401).json({
error: "token_expired",
error_description: "Access token has expired"
});
}
// Kiểm tra scope
req.clientId = tokenInfo.client_id;
req.scopes = tokenInfo.scope.split(" ");
next();
} catch (error) {
return res.status(403).json({
error: "invalid_token",
error_description: "Token verification failed"
});
}
}
Authorization: Phân quyền tool
Không phải mọi client đều được phép sử dụng mọi tool. Hệ thống phân quyền cần kiểm soát:
// Bảng phân quyền theo role
const PERMISSIONS = {
analyst: {
tools: ["list_tables", "describe_table", "run_query"],
allowedTables: ["orders", "products", "categories"],
maxRowsPerQuery: 1000
},
support: {
tools: ["search_customer", "get_order_status"],
allowedTables: ["customers", "orders"],
maxRowsPerQuery: 50
},
admin: {
tools: ["*"], // Tất cả tools
allowedTables: ["*"],
maxRowsPerQuery: 5000
}
};
function authorizeToolCall(clientId, toolName, params) {
const role = getClientRole(clientId);
const perms = PERMISSIONS[role];
if (!perms) {
return { allowed: false, reason: "Unknown role" };
}
// Kiểm tra tool được phép
if (perms.tools[0] !== "*" && !perms.tools.includes(toolName)) {
return {
allowed: false,
reason: "Tool " + toolName + " không được phép cho role " + role
};
}
return { allowed: true, limits: perms };
}
Tool Permission Sandboxing
Mỗi tool cần được sandbox — giới hạn phạm vi tác động để giảm thiểu rủi ro khi bị khai thác:
Nguyên tắc thiết kế tool an toàn
- Least privilege: Mỗi tool chỉ có quyền tối thiểu cần thiết. Tool đọc dữ liệu không có quyền ghi. Tool ghi dữ liệu chỉ ghi vào bảng cụ thể.
- Input validation: Validate mọi tham số đầu vào. Dùng schema validation (zod, joi) thay vì kiểm tra thủ công.
- Output filtering: Loại bỏ dữ liệu nhạy cảm khỏi kết quả trước khi trả về Claude.
- Rate limiting: Giới hạn tần suất gọi tool để chống abuse.
- Idempotency: Tool ghi dữ liệu nên idempotent — gọi nhiều lần cho cùng kết quả.
// Ví dụ sandbox cho file system tool
const path = require("path");
const SANDBOX_ROOT = "/data/shared";
const BLOCKED_EXTENSIONS = [".env", ".key", ".pem", ".p12", ".pfx"];
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
function validateFilePath(filePath) {
// Resolve path tuyệt đối
const resolved = path.resolve(SANDBOX_ROOT, filePath);
// Kiểm tra path traversal (../../etc/passwd)
if (!resolved.startsWith(SANDBOX_ROOT)) {
throw new Error("Access denied: path outside sandbox");
}
// Kiểm tra extension nhạy cảm
const ext = path.extname(resolved).toLowerCase();
if (BLOCKED_EXTENSIONS.includes(ext)) {
throw new Error("Access denied: sensitive file type");
}
return resolved;
}
Ngăn chặn rò rỉ dữ liệu (Data Exfiltration Prevention)
Một rủi ro quan trọng khi kết nối LLM với hệ thống nội bộ: dữ liệu nhạy cảm có thể bị rò rỉ qua phản hồi của Claude. Các biện pháp phòng ngừa:
Lọc dữ liệu nhạy cảm trước khi trả về
// Danh sách pattern dữ liệu nhạy cảm cần masking
const SENSITIVE_PATTERNS = [
{
name: "credit_card",
regex: /d{4}[s-]?d{4}[s-]?d{4}[s-]?d{4}/g,
mask: "****-****-****-XXXX"
},
{
name: "phone_vn",
regex: /(0[3-9]d{8})/g,
mask: (match) => match.slice(0, 4) + "***" + match.slice(-3)
},
{
name: "email",
regex: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Z]{2,}/gi,
mask: (match) => {
const [local, domain] = match.split("@");
return local.slice(0, 2) + "***@" + domain;
}
},
{
name: "cmnd_cccd",
regex: /d{9}(d{3})?/g,
mask: "***XXXXXX"
}
];
function sanitizeOutput(data) {
let text = typeof data === "string" ? data : JSON.stringify(data);
for (const pattern of SENSITIVE_PATTERNS) {
text = text.replace(pattern.regex, (match) =>
typeof pattern.mask === "function" ? pattern.mask(match) : pattern.mask
);
}
return text;
}
Kiểm soát dữ liệu đầu ra theo column
// Loại bỏ column nhạy cảm khỏi kết quả query
const SENSITIVE_COLUMNS = [
"password", "password_hash", "secret",
"api_key", "token", "ssn", "credit_card",
"cmnd", "cccd", "bank_account"
];
function filterSensitiveColumns(rows) {
return rows.map(row => {
const filtered = { ...row };
for (const col of SENSITIVE_COLUMNS) {
if (col in filtered) {
filtered[col] = "[REDACTED]";
}
}
return filtered;
});
}
Audit Logging
Ghi log chi tiết mọi hoạt động qua MCP Server để phát hiện bất thường và phục vụ forensics:
// Structured audit logging
const winston = require("winston");
const auditLogger = winston.createLogger({
level: "info",
format: winston.format.json(),
defaultMeta: { service: "mcp-server" },
transports: [
new winston.transports.File({
filename: "audit.log",
maxsize: 50 * 1024 * 1024, // 50MB
maxFiles: 30,
tailable: true
})
]
});
function logToolCall(clientId, toolName, params, result, duration) {
auditLogger.info("tool_call", {
timestamp: new Date().toISOString(),
clientId: clientId,
tool: toolName,
params: sanitizeParams(params), // Không log password/secret
resultSize: JSON.stringify(result).length,
rowCount: result.rowCount || null,
durationMs: duration,
success: true
});
}
function logToolError(clientId, toolName, params, error) {
auditLogger.error("tool_error", {
timestamp: new Date().toISOString(),
clientId: clientId,
tool: toolName,
params: sanitizeParams(params),
error: error.message,
stack: error.stack
});
}
// Loại bỏ giá trị nhạy cảm trong params trước khi log
function sanitizeParams(params) {
const safe = { ...params };
const sensitiveKeys = ["password", "secret", "token", "key"];
for (const key of sensitiveKeys) {
if (key in safe) safe[key] = "[REDACTED]";
}
return safe;
}
Production Hardening Checklist
Checklist bảo mật trước khi triển khai MCP Server lên production:
Transport Security
- Sử dụng TLS 1.3 cho mọi remote transport
- Certificate từ CA tin cậy (Let's Encrypt hoặc internal CA)
- Cấu hình HSTS header nếu dùng HTTP transport
- Disable TLS 1.0, 1.1 và các cipher suite yếu
Authentication
- Triển khai OAuth 2.1 với PKCE cho multi-client
- API key rotation policy (đổi key mỗi 90 ngày)
- Không hardcode credentials trong source code
- Sử dụng secret manager (HashiCorp Vault, AWS Secrets Manager)
Authorization
- Phân quyền theo role cho từng client
- Whitelist tool thay vì blacklist
- Giới hạn scope truy cập dữ liệu
- Review quyền định kỳ (quarterly access review)
Tool Security
- Input validation cho mọi tham số tool
- Output sanitization loại bỏ dữ liệu nhạy cảm
- Rate limiting cho từng tool và từng client
- Timeout cho mọi thao tác có thể chạy lâu
- Sandbox file system access
Monitoring
- Structured audit logging cho mọi tool call
- Alert khi phát hiện pattern bất thường (query đến bảng nhạy cảm, tần suất bất thường, lỗi xác thực liên tiếp)
- Dashboard theo dõi số lượng request, latency, error rate
- Log retention tối thiểu 90 ngày
Infrastructure
- Chạy MCP Server trong container với non-root user
- Network isolation: MCP Server chỉ kết nối đến các service cần thiết
- Không expose MCP Server ra public internet nếu không cần thiết
- Regular security updates cho dependencies
- Dependency scanning (npm audit, Snyk)
Cấu hình rate limiting
// Rate limiting middleware
const rateLimit = require("express-rate-limit");
// Giới hạn chung: 100 request/phút
const generalLimiter = rateLimit({
windowMs: 60 * 1000,
max: 100,
message: { error: "Rate limit exceeded. Try again later." }
});
// Giới hạn cho tool nhạy cảm: 10 request/phút
const sensitiveLimiter = rateLimit({
windowMs: 60 * 1000,
max: 10,
keyGenerator: (req) => req.clientId,
message: { error: "Sensitive tool rate limit exceeded." }
});
Phòng chống Prompt Injection qua dữ liệu
Một rủi ro đặc thù với MCP: dữ liệu trả về từ tool có thể chứa hướng dẫn độc hại. Ví dụ, kẻ tấn công lưu prompt injection vào tên sản phẩm trong database. Khi Claude đọc dữ liệu này, có thể bị điều khiển.
Biện pháp phòng ngừa
// Sanitize dữ liệu trả về từ tool trước khi gửi cho Claude
function sanitizeToolOutput(data) {
const text = typeof data === "string" ? data : JSON.stringify(data);
// Loại bỏ các pattern prompt injection phổ biến
const suspiciousPatterns = [
/ignores+(previous|above|all)s+instructions/gi,
/yous+ares+nows+/gi,
/systems*:s*/gi,
/[INST]/gi,
/<system>/gi,
/forgets+everything/gi
];
let cleaned = text;
for (const pattern of suspiciousPatterns) {
if (pattern.test(cleaned)) {
// Log cảnh báo
auditLogger.warn("potential_prompt_injection", {
timestamp: new Date().toISOString(),
pattern: pattern.source,
data_preview: cleaned.substring(0, 200)
});
// Escape thay vì loại bỏ để giữ nguyên nội dung hiển thị
cleaned = cleaned.replace(pattern, (match) =>
"[FILTERED: " + match.length + " chars]"
);
}
}
return cleaned;
}
Nguyên tắc defense in depth
- Không tin tưởng dữ liệu từ bất kỳ nguồn nào — kể cả database nội bộ
- Đặt giới hạn kích thước output từ tool (ví dụ: tối đa 50KB)
- Phân tách quyền: tool đọc dữ liệu không có quyền thực thi hành động nguy hiểm
- Human-in-the-loop: Yêu cầu người dùng xác nhận trước khi Agent thực hiện thao tác nhạy cảm (gửi email, xóa dữ liệu, chuyển tiền)
Triển khai MCP Server trong container
Chạy MCP Server trong Docker container giúp cô lập và kiểm soát tốt hơn:
# Dockerfile cho MCP Server
FROM node:20-slim
# Chạy với non-root user
RUN groupadd -r mcp && useradd -r -g mcp mcp
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN chown -R mcp:mcp /app
USER mcp
# Không expose port nếu dùng stdio transport
# EXPOSE 8443
CMD ["node", "server.js"]
# docker-compose.yml
version: "3.8"
services:
mcp-server:
build: .
restart: unless-stopped
environment:
- DB_HOST=db
- DB_PORT=5432
- DB_NAME=mydb
- DB_USER=mcp_readonly
- DB_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_password
networks:
- mcp-network
deploy:
resources:
limits:
memory: 256M
cpus: "0.5"
secrets:
db_password:
file: ./secrets/db_password.txt
networks:
mcp-network:
driver: bridge
Kiểm tra bảo mật định kỳ
Ngoài cấu hình ban đầu, cần thực hiện kiểm tra bảo mật định kỳ:
Checklist kiểm tra bảo mật MCP Server hàng tháng:
1. Review audit logs: có pattern bất thường không?
- Query đến bảng/cột nhạy cảm
- Số lượng request tăng đột biến
- Lỗi xác thực liên tiếp từ cùng IP
2. Kiểm tra dependencies: npm audit, cập nhật bản vá bảo mật
3. Rotate API keys và review danh sách client được phép
4. Kiểm tra certificate TLS còn hạn (tự động với certbot)
5. Review và cập nhật danh sách SENSITIVE_COLUMNS
và BLOCKED_TABLES khi schema thay đổi
6. Test thử tấn công: prompt injection, path traversal,
SQL injection qua tham số tool
7. Backup audit logs và verify khả năng restore
Bước tiếp theo
Bảo mật MCP Server là quá trình liên tục, không phải cấu hình một lần. Hãy bắt đầu với các biện pháp cơ bản (read-only user, input validation, audit logging), sau đó nâng cấp dần lên OAuth 2.1, role-based access control và monitoring tự động. Khi triển khai cho tổ chức lớn, cân nhắc xây dựng MCP Gateway tập trung để quản lý nhiều MCP Server từ một điểm. Tham khảo thêm tại Thư viện Nâng cao Claude.
Bai viet co huu ich khong?
Bản quyền thuộc về tác giả. Vui lòng dẫn nguồn khi chia sẻ.







