17 人が閲覧(直近 30 日) セキュリティ

GASのセキュリティ|危険とされる理由とOAuthスコープ・公開設定・管理者統制の実務

GASのセキュリティ|危険とされる理由とOAuthスコープ・公開設定・管理者統制の実務

GAS(Google Apps Script)は、コードを書いた本人ではなく承認したGoogleアカウントの権限で動きます。この一点を押さえないまま「次のユーザーとして実行=自分」「アクセスできるユーザー=全員」でウェブアプリを公開すると、URLを知っただけの第三者が、オーナー権限でウェブアプリの処理を呼び出せる状態が生まれる。シートを読んで返す処理が1つでもあれば、その中身は第三者に届きます。承認画面に並ぶhttps://www.googleapis.com/auth/script.external_requestのようなスコープURLは、その権限の範囲を示す一覧です。本記事では、ウェブアプリの公開設定、スコープURLの意味と権限エラーの直し方、認証情報の置き場所、管理コンソールの統制までを、Googleの公式ドキュメントの記述に沿って整理します。

まとめ:GASの実行権限・公開範囲・スコープと運用上の安全対策

  • 実行ユーザー:マニフェストのexecuteAsがUSER_DEPLOYINGならオーナー権限で常時動く。利用者を識別したいならUSER_ACCESSINGを選ぶ。
  • 公開範囲:accessのANYONE_ANONYMOUSは「ログインしていないユーザーも含む」という定義。URLは秘密情報として扱えない。
  • OAuthスコープ:script.external_requestは外部URLへの通信(UrlFetchApp)の許可、spreadsheetsは全スプレッドシートの読み書き。appsscript.jsonで明示し、spreadsheets.currentonlyのような狭いスコープへ絞る。
  • 権限エラー:「Required permissions: …script.external_request」は、明示指定の書き漏れ・単純トリガーからの呼び出し・利用者がスコープを外して同意した、のいずれかで起きる。
  • 認証情報:スクリプトプロパティは「スクリプト、アドオン、ウェブアプリのすべてのユーザー」で共有される。利用者ごとの秘密には使えない。
  • 管理者統制:監査ログは「ドライブのログイベント」、利用人数と利用プロジェクト数の推移は「アプリレポート>Apps Script」(過去6か月)。Apps Scriptは再認証頻度の設定を強制しないため、トリガーはセッション制御の外で動き続ける。
  • 前提の変化:旧Rhinoは2026年1月31日で実行停止済み。2026年6月23日以降、Apps ScriptはGoogle Workspaceのコアサービス。

GASの実行権限モデル

承認アカウントの権限を借りる実行の仕組み

GASはブラウザやPC上ではなくGoogleのインフラで実行されます。スクリプトがスプレッドシートやGmailに触れるとき使われるのは、そのスクリプトを承認したアカウントのOAuthトークンです。「スクリプトに権限がある」のではなく「承認した人の権限をスクリプトが借りている」状態だと捉えてください。

Googleサービスの承認ガイドによれば、Apps Scriptはスクリプトを走査して使用サービス(SpreadsheetAppやGmailAppなど)を特定し、必要なスコープを決めます。同ガイドはコメントアウトしたコードでも承認要求が発生しうると明記しており、消したつもりのデバッグ行が権限を広げたままになることがある。誰の権限で動くかは種類で決まり、スタンドアロンやドキュメントにバインドしたスクリプトは操作中のユーザー、カスタム関数は匿名ユーザー、インストール型トリガーはトリガーを作成したユーザーです。この委譲は標準的なOAuth 2.0の認可フローであり、OAuth 2.0とは?仕組み・認可フローと認証との違い、OAuth 2.1の変更点をわかりやすく解説で扱う「認証」と「認可」の区別がそのまま当てはまります。

「GAS Interpreter」の実体はApps Scriptの実行ランタイム

検索で見かける「GAS Interpreter」は、Apps Scriptの公式ドキュメントに出てくる製品名ではありません。本記事では独立した製品として扱わず、実体に近いApps Scriptの実行ランタイムとして整理します。Apps Scriptは以前MozillaのRhino JavaScriptインタープリタで動作しており、2020年2月18日以降、自動互換性テストに合格した既存スクリプトが段階的にV8ランタイムへ移行されました。

