Skip to content

Security: aashari/boilerplate-mcp-server

SECURITY.md

Security Documentation

Overview

This document outlines the security measures implemented in boilerplate-mcp-server and provides guidance for secure deployment.


Implemented Security Measures

1. DNS Rebinding Protection ✅

Implementation: Origin header validation middleware (src/index.ts)

// Validates Origin header on all HTTP requests
app.use((req, res, next) => {
    const origin = req.headers.origin;
    
    // Allow requests without Origin (direct API calls)
    if (!origin) {
        return next();
    }
    
    // Validate Origin matches localhost patterns
    const allowedOrigins = [
        'http://localhost',
        'http://127.0.0.1',
        'https://localhost',
        'https://127.0.0.1',
    ];
    
    const isAllowed = allowedOrigins.some(
        (allowed) => origin === allowed || origin.startsWith(`${allowed}:`)
    );
    
    if (!isAllowed) {
        res.status(403).json({ error: 'Forbidden' });
        return;
    }
    
    next();
});

What it prevents:

  • DNS rebinding attacks where malicious websites attempt to make requests to your localhost MCP server
  • Cross-origin requests from untrusted domains
  • Remote exploitation via browser-based attacks

Configuration: To allow additional origins (e.g., development environments), modify the allowedOrigins array in src/index.ts.


2. Localhost-Only Binding ✅

Implementation: Explicit hostname binding (src/index.ts)

const HOST = '127.0.0.1'; // Explicit localhost binding
app.listen(PORT, HOST, () => {
    serverLogger.info(`HTTP transport listening on http://${HOST}:${PORT}`);
});

What it prevents:

  • Network exposure - server is NOT accessible from other machines
  • Accidental exposure on public networks (coffee shops, etc.)
  • Remote attacks even if firewall is misconfigured

Note: The server will ONLY accept connections from the local machine. This is the recommended configuration for MCP servers.


3. Secure Error Handling ✅

Implementation: Typed error responses with isError field (src/utils/error.util.ts)

export function formatErrorForMcpTool(error: unknown): {
    content: Array<{ type: 'text'; text: string }>;
    isError: true;  // Explicit error flag
    metadata?: {
        errorType: ErrorType;
        statusCode?: number;
        errorDetails?: unknown;
    };
}

What it provides:

  • MCP clients can reliably detect error states
  • Prevents sensitive error details from leaking
  • Structured error context for debugging

Authentication & Authorization

Current State: No Authentication (Localhost-Only)

The boilerplate does not implement authentication by default because:

  1. Server binds to 127.0.0.1 (localhost-only)
  2. DNS rebinding protection prevents browser-based attacks
  3. MCP clients on the same machine are trusted

This is secure for local development and personal use.


When Authentication is Required

Implement authentication if:

  • ❌ You expose the server to a network (beyond localhost)
  • ❌ Multiple users share the same machine
  • ❌ You deploy to a remote server
  • ❌ You handle sensitive data or privileged operations

Authentication Implementation Options

Option 1: Bearer Token (Simple)

Use case: Single-user remote deployment

// Add to src/index.ts before other middleware
app.use((req, res, next) => {
    const authHeader = req.headers.authorization;
    const expectedToken = process.env.MCP_AUTH_TOKEN;
    
    if (!expectedToken) {
        // Authentication disabled
        return next();
    }
    
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'Unauthorized' });
    }
    
    const token = authHeader.substring(7);
    if (token !== expectedToken) {
        return res.status(401).json({ error: 'Invalid token' });
    }
    
    next();
});

Configuration:

# .env.local
MCP_AUTH_TOKEN=your-secret-token-here

Client configuration:

{
    "mcpServers": {
        "boilerplate": {
            "url": "http://localhost:3000/mcp",
            "headers": {
                "Authorization": "Bearer your-secret-token-here"
            }
        }
    }
}

Option 2: API Key (Multi-User)

Use case: Multiple users with different permissions

// API key validation middleware
app.use(async (req, res, next) => {
    const apiKey = req.headers['x-api-key'];
    
    if (!apiKey) {
        return res.status(401).json({ error: 'API key required' });
    }
    
    // Validate against database or key store
    const user = await validateApiKey(apiKey);
    if (!user) {
        return res.status(401).json({ error: 'Invalid API key' });
    }
    
    // Attach user to request for authorization checks
    req.user = user;
    next();
});

