https://a.storyblok.com/f/270183/1368x665/9cf2bb9c91/25oct_dev-blog_sim-swap-go.jpg

GoとVonageでSIMスワップ詐欺を検知する

最終更新日 October 23, 2025

所要時間:1 分

ほとんどのオンラインアカウントは、いまだに二要素認証(2FA)をSMSに頼っている。しかし、詐欺師がキャリアを説得し、あなたの番号をキャリアのSIMに移行させたらどうなるだろうか(SIMスワップ攻撃)。SIMスワップ攻撃)?突然、あなたを守るためのセキュリティーコードが、あなたに対して使われるツールそのものになってしまうのだ。

SIM スワップチェックは 2FA に取って代わるものではなく、SMS がまだ安全なチャネルであるかどうかをアプリに知らせるものです。このチュートリアルでは、Vonage の Identity Insights API を以下と統合する方法を紹介する。 Go (Golang)と統合する方法を紹介します。

>> TL;DR:完全な GitHubで動作するコードを入手する.

前提条件

フォローするには、以下のものが必要だ:

プロジェクト設定

Vonageアプリケーションの作成

  • アプリケーションを作成するには アプリケーションの作成ページでアプリケーションの名前を定義します。

  • Webhooksを使用するAPIを使用する場合は、秘密鍵が必要です。Generate public and private key "をクリックすると、自動的にダウンロードが始まります。この鍵は紛失すると再ダウンロードできません。この鍵は紛失しても再ダウンロードできません。 private_<あなたのアプリID>.key.この鍵はAPIコールの認証に使用できます。 注意:アプリケーションを保存するまで、キーは機能しません。

  • 必要な機能(Voice、Messages、RTCなど)を選択し、必要なWebhook(イベントURL、応答URL、受信メッセージURLなど)を提供します。これらはチュートリアルで説明します。

  • 保存してデプロイするには、"Generate new application "をクリックして設定を確定します。これでアプリケーションはVonage APIで使用する準備が整いました。

Identity Insights API は、ライブのモバイルネットワークデータに依存して SIM スワップを検出します。有効化 ネットワークレジストリを有効にすると、このデータソースにアクセスできるようになります。本番環境では、オペレータの承認が必要で時間がかかりますが、プレイグラウンドモードではすぐにテストを開始できます。

そのためには、Vonageアプリケーションを Vonage Dashboardを使います:

  1. ネットワークレジストリ機能を有効にし、"Playground "を選択する。

  2. 秘密鍵の生成 秘密鍵次のセクションで、これをディレクトリのルートに移動する必要があります。

囲碁アプリのセットアップ

ターミナルで以下のコマンドを実行し、プロジェクト・ディレクトリと必要なファイルを作成する:

mkdir go_sim_swap_checker
cd go_sim_swap_checker
touch main.go .env

>>ダウンロードした 秘密鍵ディレクトリに移動します。ディレクトリに移動します。

依存関係のインストール

プロジェクト・ディレクトリでGoモジュールを初期化し、必要なパッケージをインストールします:

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/uuid
  • ゴドテンブ:.env ファイルから環境変数を読み込む

  • uuid:各JWTに一意の識別子を生成し、各トークンを安全に識別できるようにする。

設定環境ファイル

テキストエディタで.envファイルを開き、以下を追加する:

VONAGE_APPLICATION_ID=your_application_id
VONAGE_PRIVATE_KEY_PATH=./private.key
PHONE_NUMBER=+990123455
DEFAULT_HOURS=240

VonageアプリケーションID、デフォルトの電話番号、SIMスワップチェックの時間(単位:時間)を追加します。

バーチャルオペレーター Vonageバーチャルオペレーターには、テストに使用できる9つの有効な電話番号が用意されています。

をご覧ください。 Identity Insights APIリファレンスを参照してください。 期間ここでは hour と呼び、デフォルトを 240 に設定する。仮想オペレータの場合、500 未満の値は SIM がスワップされていないという応答を返す。500を超えると、Sim Swap APIは肯定的な応答を返します。