そしてこの選択肢はすでに残っていません。サンセット一覧によると、Rhinoランタイムは2025年2月20日に非推奨となり、「Rhino で実行されているスクリプトは 2026 年 1 月 31 日まで引き続き機能しますが、それ以降は実行されなくなります」と明記されています。appsscript.jsonのruntimeVersionに"DEPRECATED_ES5"を指定したスクリプトはもう動きません。注意したいのはマニフェストのリファレンスで、省略時の既定値STABLEを今も「currently Rhino」と説明しています。ページ間で記述がそろっていない以上、runtimeVersionは省略せず"V8"と明示しておくのが安全です。

V8ランタイムでもブラウザ由来のsetTimeout、fetch、Blob、Crypto APIは使えません。外部通信はUrlFetchApp.fetch、ハッシュや署名はUtilities.computeDigest()とUtilities.computeHmacSha256Signature()で代替します。つまり外へ出るデータの経路はUrlFetchAppとMailAppなどGoogleのサービス経由に限られ、統制もそこに掛ければよい。GASそのものの機能範囲はGAS(Google Apps Script)とは?できること・制限・始め方を開発者目線で解説にまとめています。

ウェブアプリの実行ユーザーと公開範囲が事故の分岐点

「自分として実行」×「全員」が最も危険な組み合わせ

ウェブアプリのデプロイでは「次のユーザーとして実行」と「アクセスできるユーザー」を別々に選びます。ウェブアプリのガイドは前者について、「自分として実行」はスクリプトのオーナーであるユーザーとしてスクリプトが常に実行される、「ウェブアプリにアクセスするユーザーとして実行」はアクティブユーザーのIDでスクリプトが実行されると説明しています。危険度は組み合わせで決まります。

実行(executeAs) 公開範囲(access) 動く権限 主なリスク
自分(USER_DEPLOYING) 全員(ANYONE_ANONYMOUS) オーナー 未ログインの第三者がオーナー権限で実行
自分(USER_DEPLOYING) Googleアカウントを持つ全員(ANYONE) オーナー 社外アカウントでも実行可能
自分(USER_DEPLOYING) 組織内の全員(DOMAIN) オーナー 全社員がオーナーのデータへ到達
アクセスするユーザー(USER_ACCESSING) Googleアカウントを持つ全員(ANYONE) 実行者本人 利用者ごとの承認が必要
アクセスするユーザー(USER_ACCESSING) 組織内の全員(DOMAIN) 実行者本人 承認頻度の制限に当たる場合あり
アクセスするユーザー(USER_ACCESSING) 自分のみ(MYSELF) 実行者本人 実質的に個人利用

マニフェストの定義はANYONEが「ログインしているすべてのユーザー」、ANYONE_ANONYMOUSが「ログインしていないユーザーも含むすべてのユーザー」です。デプロイ画面の「全員」は後者にあたり、URLさえ手元にあれば実行できる。URLをアクセス制御の手段として扱うのは誤りで、共有時のチャットログ、ブックマーク同期、社外へ転送されたメールのいずれからも漏れます。承認ガイドは「スクリプトオーナーとして動くウェブアプリにアクセスした場合は承認を求められないことがある」とも書いており、ログイン画面の有無だけで判断せず、匿名アクセスが許可されているか、公開される処理とデータが要件に合っているかを確認してください。またウェブアプリのガイドは、不正使用を防ぐためユーザーとして実行されるウェブアプリを新しいユーザーが承認できる頻度に制限が設けられていると明記しています。社内へ一斉展開する際は、承認が集中して弾かれる可能性を見込んでおきます。

実行者設定とgetActiveUserの取得可否

「誰が実行したか」を記録する設計は、実行ユーザーの設定に縛られます。Sessionクラスのリファレンスによると、Session.getActiveUser().getEmail()は次の場面で空文字を返します。

  • 単純トリガーのonOpen(e)、onEdit(e)
  • Googleスプレッドシートのカスタム関数
  • 「自分として実行」でデプロイされ、利用者ではなく開発者が承認したウェブアプリ

ただし開発者自身が実行する場合や、アクセスした人がスクリプトオーナーと同じGoogle Workspaceドメインに属している場合は、これらの制限が通常は適用されません。同じ設定でも、社内からのアクセスではメールアドレスが取れ、社外からのアクセスでは空になる。getActiveUserを認可の唯一の根拠にしてはいけない理由がここにあります。一方Session.getEffectiveUser()はスクリプトが動いている権限の持ち主を返し、「自分として実行」のウェブアプリでは開発者のアカウント、インストール型トリガーではトリガー作成者のアカウントになります。

