Twilio Auth Token Rotation — Operasyonel Prosedür
DetayProje: ecutuningportal.com (Next.js + Prisma + PM2)
Kapsam: TWILIO_AUTH_TOKEN webhook signature validation rotasyonu
Tahmini süre: 15-20 dakika
Downtime riski: Dual-token stratejisiyle sıfıra yakın
Ne Zaman Rotate Edilmeli?
Bölüm başlığı “Ne Zaman Rotate Edilmeli?”- Her 90 günde bir rutin olarak (takvime ekle, bekleme yapma)
- Secret bir yere sızdıysa veya sızma şüphesi varsa — anında
- Token’a erişimi olan bir çalışan ayrıldıysa — o gün
.envdosyası yanlışlıkla version control’a commit edildiyse — anında- Twilio hesabında yetkisiz API aktivitesi tespit edildiyse — anında
Riskler ve Azaltma Stratejisi
Bölüm başlığı “Riskler ve Azaltma Stratejisi”Temel Risk: Webhook Signature Mismatch
Bölüm başlığı “Temel Risk: Webhook Signature Mismatch”Twilio bir webhook isteği gönderdiğinde isteği yeni token ile imzalar. Eğer sunucuda hâlâ eski token ile validate ediyorsak, Twilio’nun yeni imzasını reddederiz → 403 → Twilio retry → SMS/voice olayları kaybolur.
Pencere boyutu: Twilio’da primary token rotate edildiği andan sunucu yeni token ile yeniden başlatılana kadar geçen süre — yaklaşık 1-3 dakika.
Azaltma: Dual-Token Grace Window
Bölüm başlığı “Azaltma: Dual-Token Grace Window”Eski token’ı silmeden önce kod tarafında hem eski hem yeni token’ı aynı anda kabul et. Önce yeni token deploy edilir, sonra Twilio Console’da eski token invalidate edilir. Bu sırayla yapılırsa sıfır signature mismatch.
Timeline: t=0 → Twilio'dan yeni token al, env'e TWILIO_AUTH_TOKEN_NEW olarak ekle t=1 → Dual-token kodu deploy et, PM2 restart t=2 → Twilio Console'da eski token'ı invalidate et t=10 → Dual-token kodu kaldır, single token'a dön t=11 → Tekrar build + restartAdım Adım Prosedür
Bölüm başlığı “Adım Adım Prosedür”Adım 1 — Twilio Console’da Yeni Token Üret
Bölüm başlığı “Adım 1 — Twilio Console’da Yeni Token Üret”- console.twilio.com → Account → API keys & tokens
- Auth Tokens bölümünde Secondary Auth Token oluştur (yoksa “Create” butonu)
- Yeni Secondary token’ı kopyala — bu rotasyon boyunca TWILIO_AUTH_TOKEN_NEW olacak
- Primary token’ı şimdi invalidate ETME — bunu Adım 5’e kadar bekliyoruz
Not: Twilio bazı planlarda Secondary token üretmez. Bu durumda Primary’yi rotate et ama hemen aşağıdaki dual-token deployment’ı yapana kadar invalidate etme.
Adım 2 — .env Dosyasını Güncelle (Eski Token Korunsun)
Bölüm başlığı “Adım 2 — .env Dosyasını Güncelle (Eski Token Korunsun)”# Production sunucusunda (root veya sudo erişimi gerekli)nano /var/www/vhosts/ecutuningportal.com/httpdocs/.envEski satırı silme, yanına yeni satır ekle:
TWILIO_AUTH_TOKEN=eski_token_degeri # <-- DOKUNMATWILIO_AUTH_TOKEN_NEW=yeni_token_degeri_burayaDosyayı kaydet.
Adım 3 — Webhook Validation Kodunu Dual-Token Mode’a Al
Bölüm başlığı “Adım 3 — Webhook Validation Kodunu Dual-Token Mode’a Al”Projede Twilio webhook’u validate eden fonksiyonu bul:
grep -rn "validateRequest\|TWILIO_AUTH_TOKEN\|twilioSignature" \ /var/www/vhosts/ecutuningportal.com/httpdocs/src \ /var/www/vhosts/ecutuningportal.com/httpdocs/app \ /var/www/vhosts/ecutuningportal.com/httpdocs/lib \ --include="*.ts" --include="*.js" -lValidation mantığını şu pattern’e çevir:
import twilio from 'twilio';
function validateTwilioSignature( signature: string, url: string, params: Record<string, string>): boolean { const oldToken = process.env.TWILIO_AUTH_TOKEN!; const newToken = process.env.TWILIO_AUTH_TOKEN_NEW;
const validWithOld = twilio.validateRequest(oldToken, signature, url, params);
// Yeni token tanımlıysa dual-token mode aktif if (newToken) { const validWithNew = twilio.validateRequest(newToken, signature, url, params); return validWithOld || validWithNew; }
return validWithOld;}Kodu düzenledikten sonra kaydet.
Adım 4 — Build ve Deploy Et
Bölüm başlığı “Adım 4 — Build ve Deploy Et”cd /var/www/vhosts/ecutuningportal.com/httpdocs
# Build (yigit user olarak veya chown sonrası)npm run build
# Dosya sahipliğini düzeltchown -R yigit:yigit .next/ .env
# PM2 restartpm2 restart ecutuningportalPM2 log’larında hata yoksa onayla:
pm2 logs ecutuningportal --lines 30 --nostreamAdım 5 — Eski Token’ı Twilio Console’da Invalidate Et
Bölüm başlığı “Adım 5 — Eski Token’ı Twilio Console’da Invalidate Et”Bu adımı Adım 4 tamamlanmadan yapma. Dual-token deploy’u kesinleştikten sonra:
- Twilio Console → Account → API keys & tokens
- Primary Auth Token → Rotate (veya Secondary’yi Primary’ye promote et)
- Eski Primary token’ı invalidate et
Artık Twilio tüm webhook’ları yeni token ile imzalıyor. Sunucu dual-token mode’da olduğu için her ikisini de kabul ediyor.
Adım 6 — 5-10 Dakika Bekle, Sonra Dual-Token’ı Kaldır
Bölüm başlığı “Adım 6 — 5-10 Dakika Bekle, Sonra Dual-Token’ı Kaldır”Twilio’nun aktif retry kuyruğunda hâlâ eski token ile imzalanmış istek kalmış olabilir. 5-10 dakika beklemek bu pencereyi kapatır.
Ardından .env’i tekrar düzenle:
nano /var/www/vhosts/ecutuningportal.com/httpdocs/.envTWILIO_AUTH_TOKEN=yeni_token_degeri # artık bu tek token# TWILIO_AUTH_TOKEN_NEW satırını tamamen silWebhook validation kodundan dual-token mantığını kaldır, single token’a dön:
function validateTwilioSignature(...): boolean { const token = process.env.TWILIO_AUTH_TOKEN!; return twilio.validateRequest(token, signature, url, params);}Adım 7 — Son Build + Restart
Bölüm başlığı “Adım 7 — Son Build + Restart”npm run buildchown -R yigit:yigit .next/ .envpm2 restart ecutuningportalDoğrulama
Bölüm başlığı “Doğrulama”Geçerli Signature ile 200 Bekliyoruz
Bölüm başlığı “Geçerli Signature ile 200 Bekliyoruz”Twilio webhook POST’unu simüle et. X-Twilio-Signature hesaplamak için:
# Node.js ile imza üret (local veya sunucuda)node -e "const twilio = require('twilio');const token = 'YENI_TOKEN_BURAYA';const url = 'https://ecutuningportal.com/api/twilio/webhook';const params = {};const sig = twilio.getExpectedTwilioSignature(token, url, params);console.log(sig);"Üretilen imza ile POST at:
curl -s -o /dev/null -w "%{http_code}" \ -X POST https://ecutuningportal.com/api/twilio/webhook \ -H "X-Twilio-Signature: URETILEN_IMZA_BURAYA" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d ""# Beklenen çıktı: 200Geçersiz Signature ile 403 Bekliyoruz
Bölüm başlığı “Geçersiz Signature ile 403 Bekliyoruz”curl -s -o /dev/null -w "%{http_code}" \ -X POST https://ecutuningportal.com/api/twilio/webhook \ -H "X-Twilio-Signature: yanlis_imza_degeri" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d ""# Beklenen çıktı: 403Her iki test geçtiyse rotasyon tamamdır.
Rollback
Bölüm başlığı “Rollback”Kritik Uyarı: Twilio Console’da eski token’ı invalidate etmeden önce (Adım 5 öncesi) rollback yapılabilir. Adım 5 sonrasında Twilio eski token’ı tanımaz,
.env’e geri yazsan da webhook’lar başarısız olur.
Adım 5 Öncesi Rollback (Güvenli)
Bölüm başlığı “Adım 5 Öncesi Rollback (Güvenli)”# .env'den TWILIO_AUTH_TOKEN_NEW satırını sil# TWILIO_AUTH_TOKEN eski değerde kalsınnano /var/www/vhosts/ecutuningportal.com/httpdocs/.env
# Dual-token kodu geri al (git revert veya manuel düzenleme)git diff HEAD -- lib/twilio.ts # değişikliği görgit checkout -- lib/twilio.ts # eski haline döndür
# Rebuild + restartnpm run build && chown -R yigit:yigit .next/ .env && pm2 restart ecutuningportalAdım 5 Sonrası — Rollback Yapılamaz
Bölüm başlığı “Adım 5 Sonrası — Rollback Yapılamaz”Eski token Twilio’da geçersiz. Bu noktada rollback girişimi daha fazla hasara yol açar. Tek seçenek: Adım 3-7’yi yeni token üzerinden tamamlamak. Dual-token mode zaten çalışıyor olduğu için kalan adımlar hızlı ilerler.
Bu yüzden Adım 4’ü kesinleştirmeden Adım 5’e geçme.
Uzun Vadede: Secrets Manager Geçiş Notu
Bölüm başlığı “Uzun Vadede: Secrets Manager Geçiş Notu”Manuel rotation prosedürü insan hatasına açık. Orta vadede .env yerine bir secrets manager entegre edilmesi önerilir:
- Doppler — Next.js + Laravel için native entegrasyon, rotation otomasyonu, audit log
- AWS Secrets Manager — Lambda rotation function ile 90 günlük otomatik schedule, IAM tabanlı erişim
- HashiCorp Vault — Self-hosted, dynamic secrets, lease-based token ömrü
Her üç seçenek de bu runbook’u devre dışı bırakır: token Twilio’da rotate edildiğinde uygulama sıfır manuel müdahaleyle yeni değeri okur. Geçiş önceliği: Doppler en düşük friction, AWS Secrets Manager production-grade.