Giao thức mô hình ngữ cảnh (MCP) cung cấp một cách mạnh mẽ, tiêu chuẩn hóa cho LLM để tương tác với các công cụ bên ngoài. nhưng ngay khi bạn chuyển từ một bản demo địa phương sang một ứng dụng thế giới thực, một câu hỏi quan trọng phát sinh: How do you secure it? Để lộ một máy chủ MCP mà không có bảo mật giống như để cửa trước của ngôi nhà của bạn mở rộng. bất cứ ai cũng có thể bước vào và sử dụng các công cụ của bạn, truy cập dữ liệu của bạn, hoặc gây ra sự tàn phá. Hướng dẫn này sẽ hướng dẫn bạn qua việc bảo mật một máy chủ Node.js MCP từ đầu bằng cách sử dụng Chúng tôi sẽ bao gồm xác thực (bạn là ai?) và ủy quyền (bạn được phép làm gì?), với các mẫu mã thực tế dựa trên dự án này có thể được tìm thấy tại . JSON Web Tokens (JWT) Mẫu Azure/mcp-container-ts Mục tiêu: Từ không được bảo vệ đến hoàn toàn an toàn Mục tiêu của chúng tôi là có một máy chủ MCP cơ bản và thêm một lớp bảo mật mạnh mẽ mà: every request to ensure it comes from a known user. Authenticates the user, granting them specific permissions based on their role (e.g., vs. ). Authorizes admin readonly individual tools, so only authorized users can access them. Protects Tại sao JWT là hoàn hảo cho MCP Security JWT là tiêu chuẩn công nghiệp để bảo mật API, và nó phù hợp lý tưởng cho các máy chủ MCP vì một vài lý do chính: Each JWT contains all the information needed to verify a user. The server doesn't need to store session information, which makes it highly scalable—perfect for handling many concurrent requests from AI agents. Stateless: A JWT can carry user details, their role, and specific permissions directly within its payload. Self-Contained: JWTs are digitally signed. If a token is modified in any way, the signature becomes invalid, and the server will reject it. Tamper-Proof: A single JWT can be used to access multiple secured services, which is common in microservice architectures. Portable: Tầm nhìn dòng chảy bảo mật Đối với người học trực quan, sơ đồ chuỗi này minh họa dòng xác thực và ủy quyền đầy đủ: Một lưu ý về MCP Specification Compliance! Điều quan trọng cần lưu ý là hướng dẫn này cung cấp một triển khai thực tế, thực tế để bảo mật một máy chủ MCP, nhưng nó không Thực hiện đầy đủ các . not Thông số kỹ thuật chính thức của MCP Authorization Việc thực hiện này tập trung vào một mô hình mạnh mẽ, không có trạng thái và được hiểu rộng rãi bằng cách sử dụng JWT truyền thống và kiểm soát truy cập dựa trên vai trò (RBAC), đủ cho nhiều trường hợp sử dụng. Tuy nhiên, để tuân thủ đầy đủ với thông số kỹ thuật MCP, bạn sẽ cần thực hiện các tính năng bổ sung. Trong một bài viết trong tương lai, chúng tôi có thể khám phá cách mở rộng thực hiện JWT của chúng tôi để hoàn toàn phù hợp với thông số kỹ thuật MCP. Chúng tôi khuyên bạn nên nhìn vào để cập nhật và nhận thông báo về những cải tiến trong tương lai. Lưu trữ GitHub Bước 1: Xác định vai trò và quyền Trước khi viết bất kỳ mã nào, chúng ta phải xác định các quy tắc bảo mật của mình. Những vai trò nào tồn tại? Mỗi vai trò có thể làm gì? Đây là nền tảng của hệ thống ủy quyền của chúng tôi. Trong chúng ta File, chúng tôi định nghĩa và Điều này làm cho mã của chúng tôi rõ ràng, dễ đọc và ít dễ viết hơn. src/auth/authorization.ts UserRole Permission // src/auth/authorization.ts export enum UserRole { ADMIN = "admin", USER = "user", READONLY = "readonly", } export enum Permission { CREATE_TODOS = "create:todos", READ_TODOS = "read:todos", UPDATE_TODOS = "update:todos", DELETE_TODOS = "delete:todos", LIST_TOOLS = "list:tools", } // This interface defines the structure of our authenticated user export interface AuthenticatedUser { id: string; role: UserRole; permissions: Permission[]; } // A simple map to assign default permissions to each role const rolePermissions: Record<UserRole, Permission[]> = { [UserRole.ADMIN]: Object.values(Permission), // Admin gets all permissions [UserRole.USER]: [ Permission.CREATE_TODOS, Permission.READ_TODOS, Permission.UPDATE_TODOS, Permission.LIST_TOOLS, ], [UserRole.READONLY]: [Permission.READ_TODOS, Permission.LIST_TOOLS], }; Bước 2: Tạo dịch vụ JWT Tiếp theo, chúng ta cần một dịch vụ tập trung để xử lý tất cả các logic liên quan đến JWT: tạo token mới để kiểm tra và, quan trọng nhất, xác minh các token đến. Đây là toàn bộ file. nó sử dụng thư viện để thực hiện việc nâng nặng. src/auth/jwt.ts jsonwebtoken // src/auth/jwt.ts import * as jwt from "jsonwebtoken"; import { AuthenticatedUser, getPermissionsForRole, UserRole, } from "./authorization.js"; // These values should come from environment variables for security const JWT_SECRET = process.env.JWT_SECRET!; const JWT_AUDIENCE = process.env.JWT_AUDIENCE!; const JWT_ISSUER = process.env.JWT_ISSUER!; const JWT_EXPIRY = process.env.JWT_EXPIRY || "2h"; if (!JWT_SECRET || !JWT_AUDIENCE || !JWT_ISSUER) { throw new Error("JWT environment variables are not set!"); } /** * Generates a new JWT for a given user payload. * Useful for testing or generating tokens on demand. */ export function generateToken( user: Partial<AuthenticatedUser> & { id: string } ): string { const payload = { id: user.id, role: user.role || UserRole.USER, permissions: user.permissions || getPermissionsForRole(user.role || UserRole.USER), }; return jwt.sign(payload, JWT_SECRET, { algorithm: "HS256", expiresIn: JWT_EXPIRY, audience: JWT_AUDIENCE, issuer: JWT_ISSUER, }); } /** * Verifies an incoming JWT and returns the authenticated user payload if valid. */ export function verifyToken(token: string): AuthenticatedUser { try { const decoded = jwt.verify(token, JWT_SECRET, { algorithms: ["HS256"], audience: JWT_AUDIENCE, issuer: JWT_ISSUER, }) as jwt.JwtPayload; // Ensure the decoded token has the fields we expect if (typeof decoded.id !== "string" || typeof decoded.role !== "string") { throw new Error("Token payload is missing required fields."); } return { id: decoded.id, role: decoded.role as UserRole, permissions: decoded.permissions || [], }; } catch (error) { // Log the specific error for debugging, but return a generic message console.error("JWT verification failed:", error.message); if (error instanceof jwt.TokenExpiredError) { throw new Error("Token has expired."); } if (error instanceof jwt.JsonWebTokenError) { throw new Error("Invalid token."); } throw new Error("Could not verify token."); } } Bước 3: Xây dựng Middleware xác thực Một "middleware" là một chức năng chạy quản lý yêu cầu chính của bạn. Đó là nơi hoàn hảo để đặt kiểm tra bảo mật của chúng tôi. middleware này sẽ kiểm tra mọi yêu cầu đến, tìm kiếm một JWT trong header, và kiểm tra nó. trước Authorization Nếu token là hợp lệ, nó đính kèm thông tin của người dùng vào đối tượng yêu cầu để sử dụng sau này. lỗi và ngăn chặn yêu cầu tiếp tục. 401 Unauthorized Để làm cho loại này an toàn, chúng tôi cũng sẽ mở rộng Express Interface để bao gồm của chúng tôi đối tượng Request user // src/server-middlewares.ts import { Request, Response, NextFunction } from "express"; import { verifyToken, AuthenticatedUser } from "./auth/jwt.js"; // Extend the global Express Request interface to add our custom 'user' property declare global { namespace Express { interface Request { user?: AuthenticatedUser; } } } export function authenticateJWT( req: Request, res: Response, next: NextFunction ): void { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { res.status(401).json({ error: "Authentication required", message: "Authorization header with 'Bearer' scheme must be provided.", }); return; } const token = authHeader.substring(7); // Remove "Bearer " try { const userPayload = verifyToken(token); req.user = userPayload; // Attach user payload to the request next(); // Proceed to the next middleware or request handler } catch (error) { res.status(401).json({ error: "Invalid token", message: error.message, }); } } Bước 4: Bảo vệ máy chủ MCP Bây giờ chúng ta có tất cả các mảnh.Chúng ta hãy đặt chúng lại với nhau để bảo vệ máy chủ của chúng ta. Đầu tiên, chúng tôi áp dụng middleware đến điểm cuối MCP chính trong Điều này đảm bảo Yêu cầu to Phải có một JWT hợp lệ. authenticateJWT src/index.ts every /mcp // src/index.ts // ... other imports import { authenticateJWT } from "./server-middlewares.js"; // ... const MCP_ENDPOINT = "/mcp"; const app = express(); // Apply security middleware ONLY to the MCP endpoint app.use(MCP_ENDPOINT, authenticateJWT); // ... rest of the file Tiếp theo, chúng tôi sẽ thực thi các giấy phép hạt mịn của chúng tôi. Hành động trong Chúng tôi sẽ sửa nó để kiểm tra xem người dùng đã xác thực có quyền trước khi trả về danh sách công cụ. ListTools src/server.ts Permission.LIST_TOOLS // src/server.ts // ... other imports import { hasPermission, Permission } from "./auth/authorization.js"; // ... inside the StreamableHTTPServer class private setupServerRequestHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async (request) => { // The user is attached to the request by our middleware const user = this.currentUser; // 1. Check for an authenticated user if (!user) { return this.createRPCErrorResponse("Authentication required."); } // 2. Check if the user has the specific permission to list tools if (!hasPermission(user, Permission.LIST_TOOLS)) { return this.createRPCErrorResponse( "Insufficient permissions to list tools." ); } // 3. If checks pass, filter tools based on user's permissions const allowedTools = TodoTools.filter((tool) => { const requiredPermissions = this.getToolRequiredPermissions(tool.name); // The user must have at least one of the permissions required for the tool return requiredPermissions.some((p) => hasPermission(user, p)); }); return { jsonrpc: "2.0", tools: allowedTools, }; }); // ... other request handlers } Với sự thay đổi này, một người dùng với một Role có thể liệt kê các công cụ, nhưng một người dùng không có Quyền truy cập sẽ bị từ chối. readonly LIST_TOOLS Kết luận và các bước tiếp theo Chúc mừng bạn! Bạn đã thực hiện thành công một lớp xác thực và ủy quyền mạnh mẽ cho máy chủ MCP của bạn. Bằng cách làm theo các bước này, bạn có: Xác định các vai trò và quyền rõ ràng. Tạo một dịch vụ tập trung để xử lý JWTs. Xây dựng một middleware để bảo vệ tất cả các yêu cầu đến. Các quyền granular được áp dụng ở cấp độ công cụ. Máy chủ MCP của bạn không còn là một cửa mở - đó là một dịch vụ an toàn. Từ đây, bạn có thể mở rộng các khái niệm này bằng cách thêm nhiều vai trò, nhiều quyền hơn và logic kinh doanh phức tạp hơn vào hệ thống ủy quyền của bạn. ngôi sao của chúng tôi để cập nhật và nhận thông báo về những cải tiến trong tương lai. Lưu trữ GitHub