監査証跡を残したいなら、実行者を「アクセスするユーザー」にしたうえで、入口で呼び出し元を確認します。次の例では、許可するドメインへの書き換えとindex.htmlの作成が必要です。google.script.runから呼び出す公開関数にも、操作ごとの認可チェックを設けてください。

function doGet(e) {
  const email = Session.getActiveUser().getEmail();
  if (!email || !email.endsWith('@example.co.jp')) {
    return ContentService.createTextOutput('forbidden');
  }
  console.log('access by %s', email);
  return HtmlService.createHtmlOutputFromFile('index');
}

「自分として実行」のまま社外へ公開するとemailが空になり、条件式が成立して全員が弾かれます。逆に判定を外せば全員が通る。console.logの出力先は実行ログ(Cloud Logging)です。

OAuthスコープの意味と権限エラーへの対処

主要なスコープURLと許される操作

承認画面やエラーに出るhttps://www.googleapis.com/auth/…の文字列が、OAuthスコープです。どのメソッドがどのスコープを要求するかは、各サービスのリファレンスの「Authorization」欄に書かれています。たとえばUrlFetchAppのリファレンスは、fetchを使うスクリプトにscript.external_requestでの承認を求めています。よく出るものを並べると次のとおりです。

スコープ(auth/以降) 要求する主なメソッド 許される範囲
script.external_request UrlFetchApp.fetch 任意の外部URLへの通信
spreadsheets SpreadsheetApp.openById 全スプレッドシートの閲覧・編集・作成・削除
spreadsheets.currentonly SpreadsheetApp.getActiveSpreadsheet スクリプトが入っているシートのみ
spreadsheets.readonly Sheets APIの読み取り操作 全スプレッドシートの閲覧
drive DriveApp.getFiles ドライブの全ファイルの閲覧・編集・削除
drive.file アプリで扱うファイル そのアプリで使うファイルのみ
script.send_mail MailApp.sendEmail 本人としてメール送信
script.scriptapp ScriptApp.newTrigger トリガーの作成(不在時の実行)
userinfo.email Session.getActiveUser 本人のメールアドレス

注意したいのはscript.external_requestの広さです。このスコープは通信先のURLを限定しません。同じスクリプトがspreadsheetsも持っていれば、全スプレッドシートの中身を任意のサーバーへ送れる組み合わせになる。承認画面で外部通信と広い読み取り権限が並んでいたら、どこへ何を送るのかをコードで確認してから承認してください。Slackなどへの通知で外部通信を使う場面は多く、送信側の実装はSlack Incoming Webhookの実装手順:URL発行・Block Kit整形・SDK送信と429対策が参考になります。

スプレッドシートの差も実務では大きく、getActiveSpreadsheet()はspreadsheets.currentonlyで動きますが、openById()はspreadsheetsしか受け付けません。IDで別のシートを開く処理が1行でもあると、全スプレッドシートへの権限が必要になります。バインドしたシートだけで完結するなら、ファイル冒頭に/** @OnlyCurrentDoc */を書くと、承認ダイアログが現在のファイルへのアクセスだけを求めるようになります。

「Required permissions: …script.external_request」の原因と直し方

UrlFetchApp.fetchを呼んだ行で次のエラーが出る事例が広く報告されています。

Exception: You do not have permission to call UrlFetchApp.fetch.
Required permissions: https://www.googleapis.com/auth/script.external_request

原因は3つに分かれます。症状は同じでも直し方が違うので、どれに当たるかを先に切り分けてください。

原因 見分け方 直し方
oauthScopesの書き漏れ appsscript.jsonにoauthScopesがある スコープを追記し再承認
単純トリガーから呼んだ onEdit・onOpenの中で失敗 インストール型トリガーへ
利用者がスコープを外して同意 一部の利用者・トリガーだけ失敗 requireScopesで再要求

1つ目は明示スコープの書き漏れです。oauthScopesを明示した時点で自動判定による追加は止まるため、あとからUrlFetchAppを書き足すと権限が足りなくなる。プロジェクトの設定で「『appsscript.json』マニフェスト ファイルをエディタで表示する」にチェックを入れ、配列にscript.external_requestを足します。追記しただけでは許可されないので、エディタから関数を一度実行し、表示される承認ダイアログで許可するところまでが手順です。

