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.
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
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
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
// 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
Related Integration Stacks
Twilio SMS Integration Webhook Signature Patterns Edge Function Security