Modelio konteksto protokolas (MCP) suteikia galingą, standartizuotą būdą LLMs sąveikauti su išoriniais įrankiais. bet kai tik pereinate nuo vietinės demo prie realaus pasaulio paraiškos, kyla kritinis klausimas: How do you secure it? MCP serverio atskleidimas be saugumo yra tas pats, kas palikti savo namų duris plačiai atviras. Šis vadovas padės jums apsaugoti Node.js MCP serverį nuo pat pradžių, naudojant Mes apimsime autentifikavimą (kas esate jūs?) ir autorizaciją (ką jums leidžiama daryti?), su praktiniais kodo pavyzdžiais, pagrįstais šiuo projektu, kuriuos galima rasti . JSON Web Tokens (JWT) „Azure“ pavyzdžiai/mcp-container-ts Tikslas: nuo neapsaugoto iki visiškai apsaugoto Mūsų tikslas yra imtis pagrindinio MCP serverio ir pridėti tvirtą saugumo sluoksnį, kuris: 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 Kodėl JWT yra puikus MCP saugumui JWT yra pramonės standartas, skirtas apsaugoti API, ir jis idealiai tinka MCP serveriams dėl kelių pagrindinių priežasčių: 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: Vizualizuoti saugumo srautą Vizualiems besimokantiesiems ši sekos diagrama iliustruoja visą autentifikavimo ir autorizacijos srautą: Atkreipkite dėmesį į MCP specifikacijų laikymąsi! Svarbu pažymėti, kad šis vadovas suteikia praktinį, realaus pasaulio įgyvendinimą MCP serverio apsaugai, tačiau jis Visapusiškai įgyvendinti . not Oficiali MCP autorizacijos specifikacija Šiame įgyvendinime daugiausia dėmesio skiriama tvirtam, nepriklausomam ir plačiai suprantamam modeliui, naudojant tradicinius JWT ir vaidmenų pagrindu pagrįstą prieigos kontrolę (RBAC), kuris yra pakankamas daugeliui naudojimo atvejų. tačiau, norint visiškai laikytis MCP specifikacijos, jums reikės įgyvendinti papildomas funkcijas. Rekomenduojame žiūrėti į Būti atnaujintam ir gauti pranešimus apie būsimus patobulinimus. Github archyvas 1 žingsnis: vaidmenų ir leidimų apibrėžimas Prieš rašydami bet kokį kodą, turime apibrėžti savo saugumo taisykles. Kokie vaidmenys egzistuoja? Ką gali atlikti kiekvienas vaidmuo? Mūsų failą, mes apibrėžiame ir Tai daro mūsų kodą aiškų, įskaitomą ir mažiau linkęs į rašymą. 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], }; 2 žingsnis: sukurkite JWT paslaugą Toliau mums reikia centralizuotos paslaugos, kad galėtume tvarkyti visą su JWT susijusią logiką: kurti naujus žetonus bandymams ir, svarbiausia, tikrinti gaunamus žetonus. Štai ir pilnas archyvas. jis naudoja Knygos, skirtos sunkiam kėlimui 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."); } } 3 žingsnis: sukurkite autentifikavimo vidurinę programą "Middleware" yra funkcija, kuri veikia Tai puiki vieta įdėti mūsų saugumo patikrinimą. Ši tarpinė programinė įranga patikrins kiekvieną gaunamą užklausą, ieškokite JWT Header ir patikrinkite. Prieš Authorization Jei žymuo yra galiojantis, jis prideda naudotojo informaciją prie užklausos objekto vėlesniam naudojimui. klaida ir sustabdo prašymą tęsti toliau. 401 Unauthorized Kad šis tipas būtų saugus, mes taip pat išplėsime "Express" Įtraukti į mūsų sąsają Objektų 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, }); } } 4 žingsnis: MCP serverio apsauga Dabar mes turime visus gabalus. sudėkime juos kartu, kad apsaugotume savo serverį. Visų pirma, mes naudojame savo į pagrindinį MCP galinį tašką Tai užtikrina Prašymas į Turite turėti galiojantį JWT. 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 Tada mes vykdysime mūsų smulkiųjų grūdų leidimus. Veikia į Mes jį pakeisime, kad patikrintume, ar autentifikuotas vartotojas turi leidimą prieš grąžinant įrankių sąrašą. 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 } Su šiuo pakeitimu, vartotojas su funkcija gali išvardyti įrankius, tačiau vartotojas be Prieiga bus atsisakyta. readonly LIST_TOOLS Išvados ir tolesni žingsniai Jūs sėkmingai įgyvendinote tvirtą autentifikavimo ir autorizacijos sluoksnį savo MCP serveriui. Aiškiai apibrėžti vaidmenys ir leidimai. Sukurta centralizuota paslauga JWT tvarkymui. Sukurkite vidinę programinę įrangą, kad apsaugotumėte visus gaunamus prašymus. Priverstiniai granuliariniai leidimai įrankių lygiu. Iš čia galite išplėsti šias sąvokas, pridėdami daugiau vaidmenų, daugiau teisių ir dar sudėtingesnę verslo logiką į savo autorizacijos sistemą. Mūsų žvaigždė Būti atnaujintam ir gauti pranešimus apie būsimus patobulinimus. Github archyvas