2つ目は単純トリガーの仕様です。トリガーのガイドは単純トリガーの制限として「承認が必要なサービスにはアクセスできない」「30秒を超えて実行できない」を挙げています。onEditという名前の関数から外部APIを呼ぶと、スコープを正しく書いていても失敗する。関数名をonEdit以外に変え、トリガー画面から「編集時」のインストール型トリガーとして登録すれば、作成者の権限で動きます。

3つ目は次の節で扱うスコープ単位の同意によるものです。エラーが出たときに「とりあえずhttps://www.googleapis.com/auth/driveやspreadsheetsも足しておく」対応はしないでください。足りないのは要求されたスコープ1つで、それ以外を足すほど承認画面が重くなります。

スコープ単位の同意(granular consent)への備え

スコープの解説ページによると、スコープを1つずつ選んで許可できる同意画面はエディタ、マクロ、トリガー、ウェブアプリ、アドオンを含むすべての実行面でサポートされています。Google Workspace Updatesによると、エディタからの実行では2025年1月8日に始まり、公開済みのウェブアプリとApps Script製のWorkspaceアドオンへは2026年1月7日から段階的に広がりました。利用者はGmailを外してカレンダーだけ許可する、といった部分的な同意ができる。管理者側でこの画面を止める設定はなく、新しく承認するときだけ適用され、承認済みの権限には影響しません。セキュリティ上は利用者に有利な変更ですが、スクリプト側は「要求したスコープが全部そろっている」前提で書けなくなりました。

影響が最も大きいのはインストール型トリガーです。同ページは、トリガーは利用者の操作なしにバックグラウンドで動くため途中で権限を求められず、許可されていないサービスに触れた時点で「Authorization is required to perform that action.」のエラーで即座に失敗すると説明しています。推奨されているのは、トリガーを設置する関数の先頭でScriptApp.requireScopes()を呼び、必要な権限がそろうまでトリガーを作らせない書き方です。

以下はスプレッドシートにバインドしたプロジェクトで使う、トリガー設置部分の例です。編集時の処理を行うnotifyOnEdit(e)は別途実装してください。後掲のマニフェスト例と組み合わせる場合は、oauthScopesにhttps://www.googleapis.com/auth/script.scriptappも追加し、設置するアカウントで必要なスコープを承認します。

function installTrigger() {
  ScriptApp.requireScopes(ScriptApp.AuthMode.FULL, [
    'https://www.googleapis.com/auth/script.scriptapp',
    'https://www.googleapis.com/auth/script.external_request',
    'https://www.googleapis.com/auth/spreadsheets.currentonly'
  ]);
  ScriptApp.newTrigger('notifyOnEdit')
    .forSpreadsheet(SpreadsheetApp.getActive())
    .onEdit()
    .create();
}

requireScopes()は、指定したスコープのどれかが欠けていれば実行を止めて承認画面を出します。ScriptAppのリファレンスは、スクリプトが必要としないスコープや無効なスコープを渡すとエラーになるとしているので、ここにはそのプロジェクトが要求するスコープだけを指定してください。明示指定している場合はoauthScopesに含め、自動判定の場合は「プロジェクトの OAuth スコープ」で確認します。欠けている権限に応じて機能を無効化したい場合は、ScriptApp.getAuthorizationInfo()で承認状況と承認用URLを取得して自前の案内を出します。

appsscript.jsonでスコープを固定する手順

スコープの解説ページは、自動判定は多くのスクリプトで十分だとしつつ、公開するアドオン、ウェブアプリ、Chatアプリでは直接の制御が必要で、自動判定が必要以上に広いスコープを割り当てることがあると警告しています。現在のスコープは、エディタ左の「概要」の「プロジェクトの OAuth スコープ」で確認できます。

{
  "timeZone": "Asia/Tokyo",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/spreadsheets.currentonly",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/userinfo.email"
  ]
}

3行目のuserinfo.emailは見落としやすい行です。前の章のdoGetのようにSession.getActiveUser()を使うなら必須で、書き漏らすと実行時に権限エラーになる。明示指定に切り替えるときは、使っているメソッドのリファレンスで必要スコープを1つずつ確認してください。承認画面に「すべてのGoogleスプレッドシートの表示、編集、作成、削除」と出たら、スコープが広すぎるサインです。

