
Compartilhar:
Benjamin Aronov is a developer advocate at Vonage. He is a proven community builder with a background in Ruby on Rails. Benjamin enjoys the beaches of Tel Aviv which he calls home. His Tel Aviv base allows him to meet and learn from some of the world's best startup founders. Outside of tech, Benjamin loves traveling the world in search of the perfect pain au chocolat.
Detecte fraudes de troca de SIM com o Go e a Vonage
Tempo de leitura: 5 minutos
A maioria dos accounts online ainda depende do SMS para a autenticação de dois fatores (2FA). Mas o que acontece se um fraudador convencer sua operadora a transferir seu número para o SIM dele (ataque de troca de SIM)? De repente, esses códigos de segurança que deveriam protegê-lo se tornam a própria ferramenta usada contra você.
A verificação de troca de SIM não substitui a autenticação de duas etapas (2FA); ela informa ao seu aplicativo se o SMS ainda é um canal seguro para uso. Neste tutorial, vamos mostrar como integrar a API Identity Insights da Vonage com Go (Golang) para detectar trocas de SIM em tempo real e proteger seus usuários contra o sequestro de contas.
>> Resumo: Baixe o no GitHub.
Pré-requisitos
Para acompanhar, você vai precisar de:
Um account de desenvolvedor da Vonage
Configuração do projeto
Criar uma aplicação da Vonage
Para criar um aplicativo, acesse a página “Criar um aplicativo” no Painel da Vonage e defina um Nome para a sua Application.
Se você pretende usar uma API que utilize Webhooks, precisará de uma chave privada. Clique em “Gerar chave pública e privada”; o download deve iniciar automaticamente. Guarde-a em local seguro; essa chave não poderá ser baixada novamente em caso de perda. Ela seguirá a convenção de nomenclatura private_<seu ID de aplicativo>.key. Agora, essa chave pode ser usada para autenticar chamadas de API. Observação: sua chave não funcionará até que seu aplicativo seja salvo.
Escolha os recursos de que você precisa (por exemplo, Voice, Mensagens, RTC etc.) e forneça os webhooks necessários (por exemplo, URLs de eventos, URLs de resposta ou URLs de mensagens recebidas). Esses itens serão descritos no tutorial.
Para salvar e implantar, clique em “Gerar novo aplicativo” para finalizar a configuração. Seu aplicativo já está pronto para ser usado com as APIs da Vonage.
A API do Identity Insights depende de dados em tempo real da rede móvel para detectar trocas de SIM. Ativando o Registro de Rede em suas aplicações Vonage concede acesso a essa fonte de dados. Em ambiente de produção, isso requer aprovação da operadora, o que pode levar algum tempo, mas o modo Playground permite que você comece a testar imediatamente.
Para isso, crie uma aplicação da Vonage no Painel do Vonage com:
Ative o recurso “Registro de rede” e selecione “Playground”
Gerar uma private.key; você precisará movê-la para a raiz do seu diretório na seção a seguir
Configure seu aplicativo Go
No seu terminal, execute os seguintes comandos para criar um diretório de projeto e os arquivos necessários:
mkdir go_sim_swap_checker
cd go_sim_swap_checker
touch main.go .env>> Agora você pode mover sua private.key para a pasta go_sim_swap_checker diretório
Instalar dependências
No diretório do seu projeto, inicialize o módulo Go e instale os pacotes necessários:
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: Lida com a geração de tokens JWT para autenticação de API
godotenv: Carrega variáveis de ambiente de um arquivo .env
uuid: Gera identificadores exclusivos para cada JWT, garantindo que cada token possa ser distinguido com segurança
Configure seu .env Arquivo
Abra o arquivo .env no seu editor de texto e adicione o seguinte:
VONAGE_APPLICATION_ID=your_application_id
VONAGE_PRIVATE_KEY_PATH=./private.key
PHONE_NUMBER=+990123455
DEFAULT_HOURS=240Insira seu ID de aplicativo da Vonage, um número de telefone padrão e o período, em horas, para a verificação da troca do chip.
A Operadora Virtual da Vonage fornece nove números de telefone válidos que você pode usar para testes, além de um que retorna a resposta “desconhecido”.
Você pode conferir na Referência da API do Identity Insights que a informação sobre troca de SIM depende de um único parâmetro chamado período; vamos chamá-lo de “hora” e definir o valor padrão como 240. Para a operadora virtual, valores abaixo de 500 retornarão uma resposta indicando que o SIM não foi trocado. Valores acima de 500 resultarão em uma resposta positiva da API de troca de SIM.
Escrevendo o script em Go para verificar se houve troca de SIM
Etapa 1: Configurar as importações e o ambiente
Antes de podermos fazer solicitações à API, precisamos importar pacotes do Go para solicitações HTTP, manipulação de JSON e gerenciamento de variáveis de ambiente. Também carregaremos nossas credenciais a partir de um arquivo .env, para não codificarmos informações confidenciais diretamente no 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")
}
}
Etapa 2: Autenticar-se com JWT
A API do Identity Insights utiliza autenticação JWT (JSON Web Token) . Um JWT comprova à Vonage que seu aplicativo tem permissão para fazer a solicitação.
Vamos definir um tipo de para armazenar o ID do aplicativo da Vonage e a chave privada, adicionaremos um NewCredentials para carregá-los e, em seguida, usaremos um método de finalidade única GenerateJWT(ttl) para criar e assinar o token. Isso mantém a E/S de arquivos, a validação e a criação do token bem separadas e fáceis de testar.
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
}
Etapa 3: Definir as estruturas de troca de sim
Para lidar com a resposta JSON da API de maneira organizada em Go, definiremos um conjunto de estruturas que refletem a estrutura dos dados de troca de SIM da API do Identity Insights.
Essas estruturas nos permitirão analisar a resposta da API diretamente em objetos tipados do Go, facilitando o trabalho com os dados:
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"`
}
Etapa 4: Definir a função de troca do Core Sim
Agora que a autenticação está resolvida, vamos nos conectar à API. A função `checkSimSwap` reúne todos os elementos: ela aceita um número de telefone, um intervalo de tempo (em horas), a URL da API e o token JWT que você gerou anteriormente.
É o seguinte:
Prepare a solicitação: crie um corpo JSON que inclua o número de telefone, a finalidade de prevenção de fraudes e o período de troca do SIM.
Envie com segurança: use http.NewRequest para criar uma solicitação POST, anexe o JWT no campo cabeçalho e defina o tipo de conteúdo.
Analisar a resposta: decodificar o JSON em nosso SimSwapResponse para que possamos trabalhar com campos tipados em vez de JSON bruto.
Interprete o resultado: com base no código de status (OK, NO_COVERAGE, INTERNAL_ERROR)
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)
}
}
Etapa 5: Wrapper da CLI para interatividade
Para tornar o script prático, vamos colocá-lo dentro de uma função função main() para que você possa executá-lo diretamente. O wrapper da CLI solicita ao usuário um número de telefone e um período de tempo personalizado; caso contrário, usa os valores padrão. Em seguida, ele envia a solicitação à API do Identity Insights por meio de checkSimSwap e exibe um resultado claro no seu terminal.
Dessa forma, você pode testar diferentes números e intervalos de tempo de maneira interativa, sem precisar editar o código a cada vez. Posteriormente, essa mesma lógica poderia ser integrada a um serviço web ou a um sistema de back-end.
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)
}
Testando o script
Para executar o script, use o seguinte comando no seu terminal:
go run main.go Conclusão
Com apenas algumas linhas de código em Go e a API do Vonage Identity Insights, você pode detectar atividades de troca de SIM antes que elas se transformem em uma invasão total da conta. Essa abordagem é perfeita para testes e pode ser adaptada para o ambiente de produção assim que seu aplicativo for registrado no Network Registry.
Tem alguma dúvida ou quer compartilhar o que está criando?
Inscreva-se no Boletim Informativo para Desenvolvedores
Siga-nos no X (antigo Twitter) para ficar por dentro das novidades
Assista aos tutoriais no nosso canal do YouTube
Conecte-se conosco na página de desenvolvedores da Vonage no LinkedIn
Fique conectado e acompanhe as últimas notícias, dicas e eventos para desenvolvedores.
Compartilhar:
Benjamin Aronov is a developer advocate at Vonage. He is a proven community builder with a background in Ruby on Rails. Benjamin enjoys the beaches of Tel Aviv which he calls home. His Tel Aviv base allows him to meet and learn from some of the world's best startup founders. Outside of tech, Benjamin loves traveling the world in search of the perfect pain au chocolat.