![]() 【JavaScript】IPアドレスを基に地図上の位置を表示【ソース公開】~既存の2つのサービスを連結使用してしまう方法~ |
|
【リンクフリー】 私設研究所ネオテックラボ Neo-Tech-Lab.co.uk 【記載者】 【私設研究所Neo-Tech-Lab】 上田 智章 【掲載日】2010年12月19日 |
![]() |
ここにチェックボックス型外部コンテンツ・メニューが入ります。 | |
ここにGoogle Mapが表示されます ★iPinfoDBの精度が悪いので一致しない事が多いと思います。 |
【はじめに】無料のApacheを載せたパソコン自宅サーバーだけで、投資を伴わずにどんなサービスが提供可能かトライを続けています。このページでは、JavaScriptだけでIPアドレスを調べ、地図上の位置を調べ、Google Map上に表示する方法について記述しています。HTML5を使えばもっと精確な位置が表示できるのですが、いちいちユーザーに許可を求めるようではアクセス解析などに用いることができません。何も入力操作をしなくても地図上の位置が判明するようにデータベースを用いる方法を採用しています。最初に作成したのが、jsonip.comでIPアドレスを調べてgeomaplookup.netを呼び出して表示させ、iframeやdivタグで肝心の部分だけを表示させるサービス・ハック手法(BGMプレーヤーの左側に表示)です。しかし、それではあんまりなので、次に作ったのが、iPinfoDBというデータベースでIPアドレスを地理情報に変換し、Google Map APIで表示する方法(BGMプレーヤーの左下側の地図)です。 勿論、JavaScriptしか使っていませんから、ご使用のブラウザで『ソースを表示する』ことで、ソースを閲覧することができます。ご参考まで。 ちなみに、インターネット上の無関係なサーバーのサービスを複数組み合わせて、新たなサービスを実現する方法をパラサイト型クラウド・サービスと呼んでいます。自宅サーバーの処理負荷を軽減する為にクライアントPCや外部サーバーを使っちゃうわけです。将来的には、高負荷になっているアクセス解析も外部に投げ出してしまうつもりです。 クライアントPCからの結果の回収(サルベージ)は簡単です。ウェブ・ビーコンと似た手法を使います。パラメータに乱数だけでなく、ページ名称、緯度・経度等の情報も付けて解析用ドメインに送るだけです。(ウェブ・ビーコンでは受信動作で実は情報を隠ぺい送信しています。) 詳細は後日追記します。 |
【動作原理】まず、ページ閲覧者のIPアドレスを調べる。勿論、JavaScriptだけでIPアドレスを直接調べることはできないが、 IPアドレスを調べて結果をテキスト形式(JSON形式)で返送してくれる外部ドメインのサービスを利用する。 【例1】http://jsonip.appspot.com/上記リンクをクリックすると、JSON形式で{"ip": "xxx.xxx.xxx.xxx", "address":"xxx.xxx.xxx.xxx"}と返送されてくる。詳細はこちらで(英語) 【例2】http://jsonip.com/上記リンクをクリックすると、JSON形式で{"ip":"xxx.xxx.xxx.xxx","about":"/about"}と返送されてくる。【JSONPとは】このデータをJavaScriptで受け取って使うために、上記URLに半角『?』をつけてパラメータとして、 『callback=XXXXXX』を指定します。 XXXXXXの所には文字列を受け取るJavaScriptの関数名を書きます。 文字列取得後にcallbackで指定した関数が呼び出されます。 【指定例】http://jsonip.com/?callback=hogehoge【受信結果】hogehoge({"ip":"xxx.xxx.xxx.xxx","about":"/about"})もし、JavaScript中に以下の関数hogehogeが用意されていたなら、 引数data.ipにIPアドレスが格納されていることになります。 function hogehoge(data) { } 【ブラウザのキャッシュ対策】但し、ブラウザのキャッシュ機能のせいで、指定ドメインが同じ場合には2度目以降は実際にはアクセスされずに、キャッシュされた内容が採用される欠点があります。つまり1回目のアクセス時のIPアドレスしか得られません。 デスクトップPCからのアクセスであれば場所は固定されているのですが、 モバイルや携帯電話の場合はIPアドレスも実際の位置も毎回異なるはずなのでそれでは困ります。 そこで、キャッシュ機能を無効にするために、もう一つパラメータ『dummy=vvvvvvv』を指定します。 vvvvvvvは乱数や年月日日時等を使って毎回違う値を指定します。 複数のパラメータを使う場合には半角『&』を使ってパラメータを連結します。 このパラメータ付与によりURLが毎回異なるので、キャッシュ機能が解除されます。 http://jsonip.com/?dummy=vvvvvvv&callback=hogehoge 【Geo Map Lookupサービス】http://geomaplookup.net/はIPアドレスを入力すれば、Google Map上に表示してくれるサービスです。http://geomaplookup.net/?ip=xxx.xxx.xxx.xxxとパラメータとしてIPアドレスを指定することもできます。 divタグとiframeタグのstyleにmarginとoverflowの機能があるので、 少し大きめの地図を表示して、左上方向に移動して表示し、地図の部分だけを表示するようにします。 【追記】既存サービスに寄生してサービスを作る方法を例示しました。本当は、IPアドレスを環境変数から取り出すモジュールと、 通販されているIPアドレスのデータベースで緯度・経度情報を取り出し、 Google Map上にピンを立てればいいだけなのですが、 何より自前のサーバーの負荷を殆ど食わないとことがパラサイトの旨味です。 リモート・アドレス(IPアドレス)だけでなく、リモート・ホストを使えば、 法人名がわかる場合があり、より正確な現在地情報が得られます。 最近当サイトで検出しているだけでも新たなサイバー攻撃手法が組織的に実験されているようです。 アクセス元の国、使用言語やアクセス履歴などから、ハッカー検出を自動的に行い、 世界中の多数のサーバーを使って反撃するシステムの構築・改良を行っています。 使うと言ってもハックするのではありません。 攻撃者側にアクセスさせるだけで、大量のデータを相手側PCに流し込むことができるからです。 このような場合に、ゾンビ化したPCの取扱いをどうするかが鍵になってきます。 相手の地理情報の取得は不可欠となります。 |
【ソース】function NTL_SendCmd() { // var URL = "http://jsonip.appspot.com/"; //【App Engine URL】★ここをアクセスすると{"ip": "xxx.xxx.xxx.xxx", "address":"xxx.xxx.xxx.xxx"}と返送される var URL = "http://jsonip.com/"; //【App Engine URL】★ここをアクセスすると{"ip":"xxx.xxx.xxx.xxx","about":"/about"}と返送される var number = new Date().getTime(); //【イメージ・リフレッシュ用】 var IPTag = document.createElement("script"); IPTag.charset = "utf-8"; IPTag.type = "text/javascript"; IPTag.src = URL + "?dummy=" + number + "&callback=" + "NTL_GetData"; document.body.appendChild(IPTag); } function NTL_GetData(data) { //【取得したIPアドレスを表示する】 document.getElementById("UserInfo").innerHTML = "<font face='Times New Roman' color='red' size='3'>" + "【JavaScriptパラサイト型クラウドの実験】あなたのIPアドレスは" + data.ip + "です。" + "あなたの概略位置が左下の世界地図上に表示されます。</font><br>"; //【IPアドレスの返送があったので、初期表示のイメージを消去】 document.getElementById("GeoMap").innerHTML = ""; //【IPアドレスに基づいて世界地図上の位置を求め表示する】 //【divのstyleのmargin, overflowとともに設定して地図の表示位置を調整】 var MapURL = "http://geomaplookup.net/?ip=" + data.ip; var base = document.getElementById("GeoMap"); var obj=document.createElement("iframe"); obj.setAttribute("border", "0"); obj.setAttribute("frameborder", "0"); obj.setAttribute("border", "0"); obj.width = "655px"; obj.height = "700px"; obj.style.margin = "-250px 0px 0px -200px"; obj.style.overflow = "hidden"; obj.src = MapURL; base.appendChild(obj); } 【HTML】<div id="GeoMap" name="GeoMap" style="position:relative; top:0px; left:0px; z-index:0; width:435px; height:400px; line-height:110%; margin:0px; overflow:hidden;"> <a title="【Geo Map Lookup】" href="http://geomaplookup.net" target="_blank"><img border="0" src="../script/b/GeoMapLookup.png" width="435px"></a><br> このページは表示までに時間がかかります。<br>少々お待ちください。<br> </div> |
【iPinfoDBサービス】IPアドレスから位置情報がJSONPで得られる無料のサービス。JSON形式で、IPアドレスから緯度・経度、国、州、都市名が得られるIP Location API 但し、利用に際しては、サービス改善の為にアカウント登録を行って、API-KEYも発行してもらう必要がある。 アカウント登録は簡単で即時API-KEYを発行してもらえる。 【使い方】都市精度版とより高速な国精度版があるが、都市精度版でも結構速い。勿論、使用にあたっては、ブラウザのキャッシュを無効にするため、パラメータにdummy=xxxxxxxxを追加して 乱数性を持たせる必要があることは言うまでもない。 Google Mapと組み合わせれば、JavaScriptだけでGeo Map Locationと同じようなサービスが構築できる。 1) http://api.ipinfodb.com/v3/ip-city/?key=<your_api_key>&ip=202.215.26.52&format=json 【結果】 { "statusCode" : "OK", "statusMessage" : "", "ipAddress" : "202.215.26.52", "countryCode" : "JP", "countryName" : "JAPAN", "regionName" : "KYOTO", "cityName" : "KYOTO", "zipCode" : "-", "latitude" : "35.0211", "longitude" : "135.754", "timeZone" : "+09:00" } 2) http://api.ipinfodb.com/v3/ip-city/?key=<your_api_key>&ip=202.215.26.52&format=json&callback=Test 【結果】 Test( { "statusCode" : "OK", "statusMessage" : "", "ipAddress" : "202.215.26.52", "countryCode" : "JP", "countryName" : "JAPAN", "regionName" : "KYOTO", "cityName" : "KYOTO", "zipCode" : "-", "latitude" : "35.0211", "longitude" : "135.754", "timeZone" : "+09:00" } ) |
【Google Map API】でも、GeoMapLookupに強く依存するのもあれなので、iPinfoDBとGoogle Map APIを組み合わせて、等価なサービスを構築してみました。処理の流れは次の通りです。 1) IPアドレス取得 2) iPinfoDBを用いて、ロケーション情報取得 3) Google Map上に表示 似たようなものは作ることができました。iPinfoDBの精度があまり良くないようなので、独自に機能追加する必要がありそうです。 【ソース】var YourIPAddress = ""; function SendGetIPAddressCmd() { //【確実にIPアドレスを得るために2か所のサービスを呼ぶ】 SendGetCmd("http://jsonip.com/", "GetIPAddressData"); //【JSON形式】{"ip":"xxx.xxx.xxx.xxx","about":"/about"} SendGetCmd("http://jsonip.appspot.com/", "GetIPAddressData"); //【JSON形式】{"ip": "xxx.xxx.xxx.xxx", "address":"xxx.xxx.xxx.xxx"} function SendGetCmd(url, cb) { var number = new Date().getTime(); //【イメージ・リフレッシュ用】 var IPTag = document.createElement("script"); IPTag.charset = "utf-8"; IPTag.type = "text/javascript"; IPTag.src = url + "?dummy=" + number + "&callback=" + cb; document.body.appendChild(IPTag); } } function GetIPAddressData(data) { if(YourIPAddress == "") { //【上記2つのIPアドレスのうち先着の方のIPアドレスで国や地理座標を参照】 YourIPAddress = data.ip; //【先着のipアドレスを保存】 document.getElementById("UserInfo").innerHTML = "<font face='Times New Roman' color='red' size='3'>" + "【JavaScriptパラサイト型クラウドの実験】あなたのIPアドレスは" + data.ip + "です。" + "あなたの概略位置が左下の世界地図上に表示されます。</font><br>"; SendIPinfoDB(data.ip); //【iPinfoDB】⇒【Google Map】 CreateGeoMapLocation(data.ip); //【Geo Map Location】 } } function CreateGeoMapLocation(ip) { var MapURL = "http://geomaplookup.net/?ip=" + ip; //【IPアドレスに基づいて世界地図上の位置を求め表示する】 //【divのstyleのmargin, overflowとともに設定して地図の表示位置を調整】 var base = document.getElementById("GeoMap"); base.innerHTML = ""; var obj=document.createElement("iframe"); obj.setAttribute("border", "0"); obj.setAttribute("frameborder", "0"); obj.setAttribute("border", "0"); obj.width = "655px"; // "437px"; obj.height = "700px"; // "400px"; obj.style.margin = "-250px 0px 0px -200px"; obj.style.overflow = "hidden"; obj.src = MapURL; base.appendChild(obj); } function SendIPinfoDB(ip) { var APIkey = "0fba88ae781cde9accb23781c6f49259afcc618ff62017f395ab97c8faa75fc1"; //【iPinfoDBのAPI Key】neo-tech-lab.co.uk var number = new Date().getTime(); //【イメージ・リフレッシュ用】 var URL = "http://api.ipinfodb.com/v3/ip-city/?key=" + APIkey + "&ip=" + ip + "&format=json"; var IPTag = document.createElement("script"); IPTag.charset = "utf-8"; IPTag.type = "text/javascript"; IPTag.src = URL + "&callback=" + "loadGoogleMap" + "&dummy=" + number; document.body.appendChild(IPTag); } // Google Map Viewer JavaScript function loadGoogleMap(location) { var msg = "<font size='2'>IP Address :<b>" + location.ipAddress + "</b><br>" + "Latitude : " + location.latitude + ", " + "Longitude : " + location.longitude + "<br>" + "City : " + location.cityName + "<br>" + "Region : " + location.regionName + "<br>" + "Country : " + location.countryName +"</font>"; var myLatlng = new google.maps.LatLng(location.latitude, location.longitude); var mapOptions = { zoom: 6, center: myLatlng, mapTypeId: google.maps.MapTypeId.HYBRID } var map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions); var marker = new google.maps.Marker({ position: myLatlng, map: map, title: 'MyAddress' }); var infowindow = new google.maps.InfoWindow({ map: map, position: myLatlng, content: msg }); map.setCenter(myLatlng); } |
【NsLookup】ホスト名を調べるのには、【Creazy】『Nslookup API を作りました』のyagerさんのNsLookup APIが便利です。【使い方】http://pipes.yahoo.com/yager/nslookup?_render=json&_callback=callback&search=202.215.26.52これもURLの後ろに乱数を付与してブラウザのキャッシュを無効にする必要があります。 【ソース】//【外部サービスNsLookupを用いてIPアドレスからホスト名を取得する】 function NTL_SendNsLookup(ip) { //【例】http://pipes.yahoo.com/yager/nslookup?_render=json&_callback=callback&search=202.215.26.52 var number = new Date().getTime(); //【ブラウザのキャッシュ機能を無効化してリフレッシュ】 var URL = "http://pipes.yahoo.com/yager/nslookup?_render=json"; var IPTag = document.createElement("script"); IPTag.charset = "utf-8"; IPTag.type = "text/javascript"; IPTag.src = URL + "&_callback=" + "NTL_GetHostName" + "&search=" + ip + "&dummy=" + number; document.body.appendChild(IPTag); } //【逆引きしたHostNameを受信・保存】 function NTL_GetHostName(data) { NTL_YourHostName = data.value.items[0].hostname; } |
【ちなみにHTML5を使うとどうなるか?】いちいちブラウザがアクセス許可を要求してくるので使いづらいのですが、HTML5を実装しているブラウザであれば、サンプルのように位置が特定されます。 |
【まとめ】当サイトでは、『警告:ネットは匿名ではない。パッシブなアクセス解析だけで一体何がわかるのか?』(Interest2.htm)で書いているような処理をサーバー側で実行しています。しかし、この負荷はかなり重く、サーバーの負担となってきました。そこで、クライアント側PCと外部 サーバーに負荷分散を行おうとしています。問題点はIPアドレスから取得する地理座標の精度です。これがデータベースによって大きく 異なるのです。現状のパラサイト型クラウドではまだまだ最適解とは言えませんので、さらに改良を加えていきたいと思います。 【考察】JSONPの仕組みはクロスドメインでのデータ受信が可能だ。また、データ要求時にURLに付与しているパラメータに情報を付ければクロスドメインへの情報送信も可能だ。 例えば、 <img src="http://www.neo-tech-lab.co.uk/b/p.gif?msg=<送信情報>&num=<乱数>"> のようにクロスドメインに存在するimage取得の受信要求であってもパラメータに情報を付与することで、クロスドメイン 送信を行う事が簡単に行える。つまり、クロスドメインに自由に通信が行えるのでセキュリティーホールとなっている。 【例】【サンプル】 上記サンプルでは、JSONPの仕組みを使って別ドメインに存在する3Dモデルとそのテクスチャーデータをクロスドメインで 受け取っている。別ドメインのサーバーは何の変哲もない普通のレンタルサーバーで、JSONPに対応していない。 しかし、callback("{ データ }")のように予め受信用関数名で囲い込みを施した状態のテキストファイルを用意 (http://www.neo-tech-lab.com/Web3D/Web3D_Chibimiku.bmp.js)しておくだけで、このサンプルのようなことが可能になる。 鍵となるのはbase64形式だ。テキストであってもバイナリの情報を容易に送ることができるのだ。 『遠隔操作』事件でトロイの木馬型ウィルスでゾンビ化したパソコンが被害にあったらしい話がニュースで伝わってきたが、 実際にはそれは下等な方法だと言える。事実はページ閲覧だけで遠隔操作されてしまう危険性がある。 外部にメールサーバーに偽装IPで押し込むサーバーがあればページ閲覧で取得したIPアドレスでメール送信も理論的には可能 と考えられる。あるいはJSONPに悪意のコードが混入すれば後出しジャンケンでページ表示を変更することができる。 こういうテクニックが実際のサイバー攻撃では既に使用されている可能性がある。 SUNのJAVAが実装されているスマホやパソコンは非常に多い。JavaScriptと違ってJavaは遠隔操作される危険度が非常に高い。 サイバー攻撃で利用するために作成・準備された言語だと考えられる。パソコンならJAVAを使うサイトは殆ど存在しないので 早急にアンインストールされることをお薦めする。これだけで遠隔操作されてしまう可能性を8割は減らすことができるはずだ。 但し、ここで述べた方法は、サイバー攻撃者に対して反撃を行う場合にも有効である。 しかも、パラサイト型クラウドは、防衛側の通信回線容量を飛躍的に増大させることができる。 応戦するサーバーも世界中に分散させておくことで、相手側の通信回線をパンクさせることは容易だろうと考える。 |