Protocolul Context Model (MCP) oferă o modalitate puternică, standardizată pentru ca LLM-urile să interacționeze cu instrumente externe. How do you secure it? Expunerea unui server MCP fără securitate este ca și cum ai lăsa ușa din față a casei tale larg deschisă. Acest ghid vă va ghida prin securizarea unui server Node.js MCP de la zero folosind Vom acoperi autentificarea (cine ești tu?) și autorizarea (ce ai voie să faci?), cu eșantioane practice de cod bazate pe acest proiect care pot fi găsite la . JSON Web Tokens (JWT) Eșantioane Azure/mcp-container-ts Obiectivul: De la neprotejat la complet securizat Scopul nostru este de a lua un server MCP de bază și de a adăuga un strat de securitate robust care: 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 De ce JWT este perfect pentru securitatea MCP JWT este standardul industriei pentru securizarea API-urilor și este ideal pentru serverele MCP din câteva motive cheie: 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: Vizualizarea fluxului de securitate Pentru cursanții vizuali, această diagramă de secvență ilustrează fluxul complet de autentificare și autorizare: O notă privind conformitatea cu specificațiile MCP! Este important să rețineți că acest ghid oferă o implementare practică, reală pentru securizarea unui server MCP, dar nu Punerea în aplicare deplină a . not Specificații oficiale de autorizare MCP Această implementare se concentrează pe un model robust, fără stat și înțeles pe scară largă utilizând JWT-uri tradiționale și controlul accesului bazat pe roluri (RBAC), care este suficient pentru multe cazuri de utilizare. Vă recomandăm să vă uitaţi la pentru a rămâne la curent și pentru a primi notificări despre îmbunătățirile viitoare. GitHub Repositorie Pasul 1: Definirea rolurilor și a permisiunilor Înainte de a scrie orice cod, trebuie să definim regulile noastre de securitate. Ce roluri există? Ce poate face fiecare rol? Aceasta este baza sistemului nostru de autorizare. În noi Fişierul defineşte şi Acest lucru face codul nostru clar, lizibil și mai puțin predispus la tipărire. 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], }; Pasul 2: Crearea unui serviciu JWT Apoi, avem nevoie de un serviciu centralizat pentru a gestiona toată logica legată de JWT: crearea de noi jetoane pentru testare și, cel mai important, verificarea jetoanelor de intrare. Aici este completul Fişierul utilizează bibliotecă pentru a face ridicarea grea. 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."); } } Pasul 3: Construirea middleware-ului de autentificare Un "middleware" este o funcție care rulează este locul perfect pentru a pune controlul nostru de securitate. Acest middleware va inspecta fiecare cerere de intrare, caută un JWT în Header și verificați. Înainte Authorization Dacă tokenul este valabil, atașează informațiile utilizatorului la obiectul solicitării pentru utilizare ulterioară. greșeală și oprește cererea de a continua. 401 Unauthorized Pentru a face acest tip sigur, vom extinde, de asemenea, expresia Interfața noastră include Obiectul . 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, }); } } Pasul 4: Protejarea serverului MCP Acum avem toate piesele.Să le punem împreună pentru a ne proteja serverul. În primul rând, aplicăm intermediarului către principalul punct de terminare MCP în Acest lucru asigură Cerere pentru trebuie să aibă un JWT valabil. 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 Apoi, vom pune în aplicare permisiunile noastre fine-grain. Acţionează în O vom modifica pentru a verifica dacă utilizatorul autentificat are permisiunea înainte de a returna lista de instrumente. 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 } Cu această modificare, un utilizator cu o rolul poate lista instrumente, dar un utilizator fără Permisul de acces va fi refuzat. readonly LIST_TOOLS Concluzii și pași următori Ați implementat cu succes un nivel robust de autentificare și autorizare pentru serverul dvs. MCP. Urmând acești pași, aveți: Roluri și permisiuni clare. Creat un serviciu centralizat pentru gestionarea JWT. Construiți un middleware pentru a proteja toate solicitările primite. Permisiuni granulare forțate la nivelul instrumentului. De aici, puteți extinde aceste concepte prin adăugarea de mai multe roluri, mai multe permisiuni și chiar mai multă logică de afaceri complexă la sistemul de autorizare. Steaua noastră pentru a rămâne la curent și pentru a primi notificări despre îmbunătățirile viitoare. GitHub Repositorie