SendGrid Integration Security Guide

To secure SendGrid integration, you need to: (1) create API keys with minimal permissions (Mail Send only for sending), (2) validate webhook signatures using ECDSA verification, (3) sanitize email content to prevent header injection attacks, (4) configure DKIM/SPF records for domain authentication, and (5) enable IP access management in the SendGrid console. This blueprint protects your email infrastructure from abuse and ensures deliverability.

Setup Time1-2 hours

TL;DR

SendGrid API keys should have minimal permissions and be stored server-side only. Validate webhook signatures, sanitize email content to prevent injection, configure DKIM/SPF for deliverability and security, and use IP access management for sensitive operations.

Sending Email Securely SendGrid

lib/email.ts
import sgMail from '@sendgrid/mail'

sgMail.setApiKey(process.env.SENDGRID_API_KEY!)

export async function sendWelcomeEmail(to: string, name: string) {
  // Sanitize inputs to prevent header injection
  const sanitizedName = name.replace(/[\r\n]/g, '')

  await sgMail.send({
    to,
    from: {
      email: 'noreply@yourdomain.com',
      name: 'Your App',
    },
    templateId: 'd-xxxxxxxxxxxxx',  // Use templates, not raw HTML
    dynamicTemplateData: {
      name: sanitizedName,
    },
  })
}

export async function sendEmail(options: {
  to: string
  subject: string
  text: string
}) {
  // Sanitize subject to prevent header injection
  const sanitizedSubject = options.subject.replace(/[\r\n]/g, '')

  await sgMail.send({
    to: options.to,
    from: 'noreply@yourdomain.com',
    subject: sanitizedSubject,
    text: options.text,
  })
}

Webhook Signature Validation SendGrid

app/api/webhooks/sendgrid/route.ts
import { EventWebhook } from '@sendgrid/eventwebhook'

const verificationKey = process.env.SENDGRID_WEBHOOK_VERIFICATION_KEY!

export async function POST(req: Request) {
  const signature = req.headers.get('x-twilio-email-event-webhook-signature')!
  const timestamp = req.headers.get('x-twilio-email-event-webhook-timestamp')!

  const body = await req.text()

  const eventWebhook = new EventWebhook()
  const ecPublicKey = eventWebhook.convertPublicKeyToECDSA(verificationKey)

  const isValid = eventWebhook.verifySignature(
    ecPublicKey,
    body,
    signature,
    timestamp
  )

  if (!isValid) {
    return new Response('Invalid signature', { status: 403 })
  }

  const events = JSON.parse(body)

  for (const event of events) {
    switch (event.event) {
      case 'bounce':
        await handleBounce(event.email)
        break
      case 'spam_report':
        await handleSpamReport(event.email)
        break
    }
  }

  return new Response('OK')
}

API Key Best Practices

Recommended API Key Permissions
// Create separate API keys for different purposes:

// 1. Sending emails (minimal permissions)
//    - Mail Send: Full Access
//    - All other permissions: No Access

// 2. Webhook validation (read-only)
//    - Event Notification: Read Access
//    - All other permissions: No Access

// 3. Never use full-access keys in applications

Use minimal API key permissions. Create separate keys for sending vs webhook handling. Enable IP access management in SendGrid for additional security.

Security Checklist

Pre-Launch Checklist

API key has minimal permissions

Webhook signatures validated

Email content sanitized

DKIM/SPF configured

IP access management enabled

Twilio SMS Integration Webhook Signature Patterns Edge Function Security

Check Your Email Integration

Scan for email security issues.

Security Blueprints

SendGrid Integration Security Guide