SIMの入れ替えをチェックするGoスクリプトの書き方

ステップ1:インポートと環境のセットアップ

APIリクエストを行う前に、HTTPリクエスト、JSON処理、環境変数管理用のGoパッケージを導入する必要がある。また、スクリプトに機密情報をハードコーディングしないように、.envファイルから認証情報を読み込みます。

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")
	}
}

ステップ2:JWTによる認証

Identity Insights API は以下を使用します。 JWT(JSONウェブトークン)認証を使用します。JWT は、アプリケーションにリクエストを行う権限があることを Vonage に証明します。

ここでは を定義します。型を定義し、Vonage アプリケーション ID と秘密鍵を保持するようにします。 を追加します。コンストラクタを追加します。 GenerateJWT(ttl)を使用する。これにより、ファイル入出力、検証、トークン作成がきれいに分離され、テストしやすくなります。

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
}

ステップ3:Simスワップ構造体の定義

Go で API の JSON レスポンスをきれいに処理するために、Identity Insights API の SIM Swap データの構造を反映した構造体のセットを定義する。

これらの構造体によって、APIレスポンスを型付けされたGoオブジェクトに直接パースすることができ、データを簡単に扱うことができます:

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"`
}

ステップ4:コア・シム・スワップ機能の定義

認証が完了したら、APIに接続してみよう。checkSimSwap関数は、電話番号、タイムウィンドウ(時間単位)、API URL、そして先ほど生成したJWTトークンを受け取る。

これが現状だ:

  1. リクエストの準備:電話番号、詐欺防止目的、SIM交換期間を含むJSONボディを作成する。

  2. 安全に送信するには HTTP.NewRequestを使用してPOSTリクエストを作成し、JWTを ヘッダーにJWTを添付しヘッダーに JWT を添付し、コンテントタイプを設定します。

  3. レスポンスを解析します。 にデコードします。構造体にデコードし、生のJSONの代わりに型付きフィールドを扱えるようにします。

  4. 結果を解釈する:ステータスコード(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)
  }
}

ステップ5:インタラクティビティのためのCLIラッパー

このスクリプトを実用的なものにするために、スクリプトを main()関数でラップします。CLI ラッパーは、ユーザーに電話番号とカスタム期間の入力を求めるか、デフォルトに戻ります。次に、Identity Insights API にリクエストを送信します。 チェックシムスワップを通じて Identity Insights API にリクエストを送信し、明確な結果をターミナルに出力します。

こうすることで、毎回コードを編集することなく、さまざまなNumbersや時間窓をインタラクティブにテストすることができる。後で、同じロジックをウェブサービスやバックエンドシステムに統合することもできる。

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)
}

スクリプトのテスト

スクリプトを実行するには、ターミナルで以下のコマンドを使用する:

go run main.go

結論

わずか数行の Go と Vonage Identity Insights API を使用するだけで、本格的なアカウント乗っ取りに発展する前に SIM 交換アクティビティを検出できます。このアプローチはテストに最適で、アプリケーションがネットワークレジストリに登録されれば、本番環境にも適用できます。

ご質問がある場合、またはあなたが作っているものを共有したい場合は、こちらをクリックしてください。

最新の開発者向けニュース、ヒント、イベント情報をお届けします。

シェア:

https://a.storyblok.com/f/270183/384x384/e4e7d1452e/benjamin-aronov.png
Benjamin Aronovデベロッパー・アドボケイト

Benjamin AronovはVonageの開発者支援者です。彼はRuby on Railsのバックグラウンドを持つ実績のあるコミュニティ・ビルダーです。Benjaminは故郷であるテルアビブのビーチを楽しんでいる。テルアビブを拠点に、世界最高のスタートアップの創設者たちと出会い、学ぶことができる。技術以外では、完璧なパン・オ・ショコラを求めて世界中を旅するのが好き。