GAS: 末尾にアンダースコア( _ )がないサーバー側関数はクライアント側からやりたい放題

GASのHtmlServiceを使ってウェブアプリを作る際、サーバー側スクリプト(コード.gsなど)の関数名の末尾に_(アンダースコア)をつけると、ウェブアプリ側からその関数を実行できなくなります。

例えば、コード.gs内に2つの関数「doSomething」と「doSomething_」があるとします。
HTML側のjavascriptで、google.script.run.doSomething()を実行すれば「doSomething」関数を実行できますが、google.script.run.doSomething_()としても「doSomething_」関数を実行することはできません。

これを知らないと、作成したウェブアプリにセキュリティ上の問題を発生させてしまうかもしれませんので、実例を交えて説明してみます。

サンプルアプリ

スプレッドシートにユーザー・科目ごとの点数が格納されていて、
ウェブアプリにアクセスすると、自分の点数が表示されるアプリを作りました。
(この記事全編にわたり、ユーザーIDは xxx@gmail.com になっていますが、ご所属の組織のドメインを想定して読んでください)

「点数」シートにユーザー・科目ごとの点数が格納されている
ウェブアプリにアクセスすると、そのユーザーの点数が表示される(taroさんがアクセスした例)
function doGet() {
  const html = HtmlService.createHtmlOutputFromFile('index');
  return html;
}


/**
 * < ウェブアプリから呼び出す処理 >
 * ログインユーザーの点数データを取得する
 */
function getLoginUserScores() {
  const loginUserId = Session.getActiveUser().getEmail();
  const loginUserScores = getUserScoresByEmail(loginUserId);
  return loginUserScores;
}


/**
 * < ウェブアプリから呼ばない処理 >
 * 点数シートから、指定したユーザーの点数データを取得する
 */
function getUserScoresByEmail(userId) {
  // 点数シートの全データを取得(1行目はヘッダーなので除く)
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('点数');
  const scores = sheet.getDataRange().getValues().slice(1);

  // 指定したユーザーのデータのみ抽出
  const userScores = scores.filter(score =>
    score[0] === userId  
  );

  return userScores;
}
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <p>あなたの点数は...</p>
    <ul id="list"></ul>
    
    <script>
      const ul = document.getElementById("list");
      
      google.script.run.withSuccessHandler(scores => {
        scores.forEach(score => {
          const li = document.createElement('li');
          li.innerText = `${score[1]}: ${score[2]}点`;
          ul.appendChild(li);
        })
      }).getLoginUserScores();
    </script>
  </body>
</html>

GAS側の関数名は誰でも調べられる

「関数名さえバレなければ、まあ大丈夫なんじゃない?」と思うかもしれません。
しかし、ブラウザのコンソールでgoogle.script.runを入力したら、実行できる関数名が表示されてしまいました。

userHtmlFrame (userCodeAppPanel)を選択した状態で「gogle.script.run」を入力し[ Enter ]を押すと、実行できる関数の一覧を見ることができる。

HTML側からGAS側の関数を勝手に実行する

このアプリには、下記の2つの主な関数があります。

  • 1. getLoginUserScores
    Session.getActiveUser().getEmail()でログインユーザーのメールアドレスを取得。そのアドレスを引数として、もう1つの関数getUserScoresByEmailを呼び出す。
  • 2. getUserScoresByEmail
    「点数」シートから全データを取得し、引数のメールアドレスに一致するデータのみ抽出する。

HTML側からは1.のみを呼び出す想定で、2.は直接は呼び出されない想定です。

google.script.runを使って、HTML側から実行されない想定の 2.getUserScoresByEmail関数を実行してみました。引数(メールアドレス)も、他のユーザーのものを指定してみました。

taroさんがログインしているのに、hanakoさんのデータを取得することができてしまった。

コンソールから関数を実行することができてしまいました。
今回はコンソールで実行しましたが、デベロッパーツールでHTML(のJS部分)を書き換えて実行することも可能です。

このように、アンダースコア( _ )が末尾にない関数は、好きなように引数を変えて実行できてしまいます。

関数名の末尾にアンダースコア( _ )をつける

このような不正の対策として、関数名の末尾にアンダースコアをつけて、HTML側から実行できなくしてみます。

getUserScoresByEmail()getUserScoresByEmail_()に変更しました。

再度、関数名の取得と、関数の実行を試してみました。

関数の一覧にgetUserScoresByEmail_は含まれていなかった。また、google.script.runで実行することもできなかった。

関数の不正な実行はできなくなりましたね。

まとめ

ということで、HtmlServiceでウェブアプリを作る時、関数名や引数に注意しましょう。

  • 基本的には関数名の末尾にアンダースコア( _ )をつける
  • ウェブアプリから呼び出す必要がある関数のみアンダースコア( _ )なしにする
  • ウェブアプリから呼び出す関数の引数は、どんな値が来ても問題ないようにする

コメント