
Compartir:
Benjamin Aronov es desarrollador de Vonage. Es un constructor de comunidades con experiencia en Ruby on Rails. Benjamin disfruta de las playas de Tel Aviv, a la que llama hogar. Su base en Tel Aviv le permite conocer y aprender de algunos de los mejores fundadores de startups del mundo. Fuera de la tecnología, a Benjamin le encanta viajar por el mundo en busca del perfecto pain au chocolat.
Detectar el fraude del intercambio de SIM con Go y Vonage
Tiempo de lectura: 5 minutos
La mayoría de las cuentas en línea siguen confiando en los SMS para la autenticación de dos factores (2FA). Pero ¿qué ocurre si un estafador convence a tu operador para que transfiera tu número a su SIM (ataque de intercambio de SIM)? De repente, esos códigos de seguridad destinados a protegerte se convierten en la misma herramienta utilizada contra ti.
Una verificación de intercambio de SIM no reemplaza a 2FA; le indica a tu aplicación si SMS sigue siendo un canal seguro para usar. En este tutorial, te mostraremos cómo integrar la API Identity Insights de Vonage con Go (Golang) para detectar intercambios de SIM en tiempo real y proteger a tus usuarios del secuestro de Account.
>> TL;DR: Obtenga el código código de trabajo en GitHub.
Requisitos previos
Para seguirlo, necesitarás:
Una cuenta de desarrollador de Vonage
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
Configuración del proyecto
Crear una aplicación de Vonage
Para crear una aplicación, vaya a la sección Crear una aplicación en el panel de Vonage y define un nombre para tu aplicación.
Si tiene intención de utilizar una API que utilice Webhooks, necesitará una clave privada. Haga clic en "Generar clave pública y privada"; la descarga debería iniciarse automáticamente. Guárdela de forma segura; esta clave no puede volver a descargarse si se pierde. Seguirá la convención de nomenclatura private_<id de su aplicación>.key. Esta clave puede utilizarse ahora para autenticar llamadas a la API. Nota: La clave no funcionará hasta que se guarde la aplicación.
Elija las funciones que necesite (por ejemplo, Voice, Messages, RTC, etc.) y proporcione los webhooks necesarios (por ejemplo, URL de eventos, URL de respuestas o URL de mensajes entrantes). Estos se describirán en el tutorial.
Para guardar e implementar, haz clic en "Generar nueva aplicación" para finalizar la configuración. Tu aplicación ahora está lista para usar con las API de Vonage.
La API Identity Insights depende de los datos en tiempo real de la red móvil para detectar los intercambios de SIM. Activación de Registro de red en tu aplicación de Vonage te permite acceder a esta fuente de datos. En producción, esto requiere la aprobación del operador, lo que puede llevar tiempo, pero el modo Playground te permite comenzar a probar de inmediato.
Para ello, cree una aplicación de Vonage en el Panel de Vonage con:
Capacidad de registro de red activada y seleccione "Playground".
Generar una clave.privadadeberá moverla a la raíz de su directorio en la siguiente sección
Configure su Go App
En su terminal, ejecute los siguientes comandos para crear un directorio de proyecto y los archivos necesarios:
mkdir go_sim_swap_checker
cd go_sim_swap_checker
touch main.go .env>> Ahora puede mover su clave.privada en el go_sim_swap_checker directorio
Instalar dependencias
En el directorio de su proyecto, inicialice el módulo Go e instale los paquetes necesarios:
go mod init sim_swap_checker
go get github.com/golang-jwt/jwt/v5
go get github.com/joho/godotenv
go get github.com/google/uuidgolang-jwt: Gestiona la generación de tokens JWT para la autenticación de la API
godotenv: Carga variables de entorno desde un archivo .env
uuid: Genera identificadores únicos para cada JWT, garantizando que cada token pueda distinguirse de forma segura.
Configure su.env Archivo
Abra el archivo .env en su editor de texto y añada lo siguiente:
VONAGE_APPLICATION_ID=your_application_id
VONAGE_PRIVATE_KEY_PATH=./private.key
PHONE_NUMBER=+990123455
DEFAULT_HOURS=240Agrega tu ID de aplicación de Vonage, un número de teléfono predeterminado y el período de tiempo en horas para la verificación de intercambio de SIM.
El Operadora virtual de Vonage proporciona 9 números de teléfono válidos que puedes usar para realizar pruebas, junto con uno que da una respuesta "desconocida".
Puede consultar en la Referencia de la API de Identity Insights que Sim swap insight se basa en un único parámetro denominado periodolo llamaremos hora, y estableceremos el valor por defecto en 240. Para el operador virtual, los valores por debajo de 500 devolverán una respuesta de que la SIM no ha sido intercambiada. Valores por encima de 500, resultarán en una respuesta positiva de la API Sim Swap.
Escribiendo el Script Go para Comprobar el Cambio de SIM
Paso 1: Configurar las importaciones y el entorno
Antes de que podamos hacer peticiones a la API, necesitamos traer paquetes Go para peticiones HTTP, manejo de JSON y gestión de variables de entorno. También cargaremos nuestras credenciales desde un archivo .env para no codificar información sensible en el script.
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/joho/godotenv"
)
func loadEnv() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
}
Paso 2: Autenticarse con JWT
La API Identity Insights utiliza JWT (token web JSON) autenticación. Un JWT demuestra a Vonage que tu aplicación tiene permiso para realizar la solicitud.
Definiremos un campo Credenciales para guardar el ID de la aplicación de Vonage y la clave privada, agregaremos un archivo de tipo NewCredentials para cargarlas, y luego usaremos un método de propósito único GenerarJWT(ttl) para crear y firmar el token. Esto mantiene la E/S de archivos, la validación y la creación de tokens separadas y fáciles de probar.
type Credentials struct {
ApplicationID string
PrivateKeyPEM []byte
}
func NewCredentials(applicationID, privateKeyPath string) (*Credentials, error) {
if applicationID == "" {
return nil, fmt.Errorf("missing VONAGE_APPLICATION_ID")
}
if privateKeyPath == "" {
return nil, fmt.Errorf("missing VONAGE_PRIVATE_KEY_PATH")
}
pem, err := os.ReadFile(privateKeyPath)
if err != nil {
return nil, fmt.Errorf("read private key: %w", err)
}
return &Credentials{ApplicationID: applicationID, PrivateKeyPEM: pem}, nil
}
func (c *Credentials) GenerateJWT(ttl time.Duration) (string, error) {
key, err := jwt.ParseRSAPrivateKeyFromPEM(c.PrivateKeyPEM)
if err != nil {
return "", fmt.Errorf("parse rsa key: %w", err)
}
now := time.Now()
claims := jwt.MapClaims{
"iat": now.Unix(),
"exp": now.Add(ttl).Unix(),
"jti": uuid.NewString(),
"application_id": c.ApplicationID,
}
tok := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
signed, err := tok.SignedString(key)
if err != nil {
return "", fmt.Errorf("sign jwt: %w", err)
}
return signed, nil
}
Paso 3: Definir Sim Swap Structs
Para manejar la respuesta JSON de la API de forma limpia en Go, definiremos un conjunto de structs que reflejen la estructura de los datos SIM Swap de la API Identity Insights.
Estos structs nos permitirán analizar la respuesta de la API directamente en objetos Go tipados, facilitando el trabajo con los datos:
type SimSwapStatus struct {
Code string `json:"code"`
Message string `json:"message"`
}
type SimSwapDetails struct {
Swapped *bool `json:"swapped,omitempty"`
LastSimSwapAt string `json:"latest_sim_swap_at,omitempty"`
Status SimSwapStatus `json:"status"`
}
type SimSwapResponse struct {
RequestID string `json:"request_id"`
Insights struct {
SimSwap SimSwapDetails `json:"sim_swap"`
} `json:"insights"`
}
Paso 4: Definir la función Core Sim Swap
Con la autenticación resuelta, vamos a conectarnos a la API. La función checkSimSwap lo reúne todo: acepta un número de teléfono, una ventana de tiempo (en horas), la URL de la API y el token JWT que generaste antes.
Esto es lo que pasa:
Prepare la solicitud: construya un cuerpo JSON que incluya el número de teléfono, el propósito de prevención del fraude y el periodo de intercambio de la SIM.
Envíelo de forma segura: utilice http.NewRequest para crear una solicitud POST, adjunte el JWT en el campo Autorización y establece el tipo de contenido.
Parsear la respuesta: decodificar el JSON en nuestro SimSwapResponse para que podamos trabajar con campos tipificados en lugar de JSON sin procesar.
Interpretar el resultado: en función del código de estado (OK, NO_COVERAGE, ERROR_INTERNO)
func checkSimSwap(phoneNumber, hours, apiURL, jwtToken string) {
reqBody := map[string]interface{}{
"phone_number": phoneNumber,
"purpose": "FraudPreventionAndDetection",
"insights": map[string]interface{}{
"sim_swap": map[string]interface{}{
"period": hours,
},
},
}
body, err := json.Marshal(reqBody)
if err != nil {
log.Fatal("Error marshaling request body:", err)
}
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(body))
if err != nil {
log.Fatal("Error creating request:", err)
}
req.Header.Set("Authorization", "Bearer "+jwtToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatal("Error sending request:", err)
}
defer resp.Body.Close()
var res SimSwapResponse
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
log.Fatalf("Error decoding API response: %v", err)
}
switch res.Insights.SimSwap.Status.Code {
case "OK":
if res.Insights.SimSwap.Swapped != nil {
if *res.Insights.SimSwap.Swapped {
t, err := time.Parse(time.RFC3339, res.Insights.SimSwap.LastSimSwapAt)
formatted := res.Insights.SimSwap.LastSimSwapAt
if err == nil {
formatted = t.Format("January 2, 2006 at 3:04 PM")
}
fmt.Printf("\n🚨 ALERT: SIM swap detected!\nLast swap occurred around: %s\n", formatted)
} else {
fmt.Println("\n✅ No SIM swap detected. The phone number appears to be secure.")
}
} else {
fmt.Println("\n⚠️ Status was OK but swap status was missing.")
}
case "NO_COVERAGE":
fmt.Println("\nℹ️ This phone number is not supported by the SIM swap check service.")
case "INTERNAL_ERROR":
fmt.Println("\n❌ An unexpected error occurred while checking the phone number. Please try again later.")
default:
fmt.Printf("\n❌ An unknown error occurred: %s\n", res.Insights.SimSwap.Status.Message)
}
}
Paso 5: Envoltorio CLI para interactividad
Para que el script sea práctico, lo envolveremos en un script main() para que puedas ejecutarlo directamente. La envoltura CLI solicita al usuario un número de teléfono y un período de tiempo personalizado, o vuelve al predeterminado. A continuación, envía la solicitud a la API de Identity Insights a través de checkSimSwap e imprime un resultado claro en su terminal.
De este modo, puede probar diferentes números y ventanas de tiempo de forma interactiva sin tener que editar el código cada vez. Más adelante, la misma lógica podría integrarse en un servicio web o un sistema backend.
func main() {
loadEnv()
apiURL := "https://api-eu.vonage.com/v0.1/identity-insights"
phoneNumber := os.Getenv("PHONE_NUMBER")
defaultHours := os.Getenv("DEFAULT_HOURS")
applicationID := os.Getenv("VONAGE_APPLICATION_ID")
privateKeyPath := os.Getenv("VONAGE_PRIVATE_KEY_PATH")
creds, err := NewCredentials(applicationID, privateKeyPath)
if err != nil {
log.Fatal("Credentials error:", err)
}
jwtToken, err := creds.GenerateJWT(15 * time.Minute)
if err != nil {
log.Fatal("JWT error:", err)
}
fmt.Println("=== Vonage Identity Insights - SIM Swap Checker ===")
fmt.Printf("Enter phone number [Default: %s]: ", phoneNumber)
var phone string
fmt.Scanln(&phone)
if phone == "" {
phone = phoneNumber
}
fmt.Printf("Enter number of hours to check for SIM swap [Default: %s]: ", defaultHours)
var hours string
fmt.Scanln(&hours)
if hours == "" {
hours = defaultHours
}
checkSimSwap(phone, hours, apiURL, jwtToken)
}
Probar el guión
Para ejecutar el script, utilice el siguiente comando en su terminal:
go run main.go Conclusión
Con solo unas pocas líneas de Go y la API Identity Insights de Vonage, puedes detectar la actividad de intercambio de SIM antes de que se convierta en una toma de posesión de cuenta a gran escala. Este enfoque es perfecto para pruebas y se puede adaptar para producción una vez que tu aplicación esté registrada en Network Registry.
¿Tienes alguna pregunta o algo que compartir? Únete a la conversación en Slack de la comunidad de Vonagey mantente actualizado con el Boletín para desarrolladoressíguenos en X (antes Twitter)suscríbete a nuestro canal de YouTube para ver tutoriales en video, y sigue la página de página para desarrolladores de Vonage en LinkedInun espacio para que los desarrolladores aprendan y se conecten con la comunidad. Mantente conectado, comparte tu progreso y entérate de las últimas noticias, consejos y eventos para desarrolladores.
Compartir:
Benjamin Aronov es desarrollador de Vonage. Es un constructor de comunidades con experiencia en Ruby on Rails. Benjamin disfruta de las playas de Tel Aviv, a la que llama hogar. Su base en Tel Aviv le permite conocer y aprender de algunos de los mejores fundadores de startups del mundo. Fuera de la tecnología, a Benjamin le encanta viajar por el mundo en busca del perfecto pain au chocolat.
