Skip to content

Security

github-actions[bot] edited this page Mar 23, 2026 · 20 revisions

Security

The MATLAB MCP Server implements a multi-layered security architecture to safely expose MATLAB execution to AI agents while preventing unauthorized access and misuse.

Architecture Overview

graph TB
    Client["AI Agent / Client"]
    Auth["Authentication Layer<br/>(Reverse Proxy)"]
    Server["MCP Server<br/>(SSE/stdio)"]
    Validator["Security Validator<br/>(Blocklist Check)"]
    Executor["Job Executor<br/>(Code Execution)"]
    Pool["Engine Pool<br/>(Workspace Isolation)"]
    MATLAB["MATLAB Engine"]
    
    Client -->|Request| Auth
    Auth -->|Authenticated Session| Server
    Server -->|Code + Session ID| Validator
    Validator -->|Blocked?| Executor
    Executor -->|Safe Code| Pool
    Pool -->|Isolated Workspace| MATLAB
    
    style Auth fill:#ff9999
    style Validator fill:#ffcc99
    style Pool fill:#99ccff
Loading

Authentication & Authorization

Transport-Based Access Control

stdio Transport (Single User)

  • No authentication needed
  • Suitable for local development or trusted environments
  • Single fixed session per server instance

SSE Transport (Multi-User)

  • Requires reverse proxy with authentication
  • Each client connection receives a unique session_id
  • Session-isolated temporary directories and workspaces
  • Configure via server.transport: "sse" and security.require_proxy_auth: true

Proxy Authentication Setup

The server must be deployed behind an authenticating reverse proxy when using SSE:

security:
  require_proxy_auth: true  # Acknowledges auth is in place

server:
  transport: "sse"
  host: "127.0.0.1"         # Bind to localhost only
  port: 8765                 # Never expose directly

Recommended Proxy Configuration:

# Nginx example
server {
    listen 443 ssl http2;
    server_name matlab.example.com;
    
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    
    # Authentication (e.g., OAuth2, LDAP, Kerberos via auth module)
    auth_request /auth;
    
    location / {
        proxy_pass http://127.0.0.1:8765;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-User-ID $remote_user;
        proxy_set_header X-Session-ID $session_id;
    }
}

Security Warning:

The server logs a warning at startup if SSE is enabled without require_proxy_auth: true:

WARNING: SSE transport enabled without require_proxy_auth=true. 
Ensure the server is behind an authenticating reverse proxy.

Input Validation & Sanitization

Code-Level Validation

graph LR
    Input["MATLAB Code<br/>Input"]
    Strip["Strip Literals<br/>& Comments"]
    Scan["Scan for<br/>Blocked Functions"]
    SafeCode{"Safe?"}
    Execute["Execute in<br/>Engine Pool"]
    Reject["Reject with<br/>Error"]
    
    Input --> Strip
    Strip --> Scan
    Scan --> SafeCode
    SafeCode -->|Yes| Execute
    SafeCode -->|No| Reject
    
    style Reject fill:#ff9999
    style Execute fill:#99ff99
Loading

SecurityValidator (src/matlab_mcp/security/validator.py):

class SecurityValidator:
    def check_code(self, code: str) -> None:
        """Validate MATLAB code before execution.
        
        1. Strips string literals and comments
        2. Scans for blocked function calls
        3. Raises BlockedFunctionError if violation detected
        """

Function Blocklist

By default, these MATLAB functions are blocked:

Function Risk Example
system() Execute arbitrary OS commands system('rm -rf /')
unix() Execute Unix shell commands unix('cat /etc/passwd')
dos() Execute Windows commands dos('del C:\System32')
! Shell escape operator !curl http://evil.com
eval() Execute arbitrary string as MATLAB code eval('load backdoor')
feval() Call function by name string feval(user_input)
evalc() Evaluate and capture output evalc(untrusted_code)
evalin() Evaluate in specific workspace evalin('base', 'secret')
assignin() Assign variable in caller workspace assignin('base', var, val)
perl() Execute Perl scripts perl('system("id")')
python() Execute Python scripts python('import os; os.system(...)')

Smart Scanning (String Literal Stripping)

The validator strips string literals and comments before checking. This prevents false positives:

% SAFE — These will NOT trigger the blocklist:
disp('The operating system is great')    % "system" in string literal
msg = "Unix-based systems";              % "unix" in string
% system('ls')                           % "system" in comment
help_text = ['Type: system, Version: 1']; % "system" in char array

