Google Maps API キーをHTMLから隠す方法

# Maps JavaScript API を使うとき、APIキーが心配

**【ご注意】本件の記事は、「HTTP リファラー（ウェブサイト）」による制限を否定するものではありません。Google 公式が推奨しているとおり、リファラー（ウェブサイト）による制限は必須です。それに加えAPIキーを公開しない方法が無いものかと考えたのがこの記事です。**

Google は、公式サイトで `API キーをコードに直接埋め込まないでください`と言っている割には、[[公式サンプル]](https://developers.google.com/maps/documentation/javascript/adding-a-google-map)のコーディングが以下の様になっていて、堂々とHTMLの中に**YOUR_API_KEY**が登場している。
これでwebページ作ったら世界中にAPI キーを公開してしまうことになる。まるで、堂々とフリチンで公道を歩いている様な状態。公式サンプルは裸の王様か？

```
<script defer
  src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>
```
一応 Google のドキュメント[[API キーの制限を適用する]](https://cloud.google.com/docs/authentication/api-keys?hl=ja&visit_id=637360038592927763-3730975888&rd=1#api_key_restrictions)には「HTTP リファラー（ウェブサイト）」による制限が必須だと書いてはあるが、これだけでは非常に心許ない。
皆さんご存じの通り、ウエブページの名前なんてローカルにwebサーバー建てて hosts をいじれば簡単に詐称できるし・・・（私はそんなことやってませんよ。念のため）
もちろん、知らない間に課金されないように、[[API 使用の上限設定]](https://cloud.google.com/apis/docs/capping-api-usage?authuser=1&authuser=1&hl=ja&visit_id=637360063115512101-2429778502&rd=1)は実施しているが、どこかの誰かにAPIキーを勝手に使われて100万回とかアクセスされたらどうしよう？と思うと夜も眠れない・・・
## APIキーは見えないところに隠そう
やっぱりAPIキーは、HTMLで公開したくない！！
ということで CGI を使って API キーをサーバーの中に隠しておくことにしました。
### １．環境変数にセット
CGI にはAPIキーを環境変数にセットした状態で渡します。私の環境は nginx + fcgiwrap で CGI を動かしているので、/etc/nginx/fcgiwrap.conf の一番下に以下の様にセット。

```fcgiwrap.conf
location /cgi-bin/ {
    ・・・・・・
    fastcgi_param GOOGLE_MAPS_API_KEY　 YOUR_API_KEY;　＜＝YOUR_API_KEYを自分のキーに置き換えてください
}
```
ここは、皆さんの環境（apacheなど）毎に設定方法が異なるので、ご自分の環境に合わせて設定してください。
### ２．CGI を用意
方針としては、公式サンプルの `src="https://maps.googleapis.com/maps/api/js`　の部分を CGI で置き換える。
私の環境では python を使っているので、こんな感じになります。

```getapijs.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import requests
import os
url = 'https://maps.googleapis.com/maps/api/js' # 公式サンプルの HTML が src= で読んでたurl
key = os.environ['GOOGLE_MAPS_API_KEY'] # 環境変数に入っている APIキーを取り出す
mysrc = url + "?key=" + key # url に APIキーを連結する
response = requests.get(mysrc) # google マップのサイトにアクセスして src を持ってくる
print("'Content-Type': 'text/javascript; charset=UTF-8'") # HTML に返してあげるためのヘッダー
print("")
print(response.text)
```
この部分も皆さんの環境毎に異なる部分なので、ご自分の使える言語で記述してください。
要は、**サーバーの環境変数からAPIキーを持ってきて**、url 指定して google のサイトから javascript を頂戴するということです。ヘッダーの部分は、`'Content-Type': 'text/javascript; charset=UTF-8'` にしましょう。
### ３．javascript の window.onload で CGI を呼ぶ
```main.js
function initMap() {
    // 公式サンプルの内容と同じ
    // Initialize and add the map
    // The location of Uluru
    var uluru = {lat: -25.344, lng: 131.036};
    // The map, centered at Uluru
    var map = new google.maps.Map(document.getElementById('map'), {zoom: 4, center: uluru});
    // The marker, positioned at Uluru
    var marker = new google.maps.Marker({position: uluru, map: map});
}
window.onload = function() {
    // ページを表示した後に、実行したい処理を書く。ここからが今回のポイント！
    fetch("/cgi-bin/getapijs.py").then(res=>{
        // CGI 実行して、結果の TEXT だけを次にパスする
        return res.text();
    }).then(mytext => {
        // 受け取った javascript を EVAL で実行する。
        eval(mytext);
    }).then(() => {
        // 実行後の処理。公式サンプル HTML が &callback= でコールしていた部分
        initMap();
    }).catch(() =>{
        // お好きなエラー処理をどうぞ
    });
}
```
HTML には、ヘッダー部分に `<script src="main.js"></script>` を記載してます。この辺の流儀は人によって違うところなので、お好きなスタイルで良いと思います。
要は、**HTML の要素を読み込んだ後**（サンプルHTMLの defer の部分。私の例では window.onload）に、**CGIを実行**して javascript をTEXTで受け取り**EVAL で実行する**、という手順です。
##これで安心して眠れるか？
APIキー は環境変数の中にしかないので、ソースをGITHUBで公開しても問題ないし、WEBサイトのユーザーが Chrome のデベロッパーツールで HTML や JS を見ても、APIキーは見つからない。
公式サンプルが堂々とフリチンで公道を歩いているのに比べれば、奥ゆかしい出来上がりになっています。
唯一、気持ち悪いところがあるとすると「邪悪な EVAL」を使っていることです。邪悪なwebページだと思われてしまうかもしれない・・・
EVAL が禁じ手として封じられている方は今回の手法は使えませんが、APIキーを公開の場に晒すのはEVAL使うよりも危険だと思います。止めましょう。会社でEVAL禁止されている人は諦めて JAVA で重いアプリ作ってください。私は軽くHTML + javascript + python で行きます。
#### ご参考
```index.html
<!DOCTYPE html>
<html>
  <head>
    <style>
      /* Set the size of the div element that contains the map */
      #map {
        height: 400px;  /* The height is 400 pixels */
        width: 100%;  /* The width is the width of the web page */
      }
    </style>
    <title>Hello World</title>
    <script src="main.js"></script>
  </head>
  <body>
    <h3>My Google Maps Demo</h3>
    <!--The div element for the map -->
    <div id="map"></div>
  </body>
</html>
```
なお、こちらの記事を参考にさせて頂いています。
[[私がよく使うJSからの外部JSの読み込み方法]](https://qiita.com/a01sa01to/items/fc6bf8bb5b1cb0db01cf)

こちらのwebsiteもよろしくお願いします。
[[初心者のためのWEBシステムのインフラ構築]]
(https://olto3-sugi3.tk/ja/index.html)