承認ガイドは、機密性の高いスコープを使うウェブアプリやその他のスクリプトはGoogleによる審査の対象となり、承認時に「未確認のアプリ」という警告画面が表示されることがあるとも説明しています。社内展開でこの画面が出ること自体は異常ではありません。ただし利用者に「詳細→安全ではないページに移動」を毎回踏ませる運用は、フィッシングへの耐性を組織的に下げる。警告が出たら、要求スコープが必要最小限かを確認し、公開対象とOAuthクライアントの設定に応じてGoogleの確認手続きが必要かを調べてください。承認した権限は、利用者がGoogleアカウントの「セキュリティ」>「サードパーティ製のアプリとサービスとの接続」から削除できます。

認証情報の置き場所とスクリプトプロパティの共有範囲

外部APIのキーをコードに直書きすると、スクリプトの閲覧権限を持つ全員に見え、コピーされたプロジェクトにも残ります。代わりに使うのがPropertiesServiceですが、3種類のストアで誰に共有されるかが違う点を取り違えると別の穴になる。

ストア 取得メソッド 共有される範囲 公式の用途例
スクリプトプロパティ getScriptProperties() すべてのユーザー 外部データベースの認証情報
ユーザープロパティ getUserProperties() 現在のユーザー ユーザー固有の設定
ドキュメントプロパティ getDocumentProperties() 開いている文書のアドオン利用者 ドキュメント固有のデータ

プロパティサービスのガイドはスクリプトプロパティの共有範囲を「スクリプト、アドオン、ウェブアプリのすべてのユーザー」と定義しています。利用者ごとに異なる秘密(個人のAPIトークンなど)をスクリプトプロパティに入れてはいけません。それはユーザープロパティの役割です。さらにスクリプトプロジェクトの編集権限を持つ人は、エディタのプロジェクト設定画面からスクリプトプロパティの値を読める。「暗号化された保管庫」ではなく「コードから分離しただけの置き場」です。

const props = PropertiesService.getScriptProperties();
const endpoint = props.getProperty('EXTERNAL_API_URL');
if (!endpoint || !endpoint.startsWith('https://')) {
  throw new Error('EXTERNAL_API_URLに承認済みのHTTPS接続先を設定してください');
}
const apiKey = props.getProperty('EXTERNAL_API_KEY');
const res = UrlFetchApp.fetch(endpoint, {
  headers: { Authorization: 'Bearer ' + apiKey },
  muteHttpExceptions: true
});

値はすべて文字列として保存されるため、オブジェクトはJSON.stringifyを通して入れます。スクリプトプロジェクトの共有相手を絞ることが、この保護の実効性をそのまま決めます。キーの価値が高い場合は、GAS側に置かず外部の認証基盤を経由させる設計に切り替えてください。

HtmlService製UIのサンドボックス制約と遷移の実装

ウェブアプリの画面をHtmlServiceで作る場合、サンドボックスモードはIFRAMEに一本化済みです。HTMLサービスの制限事項には、NATIVEおよびEMULATEDモードは廃止され、以前にこれらを使用していたアプリは自動的にIFRAMEモードを使用するようになると記載されています。

IFRAMEサンドボックスに設定されるキーワードはallow-same-origin、allow-forms、allow-scripts、allow-popups、allow-downloads、allow-modals、allow-popups-to-escape-sandbox、そしてスタンドアロンのスクリプトプロジェクトに限りallow-top-navigation-by-user-activation。一方でallow-top-navigationは設定されていません。JavaScriptから勝手にトップレベルのリダイレクトを行うことはできない、という意味です。各キーワードの意味はiframeとは?sandbox属性14種とCookie分割の実装判断を解説で整理しています。

実装側では、リンクのtarget属性に_topまたは_blankを指定するか、<head>内に<base target="_top">を置いて既定を上書きします。これを忘れると、リンクを押してもiframeの内側で遷移しようとして何も起きない。スクリプトや外部スタイルシートなどのアクティブコンテンツはHTTPS経由で読み込む必要があるとも明記されており、社内の古い管理画面をHTTPで参照させる実装は読み込まれません。

これらはブラウザ側の枠組みにすぎず、受け取った値の検証は自前で行う必要があります。フォームから受けた文字列をそのままinnerHTMLへ流せば、IFRAMEモードでもスクリプトは動く。ブラウザで文字列を表示するときはtextContentなどを使います。HtmlServiceのテンプレートでは、出力先の文脈に応じて自動エスケープする<?= ... ?>を使い、未信頼の値を強制出力する<?! ... ?>へ渡さないでください。

ライブラリとコピペコードが持ち込むリスク

