OpenAI Plugins Security Considerations: A Defense-in-Depth Guide

OpenAI plugins require defense-in-depth security covering HTTPS enforcement, PKCE OAuth flows, server-side token storage, and OWASP-compliant headers to protect user data across distributed environments.

OpenAI plugins are independent code bundles that execute in diverse runtime environments—from cloud functions to local developer hosts—and expose skill definitions via JSON manifests. Because any ChatGPT session can invoke these plugins, they must implement rigorous security controls spanning transport encryption, authentication flows, and data protection. This guide examines the security requirements implemented in the openai/plugins repository, with specific references to production-ready patterns found in the Zoom, Twilio, and Vercel plugin implementations.

Transport-Layer Security

All plugin endpoints must enforce strict transport security to prevent man-in-the-middle attacks and injection vulnerabilities.

HTTPS and TLS Requirements

According to the Zoom security documentation in plugins/zoom/skills/zoom-apps-sdk/concepts/security.md, all external endpoints must reject plain HTTP connections and redirect to HTTPS. The implementation requires TLS 1.2 or higher and valid, non-self-signed certificates. For local development, tools like ngrok automatically supply trusted certificates that satisfy this requirement.

Security Headers

The repository mandates OWASP-compliant security headers for all plugin responses. In plugins/zoom/skills/zoom-apps-sdk/concepts/security.md, the required headers include Strict-Transport-Security, X-Content-Type-Options, Referrer-Policy, X-Frame-Options, and a strict Content-Security-Policy that restricts framing to the hosting platform domains.

app.use((req, res, next) => {
  res.setHeader('Strict-Transport-Security', 'max-age=31536000');
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('Content-Security-Policy',
    "frame-ancestors 'self' zoom.us *.zoom.us");
  res.setHeader('Referrer-Policy', 'same-origin');
  next();
});

Authentication and Authorization

Secure authentication flows prevent credential leakage and unauthorized access to user data.

OAuth with PKCE

The repository mandates PKCE (Proof Key for Code Exchange) for all public OAuth clients. As documented in plugins/zoom/skills/zoom-apps-sdk/concepts/security.md, client secrets must never appear in frontend code. Instead, generate a cryptographically random verifier and challenge:

const crypto = require('crypto');

const verifier = crypto.randomBytes(32).toString('hex');
const challenge = crypto.createHash('sha256')
  .update(verifier)
  .digest('base64url');

// Store verifier server-side only
req.session.codeVerifier = verifier;
res.json({ codeChallenge: challenge, state: req.session.state });

CSRF Protection via State Parameter

To prevent cross-site request forgery, generate a random state token before OAuth redirects and validate it on callback. The implementation in plugins/zoom/skills/zoom-apps-sdk/concepts/security.md demonstrates strict validation:

// Before redirect
const state = crypto.randomBytes(16).toString('hex');
req.session.oauthState = state;
res.redirect(`https://zoom.us/oauth/authorize?...&state=${state}`);

// Callback validation
app.get('/auth', (req, res) => {
  if (req.query.state !== req.session.oauthState) {
    return res.status(403).send('Invalid state – possible CSRF');
  }
  // Continue token exchange...
});

Webhook Verification

Incoming webhook requests require signature verification before processing. The plugins/zoom/skills/rest-api/references/authentication.md file specifies that webhook endpoints must validate cryptographic signatures (e.g., Zoom or Twilio signatures) to ensure message authenticity and prevent replay attacks.

Data Protection and Token Storage

Tokens and credentials require server-side isolation to prevent browser-based extraction attacks.

Server-Side Token Storage

Access and refresh tokens must persist exclusively on the server. According to the token storage matrix in plugins/zoom/skills/zoom-apps-sdk/concepts/security.md, never store tokens in browser storage (localStorage, sessionStorage, or cookies). Instead, use encrypted databases or Redis:

const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);

async function storeTokens(userId, tokens) {
  await redis.set(`zoom:tokens:${userId}`, 
    JSON.stringify(tokens), 
    'EX', 
    tokens.expires_in
  );
}

When session cookies are necessary, set SameSite=None; Secure because plugins run inside cross-origin embedded frames. The cookie security section in plugins/zoom/skills/zoom-apps-sdk/concepts/security.md provides this Express implementation:

app.use(require('cookie-session')({
  name: 'session',
  keys: [process.env.SESSION_SECRET],
  maxAge: 24 * 60 * 60 * 1000,
  sameSite: 'none',
  secure: true,
}));

Runtime Security and Dependency Management

Continuous security scanning and dependency hygiene prevent supply chain vulnerabilities.

Automated Security Scanning

The repository includes the codex-security skill for automated security checks. Located in plugins/codex-security/scripts/validate_report_format.py, this tool runs static analysis on pull requests to detect credential leakage and vulnerable dependencies.

Secret Management

API keys, client IDs, and secrets must reside in environment variables (process.env.*) and never appear in hard-coded strings. The plugins/vercel/agents/deployment-expert.md documentation notes that Vercel's deployment agent automatically flags environment variables exposed in client-side bundles.

Platform-Specific Security Controls

Different hosting platforms implement additional defense layers.

Zoom Plugin Security

The Zoom implementation in plugins/zoom/skills/zoom-apps-sdk/concepts/security.md combines OWASP headers, CSP policies, PKCE flows, server-side token storage, and webhook signature verification into a comprehensive security model.

Twilio and Vercel Implementations

Summary

  • Enforce TLS 1.2+ with valid certificates and OWASP-compliant security headers (Strict-Transport-Security, Content-Security-Policy).
  • Implement PKCE OAuth flows without exposing client secrets in frontend code, and validate CSRF state parameters on all callbacks.
  • Store tokens server-side in encrypted databases or Redis, never in browser storage or client-side code.
  • Configure cookies with SameSite=None; Secure for cross-origin embedding contexts.
  • Verify webhook signatures before processing incoming requests from platforms like Zoom or Twilio.
  • Use environment variables for all secrets and credentials, validated by automated scanning tools like codex-security.

Frequently Asked Questions

What transport security standards are required for OpenAI plugins?

All plugins must enforce HTTPS with TLS 1.2 or higher and reject plain HTTP connections. Additionally, endpoints must return OWASP-compliant security headers including Strict-Transport-Security, X-Content-Type-Options, and a restrictive Content-Security-Policy as specified in plugins/zoom/skills/zoom-apps-sdk/concepts/security.md.

How should OAuth tokens be stored in OpenAI plugins?

Access and refresh tokens must be stored exclusively on the server side using encrypted databases, Redis, or similar secure storage. Never persist tokens in browser localStorage, sessionStorage, or client-accessible cookies. This requirement is documented in the token storage matrix within the Zoom security guide.

What is PKCE and why is it required for OpenAI plugin authentication?

PKCE (Proof Key for Code Exchange) is an OAuth extension that prevents authorization code interception attacks. It requires generating a cryptographically random verifier that never leaves the server, ensuring that even if an authorization code is intercepted, it cannot be exchanged for tokens without the original verifier. The openai/plugins repository mandates PKCE for all public clients in plugins/zoom/skills/zoom-apps-sdk/concepts/security.md.

How do I protect against CSRF attacks in OpenAI plugin OAuth flows?

Generate a random state parameter using crypto.randomBytes(16).toString('hex') before redirecting users to the OAuth provider, store it server-side, and validate that the returned state matches exactly in your callback handler. If the values differ, reject the request immediately as a potential CSRF attack, as implemented in the Zoom plugin security documentation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →