SendGrid Integration Security Guide

Share

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.

Start Free Scan
Security Blueprints

SendGrid Integration Security Guide