他人のスクリプトIDを指定してライブラリとして取り込むと、そのコードは自分のプロジェクトの承認済み権限の中で動きます。ライブラリのガイドは、ライブラリを含むスクリプトをデバッグする場合、ライブラリのコードにステップインしたりブレークポイントを設定したりはできないとし、HEAD(開発モード)を使っても同じだと補足しています。

そのHEADがもう一段のリスクになります。ライブラリの編集権限を持つ利用者がHEAD(開発モード)を選ぶと、保存された最新コードが参照されます。共同編集者がライブラリを保存した瞬間に、呼び出し側の本番スクリプトの挙動も通知なしに変わる。呼び出し元からステップ実行で追えないうえに即座に反映されるので、本番で使うライブラリはバージョン番号で固定するのが原則です。数十行の共通処理なら、依存を増やすよりプロジェクト内へ取り込むほうが監査しやすい。

ブログ記事からコピーしたスニペットも同じで、見知らぬエンドポイントへPOSTする1行があれば承認済み権限で取得したデータの外部送信になります。取り込む前にUrlFetchAppとMailAppの呼び出し箇所を必ず読み、承認画面にscript.external_requestやscript.send_mailが新しく加わっていないかを確認してください。

管理者側の統制:監査ログ・アプリレポート・外部接続の許可リスト

監査ログとアプリレポートでの把握

個々の開発者の心がけだけでは、退職者が残したトリガーや誰も把握していない自動処理は消えません。管理コンソール側の手段は次のとおりです。

  • 監査ログ:レポート>監査と調査>ドライブのログイベントで、「ドキュメントタイプ」フィルタに「Googleスクリプト」を指定して検索します。
  • 利用状況:レポート>アプリレポート>Apps Scriptで、Apps Scriptを使った人数と利用プロジェクト数の日別推移を過去6か月分見られる(すべてのスクリプト実行を含む集計)。個々のスクリプトと持ち主の一覧ではない点に注意してください。データはダウンロードできます。
  • 組織全体の有効/無効:Apps Scriptを組織内のユーザー全体に対して有効または無効にできます。
  • 特定プロジェクトの停止:関連付けられているCloudプロジェクトを削除すると、そのスクリプトプロジェクトの実行がすべて停止する(削除にはそのCloudプロジェクトの権限が必要)。

見落とされやすいのが再認証です。承認ガイドは、Apps ScriptはGoogle Cloudのサービス設定で構成した再認証の頻度を強制しないと明記しています。トリガーは利用者の操作なしに自動で動くため、たとえば12時間ごとの再認証を設定していても、トリガー実行では再認証が求められない。セッション制御を入れたから古い自動処理も止まる、とは考えず、トリガーを持つプロジェクトを個別に棚卸ししてください。

アプリのアクセス制御とOAuthスコープの統制

OAuthスコープ単位の統制は、セキュリティ>アクセスとデータ管理>APIの制御にある「アプリのアクセス制御」で行います。アプリはGoogle所有/社内/サードパーティの3タイプに分けられ、それぞれ信頼できる(制限付きと制限なしの両方を含むすべてのGoogleサービスへアクセス可)、限定(制限なしのサービスのみ)、特定のGoogleデータ(指定したスコープのみ)、ブロック(一切アクセス不可)から選ぶ。「内部アプリを信頼する」をオンにすると、自組織が作成した内部アプリが制限付きのGoogle Workspace APIにアクセスできるようになります。未設定のサードパーティ製アプリの既定は「任意のサードパーティ製アプリにGoogleでログインできます」なので、統制を効かせたい組織はここを既定のまま放置しないでください。GAS以外のSaaSも含めたOAuth連携の棚卸しはSaaSセキュリティ対策の実装手順|設定診断・OAuth棚卸し・ログ監視を解説で扱っています。

外部への通信先を絞る2つの許可リスト

外部への送信そのものを絞る手段は、管理者側とスクリプト側に1つずつあります。管理者側は外部接続の許可リストで、対象はApps ScriptのUrlFetchAppとスプレッドシートのIMPORTXMLなどのインポート関数です。既定ではすべてのURLへのアクセスが許可されています。設定は管理コンソール>アプリ>Google Workspace>ドライブとドキュメント>機能とアプリケーション>「URLからのインポートと取得」で、「次のURLからのみインポートと取得を許可します」を選んで保存する。登録できるURLは最大1,000件、ワイルドカードは1つだけという制約があるため、切り替え前に監査と調査の「URLへのアクセスがありました」イベントで現状の通信先を洗い出す手順が欠かせません。

