カテゴリー: Tips

  • Google Apps ScriptのWebアプリで「特定の人だけ見られる」を作ろうとしてハマったこと

    自分用のダッシュボード(実測値を表示するだけの小さなWebアプリ)を、Google Apps Scriptで作って運用しています。今回、「特定の人だけ見られるようにする」という、ごく普通に思える要件で、実際にはかなりハマりました。同じことをやろうとしている人向けに、原因と対処を記録します。

    やりたかったこと

    • GASのWebアプリを、不特定多数ではなく、決まった数人のGoogleアカウントだけに見せたい
    • 見る人のアカウントは、Google Workspace(組織用)のアカウントと、個人のGmailアカウントが混在している

    最初にやったこと(うまくいかなかった)

    GASのコードで、アクセスしてきた人のメールアドレスを取得して、許可リストと照合する方法を試しました。

    function isAllowedViewer_() {
      var email = Session.getActiveUser().getEmail();
      return ALLOWED_LIST.indexOf(email) !== -1;
    }

    デプロイ設定は「Webアプリを実行するユーザー」を「アクセスしているユーザー」にし、アクセス権は「全員」にしました。これで動くはずでした。

    実際に起きたこと

    許可リストに入っているはずのアカウントでも、アクセスが弾かれました。

    原因を調べると、Session.getActiveUser() は、スクリプトの所有者と、アクセスしている人が同じGoogle Workspaceドメインに属していないと、正しい値を返さないという仕様でした。個人のGmailアカウントや、別の組織のWorkspaceアカウントからアクセスすると、メールアドレスが空文字になります。

    「アクセスしているユーザーとして実行する」に設定を変えても、この制限は変わりませんでした。OAuthスコープにuserinfo.emailを追加しても、今度は新規ユーザーの同意フローが正しく走らず、別のエラーになりました。

    実際に機能した代替案

    最終的に、GoogleアカウントでのIDの判定は諦めて、URLに秘密のキーを埋め込む方式に切り替えました。

    function isAllowedViewer_(params) {
      return params.key === SECRET_KEY;
    }

    「誰がアクセスしているか」ではなく「そのURLを知っているか」で判定する方式です。ブックマークするURLに?key=...を付けてもらう運用にすれば、実用上は十分機能します。

    もう1つハマったこと: ページ内リンクがドメイン専用URLになる

    Webアプリ内のリンク(「ページを移動する」ボタンなど)を作るとき、ScriptApp.getService().getUrl()でURLを取得して使っていました。ところが、Google Workspaceのアカウントで実行すると、このURLがそのドメイン専用の形式(/a/ドメイン名/macros/...)になることがあります。このURLは、別のドメインのアカウントからアクセスすると正しく機能しません。

    対処法は、URLを正規化する関数を挟むことです。

    function toGenericWebAppUrl_(url) {
      return String(url || '')
        .replace(/\/a\/macros\/[^\/]+\//, '/macros/')
        .replace(/\/a\/[^\/]+\/macros\//, '/macros/');
    }

    分かったこと

    • GASの「アクセスしている人を識別する」機能は、同じ組織(同じWorkspaceドメイン)内で完結する用途では機能するが、個人アカウントや複数組織が混ざる場面では前提が崩れる
    • 「認証」と「認可」を、Googleアカウントの識別に頼らず、URLキーのような単純な方式に切り替えると、動作の予測がしやすくなる
    • getService().getUrl()は、実行しているアカウントによって返り値の形式が変わることがある。正規化してから使う方が安全

    もっと詳しく知りたい方へ

    この手の「公開直前に気づいて直した」実測は、note側でも記録しています。MISSION LOGシリーズにまとめています。

    注記

    この記事の内容は、自分が運営しているサービスの技術検証を一般化したものです。特定の組織・業務に関する情報は含んでいません。

  • 公開前チェックで実際に使っている正規表現パターン集|メールアドレス・APIキー・郵便番号など

    わたしは匿名でAIに小さな事業を任せる実験をしていて、公開前に必ず「身バレ要素・機密情報が本文や画像に残っていないか」を機械で走査しています。

    その走査スクリプトの中核は正規表現です。ここでは、実際に使っているパターンのうち、汎用性が高く他の用途にも転用しやすいものを抜き出してまとめます。

    なぜ正規表現で検出するのか

    目視でのチェックには限界があります。自分の書いた文章は「見慣れすぎて」いて、誤りやすい箇所ほど目が滑ります。機械的なパターン照合は、見慣れているかどうかに関係なく同じ精度で動きます。

    メールアドレスらしき文字列

    [A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}

    一般的なメールアドレスの形式に広く一致します。誤検出(メールアドレスではない文字列を拾ってしまう)はある程度許容し、見逃しをゼロに近づける方を優先する設計です。

    日本の郵便番号

    \d{3}-\d{4}

    「123-4567」のようなハイフン区切りの形式に一致します。フォームの入力検証や、文書内の郵便番号の抽出に使えます。

    電話番号(国内・ハイフンあり)

    0\d{1,4}-\d{1,4}-\d{4}

    市外局番の桁数がまちまちなため、厳密な検証には向きませんが、「電話番号らしき文字列がどこかに残っていないか」を洗い出す用途には十分です。

    APIキー・トークンらしき文字列

    [A-Za-z0-9_-]{32,}

    32文字以上の英数字・アンダースコア・ハイフンの連続は、APIキーやトークンである可能性が高い文字列です。誤検出も多いパターンですが、「公開前に人間が二重チェックする対象を絞り込む」用途では十分に機能します。

    Windowsのユーザー名を含むパス

    C:\\Users\\[^\\]+\\

    スクリーンショットやログファイルに、実在するWindowsユーザー名を含むパスが写り込むことがあります。パスの形式そのものを検出することで、中身の文字列に依存せずに検出できます。

    実際の運用での使い方

    これらのパターンは、次のような流れで使っています。

    1. 公開予定のフォルダ・ファイルを丸ごと走査する
    2. 一致した箇所を、ファイル名と行番号つきで一覧表示する
    3. 「誤検出かどうか」の判断は人間が行う(機械は候補を絞るだけ)

    正規表現だけで完結させようとしないことが重要だと考えています。誤検出・見逃しのどちらも一定数は避けられないため、「機械が候補を絞り、人間が最終判断する」という役割分担にしています。

    画像の中身は正規表現では検出できない

    念のため書いておくと、上記のパターンはすべてテキストに対する検出です。画像の中に写り込んだ文字(スクリーンショット内のメールアドレスなど)は、この方法では検出できません。実際にこの弱点が原因で、機械のチェックをすり抜けた事故が過去に1件あります。画像は必ず目視での確認を別途行っています。

    まとめ

    パターン 用途
    [A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,} メールアドレスの検出
    \d{3}-\d{4} 郵便番号の検出・入力検証
    0\d{1,4}-\d{1,4}-\d{4} 電話番号らしき文字列の検出
    [A-Za-z0-9_-]{32,} APIキー・トークンらしき文字列の検出
    C:\\Users\\[^\\]+\\ Windowsユーザー名を含むパスの検出

    いずれもコピーしてそのまま使えます。用途に合わせて調整してください。

    公開前チェックの仕組み全体(走査スクリプトの全文・審査専門のAIエージェント定義)は、noteの記事「公開ボタンを押す前に、身バレを機械で探すスクリプトを書いた」に置いています。