Option 3: OAuth 2.0 (Enterprise)

Use case: Integration with existing identity providers

import passport from 'passport';
import { OAuth2Strategy } from 'passport-oauth2';

// Configure OAuth strategy
passport.use(new OAuth2Strategy({
    authorizationURL: process.env.OAUTH_AUTH_URL,
    tokenURL: process.env.OAUTH_TOKEN_URL,
    clientID: process.env.OAUTH_CLIENT_ID,
    clientSecret: process.env.OAUTH_CLIENT_SECRET,
    callbackURL: 'http://localhost:3000/auth/callback'
}, (accessToken, refreshToken, profile, done) => {
    // Validate user
    return done(null, profile);
}));

// Protect routes
app.use('/mcp', passport.authenticate('oauth2'), mcpHandler);

Option 4: mTLS (Mutual TLS)

Use case: Cryptographic authentication for production systems

import https from 'https';
import fs from 'fs';

const options = {
    key: fs.readFileSync('server-key.pem'),
    cert: fs.readFileSync('server-cert.pem'),
    ca: fs.readFileSync('ca-cert.pem'),
    requestCert: true,
    rejectUnauthorized: true
};

https.createServer(options, app).listen(PORT, HOST);

Security Checklist

For Local Development ✅

  • Server binds to localhost (127.0.0.1)
  • DNS rebinding protection enabled
  • CORS configured for localhost
  • Error handling doesn't leak sensitive info
  • Authentication: NOT REQUIRED (localhost-only)

For Network Deployment ⚠️

  • Implement authentication (Bearer/API Key/OAuth)
  • Use HTTPS/TLS encryption
  • Configure firewall rules
  • Implement rate limiting
  • Add audit logging
  • Set up monitoring and alerting
  • Review and restrict tool permissions
  • Validate and sanitize all inputs

For Production Deployment 🔐

  • Use mTLS or OAuth 2.0
  • Deploy behind reverse proxy (Nginx/Caddy)
  • Implement comprehensive audit logging
  • Set up intrusion detection
  • Configure automated security scanning
  • Establish incident response procedures
  • Regular security audits and penetration testing
  • Secrets management (AWS Secrets Manager, Vault, etc.)

Security Best Practices

1. Environment Variables

Never commit secrets to version control:

# .env.local (gitignored)
MCP_AUTH_TOKEN=secret-token
API_KEY=api-key-value
DATABASE_URL=mongodb://...

2. Input Validation

Always validate tool inputs with Zod:

const ToolSchema = z.object({
    param: z.string().min(1).max(100),
    number: z.number().int().positive(),
});

3. Rate Limiting

Prevent abuse with rate limiting:

import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100 // limit each IP to 100 requests per windowMs
});

app.use('/mcp', limiter);

4. Logging & Monitoring

Log authentication failures and suspicious activity:

logger.warn('Failed authentication attempt', {
    ip: req.ip,
    userAgent: req.headers['user-agent'],
    timestamp: new Date(),
});

5. Regular Updates

Keep dependencies up-to-date:

npm run update:check
npm run update:deps

Threat Model

Threats Mitigated ✅

  1. DNS Rebinding Attacks - Origin header validation
  2. Network Exposure - Localhost-only binding
  3. Error Information Disclosure - Structured error handling
  4. CORS Attacks - Explicit CORS configuration

Threats Requiring Additional Measures ⚠️

  1. DoS/DDoS - Add rate limiting
  2. Brute Force - Add authentication + rate limiting
  3. Man-in-the-Middle - Use HTTPS/TLS for network deployment
  4. Privilege Escalation - Implement authorization checks
  5. Injection Attacks - Validate inputs with Zod schemas

Reporting Security Issues

If you discover a security vulnerability:

  1. DO NOT open a public GitHub issue
  2. Email security concerns privately to the maintainer
  3. Include detailed reproduction steps
  4. Allow time for patching before public disclosure

References


Last Updated: February 4, 2026
Security Review: Recommended quarterly

There aren’t any published security advisories