対応エディションの記載はGoogleのページ間で一致していません。外部接続の許可リストの解説ページはFrontline Plus、Business Plus、Enterprise Standard、Enterprise Plus、Education Standard、Education Plus、Enterprise Essentials Plusを挙げ、開発者向けのモニタリング解説ページはBusiness Plus、Enterprise、Education Standard、Teaching and Learning Upgrade、Education Plusを挙げている。自社の契約エディションで設定項目が現れるかは、管理コンソールの該当画面で確認してください。

スクリプト側は、マニフェストのurlFetchWhitelistです。許可リストのリファレンスによると、これを定義したスクリプトは登録していないURLにアクセスできなくなります。各項目はhttps://で始まり、完全なドメインと空でないパスを持つ必要がある(https://www.google.com/は可、https://www.google.comは不可)。前方一致で判定され、パスの大文字と小文字は区別されます。管理者側の許可リストが使えないエディションでも、自分のスクリプトの通信先はこちらで固定できる。管理作業をコマンド側から自動化したい場合はgws(Google Workspace CLI)とは?Google Workspace APIをコマンド1本で操作する使い方と導入判断が参考になります。

2026年のコアサービス化とデータリージョン対応で変わった前提

Google Workspace Updatesは2026年6月23日に、Google Apps ScriptをGoogle Workspaceのコアサービスとして提供すると発表しました。Apps ScriptはGoogle Cloud利用規約およびGoogle Workspace for Education利用規約の適用対象となり、他のコアサービスと同じエンタープライズ級のデータ保護、管理者制御、標準テクニカルサポートが及びます。提供範囲はすべてのGoogle Workspaceのお客様で、すでに有効な組織では管理者の作業は不要です。

「業務データを扱う自動化をコアサービス外に載せてよいのか」という論点で情報システム部門の承認が止まっていた組織では、その論点自体が消えます。

ただし公開設定やOAuthスコープの危険性は何ひとつ変わっていません。規約の適用範囲が広がることと、匿名公開したウェブアプリが安全になることは無関係です。この機会に、アプリレポートで利用人数や利用プロジェクト数の推移を把握し、個別のスクリプトの持ち主とトリガー作成者は、管理台帳や各プロジェクトの設定と照合してください。

2026年9月16日には、Apps Scriptのデータリージョン対応が一般提供になりました。組織部門やグループにデータリージョンのポリシーを適用すると、スクリプトのファイル、マニフェスト、トリガーのメタデータ、PropertiesServiceとCacheServiceの値が指定した地域に保存され、スクリプトの実行もその地域で処理されます。対象はEnterprise PlusとFrontline Plus(保存と処理)、Education StandardとEducation Plus(保存のみ)です。管理コンソールの「データ>コンプライアンス>データリージョン」の既存ポリシーに自動で従うため、データの保存場所を理由にGASを禁止していた組織は、契約エディションを確認したうえで判断を見直せます。ただしUrlFetchAppで外部へ送ったデータは、この地域指定の外に出る点を忘れないでください。管理外の自動化が業務に入り込む構図は、シャドーAIとは?無断利用が招く情報漏洩リスクと企業の対策を解説【2026年版】で扱うシャドーITと同じ形です。

GASに載せてはいけないデータと処理の線引き

GASは無料でGoogleサービスに触れられる代わりに、載せるべきでない対象があります。事故に直結する順で並べます。

  1. 法令上の安全管理措置が必要なデータは、統制を確かめるまで置かない。 マイナンバー、クレジットカード番号、健康情報などを扱う場合は、ファイルの共有設定に加え、組織の外部共有制限や対応エディションのDLPなどを組み合わせ、必要な安全管理措置を満たせるか確認する必要があります。それがないままでは、共有リンクの誤操作1回で範囲が広がり、スクリプトプロパティも編集権限者から読める。責任分界の考え方はクラウドセキュリティとは?リスクと責任共有モデルから見た企業の守り方の責任共有モデルがそのまま当てはまり、この層の保護は利用者側の責任範囲です。
  2. 「全員」公開が避けられない用途は、書き込み専用に絞る。 社外からの問い合わせ受付のように匿名アクセスが要件になる場合、doPostで受け取って追記するだけの処理に限定し、読み出し系のメソッドをコードから外します。読み出したデータを認可チェックなしで応答に含めたり、外部から呼び出せる関数で返したりすると、第三者への漏えいにつながります。
  3. 認証の主体をGASに置かない。 スクリプトプロパティに合言葉を入れてdoGetで照合するような自前認証は、パスワード管理・失効・総当たり対策のいずれも自作になります。Googleアカウントで識別できる範囲に用途を留め、それを超える要件が出た時点で別の基盤へ移すべきです。社外に公開する画面を残す場合は、脆弱性診断・セキュリティ診断で公開範囲と入力処理を第三者に確かめてもらうのが確実です。
  4. 監査証跡が必須の処理は、実行者設定と併せて設計する。 前述のとおり「自分として実行」では社外アクセス時にメールアドレスが空になる。「誰がいつ承認したか」を残す必要がある承認ワークフローを、匿名実行のまま作らないでください。
  5. 攻撃側にも使われる前提を持つ。 Kasperskyは2021年7月30日の記事で、Apps Scriptのウェブアプリへのリンクをフィッシングに悪用する手口を報告しています。Googleの正規ドメインへのリンクはメールサーバーのフィルタをすり抜けやすい。Googleのドメインで社外向けの窓口を公開すると、利用者に「Googleのドメインだから安全」と学習させることにもなるので、社外向けは自社ドメイン配下に置いてください。

