ガイド · Gmail API
プロダクトチームはGmail APIをどのように安全に実装すべきか
Gmail APIは、汎用的なメール配信用の認証情報としてではなく、特定のGmailメールボックスへの委任されたアクセスとして実装します。機能を実現できる最小のOAuthスコープを選び、認可の状態とリフレッシュトークンを保護し、すべてのメールボックスをテナント単位に限定します。成熟したインターネットメッセージのライブラリでメッセージを組み立て、返されたGmailのメッセージIDを記録し、Pub/Subと履歴レコードを使って変更を同期します。サービスアカウントによるなりすまし(インパーソネーション)は、Workspace管理者が判断する事項として扱います。最後に、APIによる受け付け、受信サーバーへの配信、受信トレイへの到達は別々の結果として扱ってください。
コードを書く前にメールボックスのモデルを決める
Gmail APIは、ユーザーのGmailメールボックスを操作するものです。プロダクトがそのメールボックスを読み取る、ラベルやスレッドを整理する、下書きを作成する、認可したユーザーとして送信する、メールボックスの変更を同期する、といった必要がある場合に適しています。この権限は、検証済みのプロダクトのドメインからアプリケーション用のメールAPIを呼び出すよりも、はるかに広範です。まず、メールボックスで行う具体的な処理と、アクセスを許可する主体を明確にします。ユーザー向けのプロダクトでは通常、接続するGoogleアカウントごとにOAuthの同意を得ます。社内のGoogle Workspaceの自動化であれば、管理者が承認したドメイン全体の委任を使う場合もあります。会社が管理するドメインから領収書、確認用リンク、アラートなどのプロダクトをきっかけとするメッセージを送るだけであれば、メールボックスへのアクセスは一切避け、トランザクションメールAPIを検討してください。このアーキテクチャ上の判断によって、セキュリティの制御や同意画面で補う前の段階で、不要なアクセスを減らせます。
実用上最も狭いスコープで認可する
正しいアプリケーションの種類でOAuthクライアントを設定し、登録済みのリダイレクトURIを正確に使い、予測不能なstateの値で認可レスポンスを開始元のブラウザセッションに結びつけます。アクセスは、ユーザーがそれを必要とする機能を有効にしたときに、その文脈の中で要求します。送信専用の連携であれば、`https://www.googleapis.com/auth/gmail.send` は、メールボックスを読み取ったり変更したりするスコープよりも狭い範囲です。Googleは `gmail.send` を機密性の高い(sensitive)スコープに分類しており、`gmail.readonly`、`gmail.compose`、`gmail.modify` などのスコープは制限付き(restricted)に分類しています。機密性の高いアクセスや制限付きのアクセスを使う公開アプリにはOAuthの確認が必要になることがあり、制限付きスコープのデータをサーバー側で保存または送信すると、追加のセキュリティ評価の要件が発生することがあります。オフラインアクセスは、バックグラウンド処理が本当に必要な場合にのみ要求してください。リフレッシュトークンは暗号化し、各トークンを1つの社内テナントとGoogleのサブジェクトに関連づけ、ブラウザのコードやログには決して公開せず、ローカルの認証情報を削除してバックグラウンド処理を止める、テスト済みの接続解除の手順を用意してください。
サービスアカウントとドメイン全体の委任を理解する
サービスアカウントはアプリケーションのIDであり、そのまま使えるGmailの受信トレイではありません。サービスアカウント単体では、従業員のメッセージにアクセスできません。Google Workspaceのユーザーデータについては、特権管理者が、ドメイン全体の委任を通じて、サービスアカウントの数値のクライアントIDとOAuthスコープの正確なリストを明示的に承認する必要があります。そのうえでアプリケーションは、特定のユーザーについて委任された認証情報を要求し、各API呼び出しは、承認されたスコープの範囲内でそのユーザーの権限で動作します。バックグラウンドのワーカーが黙ってメールボックスを切り替えられないよう、なりすます対象のサブジェクトをジョブのデータと監査ログに明示しておきます。性質が大きく異なるワークロードには別々のサービスアカウントを使い、実行環境がマネージドな認証情報を使える場合はダウンロード可能な秘密鍵を避け、ドメイン全体の権限付与を定期的に見直してください。一般消費者向けのGmailアカウントには、この組織全体の委任を許可できるWorkspace管理者がいないため、それらのアカウントにはユーザーのOAuth同意を使ってください。
制御と監査可能性を失わずにメッセージを送信する
Gmailは、`users.messages.send` を通じて、base64urlでエンコードされた完全なインターネットメールメッセージを `raw` フィールドで受け付けます。下書きを作成して後から送信することもできます。ヘッダー行を手作業で連結するのではなく、メンテナンスされているメッセージライブラリを使って、From、To、Cc、Bcc、Subject、Date、Message-ID、テキスト、HTML、添付ファイルの構造を生成してください。エンコードする前に受信者と内容をバリデーションし、ヘッダーインジェクションを拒否し、サイズの上限を明示的に設定します。Gmailを呼び出す前に、プロダクトの操作を冪等にしておきます。安定したアプリケーションのイベントキー、送信に使うメールボックスのサブジェクト、送信試行の状態を保存してください。成功のレスポンスを受け取ったら、Gmailが返したメッセージIDとスレッドIDをそのイベントとともに保存します。リクエストを送信したあとでクライアントがタイムアウトした場合は、メッセージがすでに受け付けられている可能性があるため、再試行する前にメールボックスの状態を突き合わせてください。元のレスポンスが失われただけでも、やみくもに再試行すると重複したメールが送られることがあります。内容や受信者に承認が必要な場合は、下書きの作成と人によるレビューを組み合わせてください。
履歴レコードを使ってメールボックスの変更を同期する
サーバーサイドのメールボックス連携では、Gmailのwatchが、Google Cloud Pub/Subを通じて変更のシグナルを発行します。この通知は同期を促すものであり、メールの完全なペイロードではありません。watchのレスポンスに含まれる現在の履歴IDと有効期限を保存し、通知には速やかに応答し、最後に正常に確定した履歴IDから `users.history.list` を呼び出して、メッセージとラベルの変更を把握します。機能に必要なメッセージだけを取得し、ローカルへの書き込みが成功してからチェックポイントを進めます。通知は遅れたり重複したりすることがあるため、メッセージと履歴の処理は冪等にしてください。Gmailでは、メールボックスのwatchを少なくとも7日ごとに更新する必要があり、毎日の更新が推奨されています。有効期限の十分前に更新をスケジュールし、失敗したらアラートを出すようにしてください。保存した履歴IDがGmailで利用できる範囲外にある場合、APIはHTTP 404を返します。これを定義済みの回復手順として扱ってください。無効な履歴IDでいつまでも再試行するのではなく、管理された形で完全な同期を行い、新しいチェックポイントを確立してから、差分処理を再開します。
段階的な実装と検証のワークフローを使う
まず、機能がメールを送信するのか、読み取るのか、変更するのか、監視するのかを文書化し、各操作を最小のOAuthスコープに対応づけます。次に、開発環境と本番環境で別々のGoogle CloudプロジェクトまたはOAuthクライアントを作成し、正確なリダイレクトURIと、認証情報の担当者を明確にします。3つ目に、stateの検証、必要な場合に限ったオフラインアクセス、暗号化したトークンの保存、トークンの失効、テナント単位のアクセスチェックを備えた認可を実装します。4つ目に、管理されたメールボックスでテストします。接続し、期限切れのアクセストークンを更新し、同意を取り消し、再接続し、1回送信し、あいまいなタイムアウトをシミュレートして、重複送信が防止されることを確認します。5つ目に、変更を受信する場合は、Pub/Subの権限を設定し、watchを開始し、履歴を差分で処理し、古くなったチェックポイントからの回復を強制的に発生させ、watchの更新を確認します。6つ目に、ユーザーごとの作業キュー、上限を設けた指数バックオフ、構造化されたエラー分類、デフォルトでメッセージ本文とトークンを含めない監査ログを追加します。リリース前に、必要なGoogleの確認とセキュリティレビューを完了し、正確なデータ利用の開示を公開し、認証情報のローテーションとユーザーデータの削除を予行演習してください。
クォータ、再試行、部分的な失敗に備える
GmailはAPIの使用量を、リクエスト数だけではなくクォータ単位で測定します。Googleのクォータページには、プロジェクトあたり毎分1,200,000単位、プロジェクトごとのユーザーあたり毎分6,000単位が掲載されています。`messages.send`、`drafts.send`、`watch`はそれぞれ100単位で、メッセージあたりの受信者上限は500人です。Gmailのユーザー別送信上限は、API、Web、SMTPクライアントのすべてに引き続き適用されます。公開されている上限をビジネスロジックにハードコードするのではなく、Cloudコンソールと最新のドキュメントを実行時設定の入力として扱ってください。メールボックスごとに処理を直列化または公平にキューイングし、同時実行数を制限し、ジッター付き指数バックオフと有限の期限で一時的なレスポンスのみを再試行します。認可、ポリシー、無効な受信者、不正なメッセージのエラーを、容量の問題であるかのように再試行しないでください。multipart batchは接続のオーバーヘッドを減らしますが、内部の各呼び出しは依然としてクォータを消費し、独立して失敗する可能性があります。
受け付け、配信、受信トレイへの到達を区別する
`messages.send` の呼び出しが成功したということは、Gmailが認可されたAPIリクエストを受け付け、GmailのMessageリソースを返したということです。すべての受信者のメールサーバーがメッセージを受け付けたことの証明にはならず、受信側のシステムがメッセージをどう分類したかもわかりません。受信サーバーへの配信とは、宛先のシステムがSMTP上の責任を引き受けたことを意味します。受信トレイへの到達はその後のフィルタリングの結果であり、メインの受信トレイ、プロモーション、隔離、迷惑メールなどがあります。そのため、プロダクトがトランザクションメールの配信、バウンス、苦情のテレメトリを必要とする場合、Gmailのメールボックス用APIはプロバイダーのイベントストリームの代わりにはなりません。突き合わせのためにGmailのメッセージIDは保存しておきますが、配信を裏づける別の証拠がない限り、ユーザーに見せる状態は「Gmailが送信した」または「Gmailが受け付けた」と正確に表現してください。送信ドメイン認証、想定どおりの受信者、コンテンツの品質、送信の挙動、宛先のポリシーはすべて、その後の処理に影響します。APIのレスポンスで、受信者の最終的なメールボックスのフォルダを決めたり約束したりすることはできません。
トランザクションメールAPIが別の用途に適する場面を知る
スレッド、ラベル、下書き、メールボックス同期を含め、プロダクトが個人または組織のGmailメールボックスへ認可済みアクセスを必要とする場合はGmail APIを使用します。トランザクションメールAPIは別のアーキテクチャに適しています。これは、ユーザーのGmailメールボックスを読む委任権限なしに、組織が管理するドメインから送信されるアプリケーション起点のメッセージです。境界が明確であれば、プロダクトは両方のシステムを使用できます。たとえば、Gmail OAuthでサポートエージェントの接続済みメールボックスを読み、別途検証済みのトランザクションプロバイダーでプロダクトの領収書を送信できます。メールボックスの権限がアプリケーション全体の送信へ漏れず、トランザクション認証情報がユーザーのGmailを読めないように、認証情報、同意、メッセージストア、再試行ポリシー、監査記録を分離してください。
よくある質問
サービスアカウントはどのGmailメールボックスにもアクセスできますか?
いいえ。サービスアカウントが自動的にGmailのユーザーデータにアクセスできるようになることはありません。Google Workspaceの特権管理者が、その数値のクライアントIDと承認済みのスコープにドメイン全体の委任を付与する必要があり、そのうえでアプリケーションがその組織内のユーザーを明示的に指定してなりすまします。一般消費者向けのGmailアカウントには、代わりにユーザーのOAuth同意を使ってください。
送信専用のGmail連携ではどのOAuthスコープを要求すべきですか?
まず `https://www.googleapis.com/auth/gmail.send` を検討してください。このスコープでは、メールボックス全般の読み取り権限を与えずに、ユーザーに代わって送信できます。より広いスコープを要求する前に、下書き、メッセージの読み取り、ラベル、変更が本当に必要なプロダクト要件がないことを確認し、Googleの機密性の高いスコープに関する確認ルールも考慮してください。
Gmail APIでの送信が成功すれば、メッセージは配信されたことになりますか?
いいえ。確認できるのは、Gmailが認可されたAPIの操作を受け付け、メッセージの記録を返したことです。受信サーバーによる受け付けと受信トレイへの到達は、その後の別の状態です。信頼できる別のシグナルがその結論を裏づけない限り、メッセージを「配信済み」と表示したり、受信トレイへの到達を約束したりしないでください。
Gmailのプッシュ通知には新しいメッセージ全体が含まれていますか?
いいえ。Pub/Subの通知は、メールボックスの状態が変わったことを知らせるもので、同期を続けるための情報が含まれています。アプリケーションは、保存した履歴IDからGmailの履歴を照会し、必要なメッセージデータを取得し、冪等に処理してから、チェックポイントを進めるべきです。
Gmailメールボックスのwatchはどのくらいの頻度で更新する必要がありますか?
Googleは、少なくとも7日に1回 `watch` を呼び出すことを求めており、毎日の更新を推奨しています。返された有効期限を保存し、その前に更新し、失敗を監視してください。更新を逃しても際限のないデータの欠落が黙って発生しないよう、フォールバックの同期ジョブも用意しておきましょう。
Gmail APIではなくトランザクションメールAPIを使うべきなのはどんなときですか?
組織が管理するドメインからアプリケーションをきっかけにメールを送ることが目的で、個人のGmailメールボックスへのアクセスが必要な機能がない場合は、トランザクションメールAPIを使います。委任されたメールボックスのメッセージ、スレッド、ラベル、下書き、設定、または代理送信(send-as)の権限がプロダクトで特に必要な場合は、Gmail APIを使います。
出典
- Gmail APIの概要 — Google for Developers
- Gmail APIのスコープを選択する — Google for Developers
- サーバーサイドの認可を実装する — Google for Developers
- ウェブサーバー アプリケーションでのOAuth 2.0の使用 — Google for Developers
- サーバー間アプリケーションでのOAuth 2.0の使用 — Google for Developers
- メールメッセージを作成して送信する — Google for Developers
- Gmail APIでプッシュ通知を設定する — Google for Developers
- クライアントをGmailと同期する — Google for Developers
- Gmail APIの使用量の上限 — Google for Developers
- Gmail APIのエラーを解決する — Google for Developers
- Google Workspace APIのユーザーデータおよびデベロッパーに関するポリシー — Google for Developers
- RFC 5322:インターネットメッセージ形式(Internet Message Format) — RFC Editor
- RFC 5321:簡易メール転送プロトコル(SMTP) — RFC Editor