Maps JavaScript API を使うとき、APIキーが心配
【ご注意】本件の記事は、「HTTP リファラー(ウェブサイト)」による制限を否定するものではありません。Google 公式が推奨しているとおり、リファラー(ウェブサイト)による制限は必須です。それに加えAPIキーを公開しない方法が無いものかと考えたのがこの記事です。
Google は、公式サイトで API キーをコードに直接埋め込まないでください
と言っている割には、[公式サンプル]のコーディングが以下の様になっていて、堂々と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 キーの制限を適用する]には「HTTP リファラー(ウェブサイト)」による制限が必須だと書いてはあるが、これだけでは非常に心許ない。
皆さんご存じの通り、ウエブページの名前なんてローカルにwebサーバー建てて hosts をいじれば簡単に詐称できるし・・・(私はそんなことやってませんよ。念のため)
もちろん、知らない間に課金されないように、[API 使用の上限設定]は実施しているが、どこかの誰かにAPIキーを勝手に使われて100万回とかアクセスされたらどうしよう?と思うと夜も眠れない・・・
APIキーは見えないところに隠そう
やっぱりAPIキーは、HTMLで公開したくない!!
ということで CGI を使って API キーをサーバーの中に隠しておくことにしました。
1.環境変数にセット
CGI にはAPIキーを環境変数にセットした状態で渡します。私の環境は nginx + fcgiwrap で CGI を動かしているので、/etc/nginx/fcgiwrap.conf の一番下に以下の様にセット。
location /cgi-bin/ {
・・・・・・
fastcgi_param GOOGLE_MAPS_API_KEY YOUR_API_KEY; <=YOUR_API_KEYを自分のキーに置き換えてください
}
ここは、皆さんの環境(apacheなど)毎に設定方法が異なるので、ご自分の環境に合わせて設定してください。
2.CGI を用意
方針としては、公式サンプルの src="https://maps.googleapis.com/maps/api/js
の部分を CGI で置き換える。
私の環境では python を使っているので、こんな感じになります。
#!/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'
にしましょう。
3.javascript の window.onload で CGI を呼ぶ
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 で行きます。
ご参考
<!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の読み込み方法]
こちらのwebsiteもよろしくお願いします。
[[初心者のためのWEBシステムのインフラ構築]]
(https://olto3-sugi3.tk/ja/index.html)