Twilio 公式例に沿った Function の Public 提供時の Basic 認証

Twilio 公式例に沿った Function の Public 提供時の Basic 認証

Twilio Function を Public で提供すると、第三者からも URL にアクセスできます。Twilio 公式ドキュメントの例に沿って Basic 認証を入れる実装を紹介します。
2026.01.06

はじめに

Twilio Function を Public で提供せざるを得ない場面では、第三者からの不正アクセスが起きやすくなります。この記事では、Twilio 公式ドキュメント を根拠に、Basic 認証を Twilio Function に実装する方法を説明します。

Twilio とは

Twilio は、SMS や音声通話などのコミュニケーション機能を API として提供するクラウドサービスです。Twilio Function は、Twilio が提供するサーバーレス実行環境であり、HTTP リクエストを受けて Node.js のコードを実行できます。

Basic 認証とは

Basic 認証は、HTTP リクエストの Authorization ヘッダーに、ユーザー名とパスワードを含めて送る認証方式です。ヘッダーには、ユーザー名とパスワードを Base64 で符号化した値を付与しますが、暗号化ではないため、HTTPS 前提で運用します。

対象読者

  • Twilio Function を Public で運用する必要がある方
  • 外部システムから Twilio Function を HTTP で呼び出す方
  • Twilio Function に最小限の認証を追加したい方

参考

実装

Twilio Function を Public にすると、URL を知っている第三者もアクセスできます。そこで、Twilio 公式の Basic 認証の実装例と同じ方式で、Authorization ヘッダーを検証して認証します。

外部システムからの通知は QueryString と JSON ボディの両方があり得ます。Twilio Function では、GET と POST のパラメータは event に集約されます。POST の JSON ボディも event に集約されます。認証は Authorization ヘッダーで判定するため、リクエスト形式によって認証実装を分ける必要はありません。

認証情報を環境変数に保存する

コードにユーザー名とパスワードを書き込むと、漏えい時の影響が大きくなります。Twilio 公式ドキュメントでも、認証情報は環境変数として保存し、Function から参照する方針を示しています。

Twilio Console の Functions Editor で、Environment Variables メニューに次を追加します。

  • BASIC_AUTH_USER
  • BASIC_AUTH_PASS

Environmental Variables setting

Twilio Function のコード例

次のサンプルコードは、Basic 認証を検証し、認証できた場合のみ 200 を返す例です。認証できない場合は 401 と WWW-Authenticate ヘッダーを返します。

/**
 * Twilio Function: Basic 認証で保護する例
 * 認証できたら 200 を返す
 */
exports.handler = (context, event, callback) => {
  const response = new Twilio.Response();

  const username = context.BASIC_AUTH_USER;
  const password = context.BASIC_AUTH_PASS;

  const unauthorized = () => {
    response.setStatusCode(401);
    response.setBody("Unauthorized");
    response.appendHeader("WWW-Authenticate", "Basic realm=twilio-function");
    return callback(null, response);
  };

  if (!username || !password) {
    response.setStatusCode(500);
    response.setBody("Server misconfigured: BASIC_AUTH_USER / BASIC_AUTH_PASS is missing");
    return callback(null, response);
  }

  // headers は小文字で参照する
  const authHeader = event?.request?.headers?.authorization;
  if (!authHeader) return unauthorized();

  const [authType, credentials] = authHeader.split(" ");
  if (!authType || authType.toLowerCase() !== "basic" || !credentials) return unauthorized();

  let decoded;
  try {
    decoded = Buffer.from(credentials, "base64").toString("utf8");
  } catch {
    return unauthorized();
  }

  const idx = decoded.indexOf(":");
  if (idx < 0) return unauthorized();

  const reqUser = decoded.slice(0, idx);
  const reqPass = decoded.slice(idx + 1);

  if (reqUser !== username || reqPass !== password) return unauthorized();

  // 認証できたら 200
  response.setStatusCode(200);
  response.appendHeader("Content-Type", "application/json");
  response.setBody({ ok: true });
  return callback(null, response);
};

Function を Public に設定する

Function の Visibility を Public に設定してデプロイしてください。

visibility setting

検証

認証の検証では、認証ヘッダーの有無と正否でレスポンスが変わることを確認します。あわせて、GET の QueryString と POST の JSON ボディのどちらでも Function に到達できることを確認します。

先に、認証用の環境変数を設定しておきます。ここで設定する環境変数は、Twilio 側の Environment Variables とは別に、curl の実行環境で参照するためのものです。

export BASIC_AUTH_USER="your-user"
export BASIC_AUTH_PASS="your-pass"

QueryString (GET) の例

# 認証なし (401)
curl -i "https://YOUR_DOMAIN.twil.io/notify?message=hello&severity=warn"

# 認証あり (200)
curl -i \
  -u "${BASIC_AUTH_USER}:${BASIC_AUTH_PASS}" \
  "https://YOUR_DOMAIN.twil.io/notify?message=hello&severity=warn"

# 認証情報が不正 (401)
curl -i \
  -u "${BASIC_AUTH_USER}:wrong-pass" \
  "https://YOUR_DOMAIN.twil.io/notify?message=hello&severity=warn"

Authorization ヘッダーがない場合は 401 が返ります。

HTTP/2 401
www-authenticate: Basic realm=twilio-function
(略)

Unauthorized

Authorization ヘッダーが不正な場合も 401 が返ります。

HTTP/2 401
www-authenticate: Basic realm=twilio-function
(略)

Unauthorized

Authorization ヘッダーが正しい場合は 200 が返ります。

HTTP/2 200
content-type: application/json
(略)

{"ok":true}

JSON ボディ (POST) の例

Twilio Functions は application/json をサポートし、JSON ボディは event に正規化されます。

curl-d を指定すると POST になりますが、ここでは -X POST も併記し、意図が読み取れる形にします。

# 認証なし (401)
curl -i \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"message":"hello","severity":"warn"}' \
  "https://YOUR_DOMAIN.twil.io/notify"

# 認証あり (200)
curl -i \
  -X POST \
  -H "Content-Type: application/json" \
  -u "${BASIC_AUTH_USER}:${BASIC_AUTH_PASS}" \
  -d '{"message":"hello","severity":"warn"}' \
  "https://YOUR_DOMAIN.twil.io/notify"

# 認証情報が不正 (401)
curl -i \
  -X POST \
  -H "Content-Type: application/json" \
  -u "${BASIC_AUTH_USER}:wrong-pass" \
  -d '{"message":"hello","severity":"warn"}' \
  "https://YOUR_DOMAIN.twil.io/notify"

Authorization ヘッダーがない場合は 401 が返ります。

HTTP/2 401
www-authenticate: Basic realm=twilio-function
(略)

Unauthorized

Authorization ヘッダーが不正な場合も 401 が返ります。

HTTP/2 401
www-authenticate: Basic realm=twilio-function
(略)

Unauthorized

Authorization ヘッダーが正しい場合は 200 が返ります。

HTTP/2 200
content-type: application/json
(略)

{"ok":true}

まとめ

Twilio Function を Public にすると、外部から直接アクセスできるため、認証を入れない運用は危険です。Twilio 公式の例をベースに Basic 認証を追加すると、外部システムからの呼び出しでも最低限の対策を入れられます。一方で、Twilio Function 内では送信元 IP を扱えない制約があるため、IP アドレス制限が必要な場合は前段のプロキシで制御します。

この記事をシェアする

FacebookHatena blogX

関連記事