% BLOCKED — These WILL be detected:
system('whoami')                         % Actual function call
y = eval(user_code)                      % Actual eval call
feval(func_name, arg)                    % Actual feval call

Filename Sanitization

The filename_sanitizer() function prevents path traversal and unsafe filenames:

def sanitize_filename(filename: str) -> str:
    """Allow only safe characters: [a-zA-Z0-9._-]"""
    # Rejects: ../../, /etc/passwd, ..\..\, C:\, etc.

Rejected Filenames:

  • ../../etc/passwd (path traversal)
  • /etc/shadow (absolute path)
  • ..\..\windows\system32 (Windows traversal)
  • file|with&special<chars> (unsafe characters)

Accepted Filenames:

  • data.csv
  • my_script.m
  • results-2024.txt
  • output_v2.0.mat

Security Configuration

Default Configuration

security:
  # Enable/disable function blocklist
  blocked_functions_enabled: true
  
  # Default blocklist (can be customized)
  blocked_functions:
    - "system"
    - "unix"
    - "dos"
    - "!"
    - "eval"
    - "feval"
    - "evalc"
    - "evalin"
    - "assignin"
    - "perl"
    - "python"
  
  # Upload protection
  max_upload_size_mb: 100
  
  # Proxy authentication flag (SSE only)
  require_proxy_auth: false

Customizing the Blocklist

Add or remove functions based on your use case:

security:
  blocked_functions_enabled: true
  blocked_functions:
    - "system"
    - "unix"
    - "dos"
    - "!"
    - "eval"
    - "feval"
    - "evalc"
    - "evalin"
    - "assignin"
    - "perl"
    - "python"
    - "web"        # Add if you want to prevent web access
    - "websave"    # Add if you want to prevent downloads
    - "urlread"    # Add if you want to prevent HTTP requests

Disabling the Blocklist (Not Recommended)

security:
  blocked_functions_enabled: false  # DANGEROUS — allows all functions

Workspace Isolation

Each user session has its own isolated MATLAB workspace:

graph TB
    Session1["Session 1<br/>(User A)"]
    Session2["Session 2<br/>(User B)"]
    Engine1["Engine 1<br/>Workspace A"]
    Engine2["Engine 2<br/>Workspace B"]
    
    Session1 -->|x=10, y=20| Engine1
    Session2 -->|x=99, y=88| Engine2
    
    Note["Variables do NOT<br/>leak between sessions"]
    
    style Session1 fill:#99ccff
    style Session2 fill:#ffcc99
    style Engine1 fill:#99ff99
    style Engine2 fill:#ffff99
Loading

Isolation Mechanism

When execution.workspace_isolation: true (default), the server automatically clears the workspace between code executions:

clear all;           % Remove user variables
clear global;        % Clear global variables
clear functions;     % Clear loaded user functions
fclose all;          % Close all file handles
restoredefaultpath;  % Reset MATLAB search path

Configuration

execution:
  workspace_isolation: true  # Recommended for multi-user
  # OR
  workspace_isolation: false # Single-user systems only

Session Management & Cleanup

Session Lifecycle

sequenceDiagram
    participant Client
    participant Server
    participant SessionMgr
    participant TempDir
    
    Client->>Server: Request with session_id
    Server->>SessionMgr: get_or_create(session_id)
    SessionMgr->>TempDir: Create ./temp/session_{id}/
    TempDir-->>SessionMgr: temp_dir
    SessionMgr-->>Server: session
    Server->>Server: Execute code in temp_dir
    Note over Server: Session idle for 60 min
    Server->>SessionMgr: Cleanup(session_id)
    SessionMgr->>TempDir: rm -rf temp/session_{id}/
Loading

Configuration

sessions:
  max_sessions: 50              # Max concurrent sessions
  session_timeout: 3600         # Idle timeout (seconds)
  job_retention_seconds: 86400  # Keep job records for 24h

execution:
  temp_cleanup_on_disconnect: true  # Delete files on disconnect

Cleanup Behavior

  • Session expiration: Sessions are marked inactive after session_timeout seconds of no activity
  • Temp file deletion: All files in ./temp/session_{id}/ are deleted when the session ends
  • Job records: Completed/failed job metadata is pruned after job_retention_seconds

Upload Protection

Size Limits

