日曜エンジニア

仕事・趣味のメモ置き場です。皆様のお役に立てれば幸いです。

【GoogleAppsScript】iPhoneウィジェットからワンタップでGmailを送る方法


注意

本記事で実装しているコード及び、レシピ、設定等は、セキュリティリスクを許容している勉強用のサンプルです。実運用される際は、各自でセキュリティ実装を行い、自己責任でお願いします🤲


image.GIF

TL;DR

  • oauth非対応のWEBクライアントからのGoogleAppsScript経由でのgmail送信を試みた
  • サーバ側(GAS)のサンプルコード
  • クライアント側(iOS ショートカットアプリ)のサンプルレシピ
  • 試行する際、サーバ側/クライアント側の環境変数設定を忘れずに!

目次

背景

サードパーティからのGmail送信が規制*1されていく中で、簡易的にGmail送信をできる方法を考えてみました。以下では、iOSのショートカットアプリ(旧 workflowアプリ)とGoogleAppsScript (以下、GAS)を使ってGmailでの送信を実装しています。

試行手順

GASとは/GASアカウント取得

  • GAS導入を丁寧に解説していただいている記事がございましたので、本記事では割愛させていただきます。

プロジェクトの作成

  • GASコンソールから、新規にプロジェクトを作成してください。(プロジェクト名はお好きに) 1.png

コードコピー

  • サンプルコード を新規プロジェクトのコード.gsにコピーしてください。(コード名はお好きに) 2.png

環境変数設定(スクリプトのプロパティ)

  • コード表示部の上、[ファイル]→[プロジェクトのプロパティ]→[スクリプトのプロパティ]
    • _DEST_ADDRESS:送信先のメールアドレス(サンプルコードでは、固定の宛先に送る仕様です)
    • _TOKEN:クライアント側(iOSショートカットアプリ)と共有するパスフレーズ(共有鍵)(パスフレーズは英数字記号を混在をオススメします)
  • 2つのパラメタを下記の様に設定します。 3.png

ウェブアプリケーションの公開

  • 公開範囲の設定を、リスク*3をご理解の上、 "全員(匿名ユーザを含む)"で公開にするとショートカットアプリからのAPI(HTTP(S) POSTリクエスト)を受けることができます。 4.png
  • 公開後、画面にURLが表示されるので、コピーして手元に残しておいてください。
  • 今回は実装までの最短手順を示しているため、全体公開にしてますが、本来であれば、Restlet Client等のテストツール使用し、検証さることを推奨します。

認証

  • 上記の公開範囲の設定の途中で以下のポップアップが表示される可能性があります。 5.png
  • これは、コード内部でMailApp.sendEmail使用してメール送信を実装しているため表示されています。
  • スクリプトからGoogleアカウントへメール関連の認可の可否が尋ねられるので、確認してください。*2 6.png

ショートカットアプリのレシピ作成

iOS側の環境変数設定

  • リマインダー"環境変数"リストを作成
  • 以下、2つのタイトルでタスクを作成し、それぞれのメモ欄にパラメタを設定ください
    • タイトル「gas_webhook2gmail_apikey」
    • タイトル「gas_webhook2gmail_url」
      • メモ欄:GASの公開時に表示されたURLを設定します
  • この2つ環境変数が他者に流出しますと、ご自分のメールアドレスの送信を不特定多数の第三者に悪用されてしまう可能性がありますので、厳格に管理ください

    準備完了

  • 以上で、サーバ/クライアントの準備は終わりました。ショートカットアプリからレシピをタップし、結果でsuccesと出ればメールが飛んでいるはずです。

解説

Webhook2Gmail.jpg

全体構成

  • トリガーは、iPhoneウィジェットのタップにより実行
  • ショートカットアプリにて、メッセージの入力、JSONリクエストの組立を行い、 GAS側の公開URLへHTTP(S)のPOSTリクエストで実施。
  • GAS側でJSONからパラメタ取得、ハッシュチェックを実施、Gmail送信apiをコール。
  • Gmailからスクリプトのプロパティで設定した送信先アドレスへメールが送信されます。