よくある質問

GASは社内で使わせても安全ですか?

プラットフォーム自体はGoogleのインフラ上で動き、コアサービス化以降は他のコアサービスと同じデータ保護と管理者制御の対象です。危険が生じるのは設定のほうで、ウェブアプリの公開範囲、OAuthスコープの広さ、外部への通信先を統制できているかで安全性が決まります。使わせる前に、アプリレポートで利用人数と利用プロジェクト数の推移を把握しておいてください。

承認画面でscript.external_requestを求められたら許可して大丈夫ですか?

このスコープは「任意の外部URLへ通信してよい」という許可で、通信先は限定されません。作成者が信頼でき、通知先や連携先のAPIが分かっているなら許可して構いません。ただし同じ画面にspreadsheetsやdriveのような広い読み取り権限も並んでいる場合は、読んだデータを外へ送れる組み合わせなので、UrlFetchApp.fetchの送信先をコードで確認してから許可してください。

ウェブアプリを「全員」に公開するとスプレッドシートの中身も見られますか?

スプレッドシート自体の共有設定が変わるわけではありません。ただしUSER_DEPLOYINGで公開した場合、スクリプトはオーナー権限で動くため、スクリプトが返す内容はすべて第三者に届きます。シート全体を読んで返す実装なら、実質的に中身が公開されているのと同じです。

退職者が作ったGASの自動処理はどうなりますか?

インストール型トリガーはトリガー作成者のアカウントの権限で実行されます。アカウントを停止・削除すれば実行も止まるのが通常ですが、業務がその処理に依存していた場合は無言で停止することになる。退職前にプロジェクトとトリガーを棚卸しし、ファイルの所有権移管に加えて、後任者のアカウントで必要な権限を承認し、インストール型トリガーを作り直してください。新しいトリガーの動作確認後に旧トリガーを削除します。自動処理だけを止めるなら、まず該当トリガーを削除します。関連付けられたCloudプロジェクトの削除でスクリプト全体を停止する場合は、同じCloudプロジェクトを利用する他の処理への影響も確認してください。

個人のGoogleアカウントでも同じセキュリティ設定になりますか?

スクリプト側の設定(実行ユーザー、公開範囲、OAuthスコープ、PropertiesService、urlFetchWhitelist)は共通です。違うのは組織側の統制で、監査ログ、アプリレポート、外部接続の許可リスト、アプリのアクセス制御はいずれもGoogle Workspaceの管理コンソールの機能。個人アカウントではこれらが使えず、公開範囲をDOMAINに絞ることもできません。業務でGASを使うなら、Workspaceのアカウントに寄せてください。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.27 テックブログ Brotli 脆弱性:CVE-2026-111497の影響確認とNode.js・Python・.NETの更新手順
  2. 2026.10.28 テックブログ リンツ不正アクセス:314万件の対象確認とパスワード強制リセット後の対処
  3. 2026.10.27 テックブログ Joshin(ジョーシン)不正アクセス:最大600万件の対象とサブスク初期パスワードの対処
  4. 2026.10.29 テックブログ So-net情報漏洩:258万件の対象確認とメールのパスワード再設定、窓口の番号
  5. 2026.10.23 テックブログ Renta!はいつ再開?不正アクセスでメールアドレス等が流出したおそれ・再開時のパスワード再設定と運営側の対応

RELATED POSTS 関連記事

目次