security:
  max_upload_size_mb: 100  # Default: 100 MB per file

Attempts to upload larger files return an error:

{
  "status": "error",
  "error": {
    "type": "ValidationError",
    "message": "File size (250 MB) exceeds max_upload_size_mb (100 MB)"
  }
}

Filename Sanitization

Upload filenames are strictly validated before storing:

# Tool: upload_data
# Parameters: filename (string), content_base64 (string)

# Sanitization rules:
# 1. Reject path traversal: ../, ..\, /path, C:\path
# 2. Allow only: [a-zA-Z0-9._-]
# 3. Max length: 255 characters

Examples:

✓ data.csv
✓ my_script.m
✓ results_v2.0.xlsx
✗ ../../etc/passwd     (path traversal)
✗ /absolute/path.mat   (absolute path)
✗ file|with&bad.txt    (special characters)

Monitoring & Audit Logging

When monitoring is enabled (monitoring.enabled: true), the server logs security events:

collector.record_event("blocked_function", {
    "function": "system",
    "code_snippet": "system('whoami')",
    "session_id": "s-abc123",
    "timestamp": 1234567890
})

Query recent security violations:

# Use the error_log tool to see recent security events
GET /api/error-log?hours=24&event_type=blocked_function

Best Practices for Secure Deployment

Development (Single User, Local)

server:
  transport: "stdio"           # Single user via stdio
  log_level: "debug"           # Verbose logging

security:
  blocked_functions_enabled: true
  require_proxy_auth: false    # Not needed for local

execution:
  workspace_isolation: false   # Not needed for single user

Team Server (Multi-User)

server:
  transport: "sse"
  host: "127.0.0.1"            # Only bind to localhost
  port: 8765

security:
  blocked_functions_enabled: true
  require_proxy_auth: true     # REQUIRED for SSE
  max_upload_size_mb: 500      # Increase for team use

sessions:
  max_sessions: 25             # Limit concurrent users
  session_timeout: 7200        # 2 hours

execution:
  workspace_isolation: true    # REQUIRED for multi-user

Reverse Proxy Configuration (nginx):

server {
    listen 443 ssl http2;
    server_name matlab.company.com;
    
    ssl_certificate /etc/letsencrypt/live/matlab.company.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/matlab.company.com/privkey.pem;
    
    # OAuth2 Proxy (or similar auth module)
    auth_request /oauth2/auth;
    error_page 401 =302 /oauth2/start;
    
    location / {
        proxy_pass http://127.0.0.1:8765;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-User-ID $remote_user;
    }
}

Production (Enterprise)

server:
  transport: "sse"
  host: "127.0.0.1"
  log_level: "warning"         # Less verbose for prod

security:
  blocked_functions_enabled: true
  # Review and customize blocklist for your applications
  blocked_functions:
    - "system"
    - "unix"
    - "dos"
    - "!"
    - "eval"
    - "feval"
    - "evalc"
    - "evalin"
    - "assignin"
    - "perl"
    - "python"
  require_proxy_auth: true     # MUST be true
  max_upload_size_mb: 1000

sessions:
  max_sessions: 100
  session_timeout: 3600        # 1 hour
  job_retention_seconds: 604800 # 7 days

execution:
  workspace_isolation: true
  temp_cleanup_on_disconnect: true
  max_execution_time: 3600     # 1 hour max per job

monitoring:
  enabled: true                # Enable metrics & audit log
  sample_interval: 5
  retention_days: 30
  dashboard_enabled: true

Deployment Checklist:

  • Server runs behind reverse proxy with TLS 1.3+
  • Proxy enforces authentication (OAuth2, SAML, Kerberos, etc.)
  • require_proxy_auth: true in config
  • Blocklist reviewed for your MATLAB code patterns
  • Monitoring enabled with secure log storage
  • Regular backups of metrics database
  • Firewall rules restrict access to localhost only
  • Log aggregation configured (Splunk, ELK, Datadog, etc.)

Reporting Security Vulnerabilities

If you discover a security vulnerability, please report it responsibly:

  1. Do NOT open a public GitHub issue
  2. Email: [security contact or link to SECURITY.md]
  3. Include:
    • Description of the vulnerability
    • Steps to reproduce
    • Potential impact
    • Suggested fix (if any)

We will acknowledge receipt within 24 hours and work toward a fix within 14 days.

References

Clone this wiki locally