コード

  • PropertiesService.getScriptProperties().getProperty
  • プライベートキーをコードに書きたくなかったので、スクリプトのプロパティに変数として設定しておき、getすることでコードの公開時に手作業で消す煩雑な作業を無くしました。setコマンドもあるので、ちょっとしたパラメタはスプレッドシートでは、なくてもいいかもしれません。
  • doPost(e)
    • 本コードのメイン関数。GASのお作法としてfunction名をdoPostにすることで、POSTメソッドを受け取った際に、当該関数が呼び出される。仮引数eは、クライアントから渡されたオブジェクトを指す。doGetと記載するとGETメソッド時の処理を書ける。
  • jsonParse(e,parseName)
    • GASではPOSTで受け取ったオブジェクトをContent-Typeで判断せず、一律FileUploadクラスとして扱っているため、一度まとめてparamsの箱に格納し、その後対象パラメタに絞り取得している。
  • tokenCHK(auth)
    • JSONのauthパラメタと、サーバ側の現在日時と事前に設定したパスフレーズを足してハッシュ化し比較。これは、通信経路上で盗聴したリクエストを再送した、リプレイ攻撃対策として、現在時間をなんちゃってOTPとして扱いクライアント、サーバ双方で計算するようにしました。
  • toGmail(mailBody)
    • JSONのmailbodyパラメタから取得した値を仮引数として受けて、MailAppクラスを使いメール送信を実施している。今回のサンプルでは、送信先メールアドレスやメールタイトルは、固定にしていますが、実装次第では可変にすることもできると思います。

ショートカットレシピ

  • レシピを眺めれば日本語で書いてあるので、基本説明不要かと思いますが、サンプルコード同様に公開することを前提に書いていたのでプライベートキーについてはリマインダーを読み込むことで実現しています。

残課題

  • URLとパスフレーズの2つで認証としているが、URL自体は通信経路で盗聴されていれば、誰でも取得できるものなので、認証強度が低い。
  • 短時間の同一IPからの施行、連続認証失敗等の不正攻撃時特有の振る舞い検知の仕組みがない。

まとめ

サービスとしては車輪の再発明でしたが、個人的にJSON解析やAPIを全体公開をする際の留意する点などを考えることができたという意味で、勉強にはなりました。 方式を考えて行く中で、WEBAPIで実装すべきセキュリティ観点って意外と日本語でまとまってないなと感じました。広義ではAPIWEBサービスですので、参考にすべき資料はいっぱいあるのですが、、ジャストでこれというものがないという印象。(ググり力不足かもしれません)

余談

  • 今回クライアント側に使用した、iOSのショートカットアプリ、便利でいいのですがアプリ名が汎用的過ぎてググるのに苦労します。もっとユニークな名前を付けてほしかったです 笑
  • コードを書いた時間<ショートカットのレシピ作成時間<この記事の執筆時間
  • もっと記事をサクッと書けるようになりたい。。。

参考にさせていただいた記事

REST セキュリティに関するチートシート

Qitta - スプレッドシートで覚えるブロックチェーン |「もしかして渡した値」「入れ替わってる!?」

Google Apps ScriptのdoPostでJSONなパラメータのPOSTリクエストを受ける

Qitta - 【GoogleAppsScript】doPost のリクエストパラメータでHTTPヘッダを取得することはできない

GASへのPOSTリクエストの返り値の受け取り方

Google Apps Scriptをウェブアプリケーションとして公開する手順

Qitta - GASでのメール送信についてまとめてみる


1: workflow標準のレシピでも以前はGmail連携はできていましたが、左記の事情により現状連携ができなくなっているみたいですGoogle、個人情報へのサードパーティーアプリからのアクセス制限を強化

2:認可時に表示された(認証した)Googleアカウントを GASの送信元としてメールは送信します

3:ここでは、iOSのアプリからのアクセスを許容するため、公開範囲を全体にしています。多少のセキュリティ実装はあれど、ご自分のgmailを不特定多数がアクセスできるインターネット上に晒すことに繋がります。セキュリティリスク(不正送信、なりすまし等)のご理解の上、ご使用ください。