IPジオロケーションの使用例
先日、以下の記事が話題になっていました。
https://nekonko2.com/nurohikari-demerit/
「nuroが利用者に海外のIPアドレスを割り当てるために、接続元国を制限するオンラインサービスが利用できない」といった趣旨の、事実誤認を含む投稿です。
一般の利用者からしてみれば、接続元国の判定がどう行われているかなど知ったことではないので、このような認識となってしまうのも致し方ないのかもしれません。
そして、その翌日には技術的側面に触れた以下の記事も話題になっていました。
IPアドレスから地域特定するGeoIP系技術について調べてみた(追記あり)
上記投稿と重複する内容もありますが、IPアドレスから国を調べる手法(本投稿ではIP Geo Location;IPジオロケーションと記載)について、実際の使用例を交えつつ紹介します。
はじめに
私がIP Geo Locationを必要としたのは、「どの国から私のネットワークに対して不審なアクセスを試行されているのか」を知りたかったからです。これは業務でもなんでもなく、単に個人的な興味本位です。
以下の投稿内に掲載しているようなグラフを日次で自動生成するにあたり、不審なアクセスを試行してきたIPアドレスはルーターのログから容易に特定できますが、そのIPアドレスがどこの国のものかは判りません。
wave.hatenablog.com
IPアドレスから国を特定する公的な方法?
そこで、IPアドレスから国を特定する方法を調べてみたものの、ICANN, IANAをはじめ公式なデータソースからIPアドレスと国の紐づけが判る網羅的な情報は公開されていないようでした。
なお、IANAではRIR(Regional Internet registry)毎のIPアドレスの割り当ては公開されていますが、この情報では大きな地域(例えば、日本なのか中国なのか識別できない、アジア太平洋地域といった粒度の地域)までしか特定できません。
IANA IPv4 Address Space Registry
whoisが使えないか?
多くの場合、個々のIPアドレスに対してwhoisリクエストを投げれば、そのレジストラの登録情報から国を特定することはできます。
が、以下の理由からwhoisの使用は見送りました。
- 毎日数千件もの不審な接続があり、その全てにwhoisリクエストを投げるのは現実的ではない(そんなことしていいのか的な意味で)
- 各レジストラによりwhoisのレスポンスのフォーマットが異なり、単純にレスポンスをgrepするだけでは国情報だけを抽出するのは無理そう(パースが面倒)
IP Geo Locationサービスを探す
こういった事情から、(単に個人で興味本位で使うだけなので)無料で利用可能なIP Geo Locationサービスを探してみるといくつかありました。
その中でも、DB-IP.comがCreative Commons (CC-BY4.0)で公開しているDB-IP Liteデータベースを利用することにしました。
Free IP Geolocation Database Downloads | DB-IP Lite
主な選定理由は以下の2つです。
- 扱いやすいCSVフォーマットでも公開されている
- メンテナンスされている(毎月更新)
なお、Lite版は有償公開されているデータベースのサブセットで、カバレッジが狭いとか、精度を落としていることが明示されています。有償版は国だけではなく都市やISPまで特定できたりするようですので、それらの情報が必要な場合は有償版を使いましょう。
DB-IP Liteデータベースを使う
上記サイトから”IP to Country Lite CSV format”データベースをダウンロードします。gz形式で圧縮されているので、gunzipで展開するとcsvファイルが得られます。
csvファイルフォーマット
このcsvファイルを直接覗いてみると、以下のように開始IPアドレス,終了IPアドレス,国コード(ISO-3166-alpha2)の3フィールドで構成されていることが解ります。また、IPv4だけではなく、IPv6の情報も含まれています。
$ head dbip-country-lite-2019-09.csv 0.0.0.0,0.255.255.255,ZZ 1.0.0.0,1.0.0.255,AU 1.0.1.0,1.0.3.255,CN 1.0.4.0,1.0.7.255,AU 1.0.8.0,1.0.15.255,CN 1.0.16.0,1.0.31.255,JP 1.0.32.0,1.0.63.255,CN 1.0.64.0,1.0.127.255,JP 1.0.128.0,1.0.255.255,TH 1.1.0.0,1.1.0.255,CN $ tail dbip-country-lite-2019-09.csv 2c0f:ffd9::,2c0f:ffe7:ffff:ffff:ffff:ffff:ffff:ffff,MU 2c0f:ffe8::,2c0f:ffe8:ffff:ffff:ffff:ffff:ffff:ffff,NG 2c0f:ffe9::,2c0f:ffef:ffff:ffff:ffff:ffff:ffff:ffff,MU 2c0f:fff0::,2c0f:fff0:ffff:ffff:ffff:ffff:ffff:ffff,NG 2c0f:fff1::,2c0f:ffff:ffff:ffff:ffff:ffff:ffff:ffff,MU 2c10::,fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,CH fc00::,fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,ZZ fe00::,fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff,CH fe80::,febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff,ZZ fec0::,ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,CH
当然と言えば当然なのですが、ファイル中では開始IPアドレス、終了IPアドレスと国が紐づけられており、特定のIPアドレスと国が直接紐づけられているわけではありません。IPv4だけでも256×256×256×256=4,294,967,295通りのIPアドレスが存在するわけで、個々のIPアドレスと国を紐づけたデータフォーマットで保持したら約43億レコードの超巨大DBになってしまいますからね…。
というわけで、とあるIPアドレスの国を調べようと思って、このCSVファイルをIPアドレスでgrepしても、それが偶然に開始/終了IPアドレスでない限りはヒットしません。すなわち、(開始IPアドレス<=調べたいIPアドレス && 終了IPアドレス>=調べたいIPアドレス)という複合条件で検索する必要があります。
これを実現する最もシンプルな方法は、何らかのRDBにCSVファイルをインポートした後、SQLでクエリを投げるような使用形態を採ることでしょう。
IPv6を除外する
私の環境ではIPv6を利用していませんので、不審な接続を試行してくる接続元もIPv4に限定されます。このため、CSVファイル中に含まれるIPv6のエントリは不要なので、以下のようにして事前に捨てます。
grep "\." ${INPUTCSV} | sed -e 's/"//g' > ${IPV4CSV}
DB設計
やりたいことは単純ですし、複数ユーザーで使うわけでもないのでサーバーサービスもいらないし、データファイルも単一ファイルで簡単に使えるRDBとして、ここではsqlite3を採用することにします。
開始IPアドレス, 終了IPアドレス, 国コードの3カラムを持った以下のようなテーブルを作成します。
CREATE TABLE IPV4_COUNTRY( ADDRESS_RANGE_FROM INTEGER NOT NULL, ADDRESS_RANGE_TO INTEGER NOT NULL, COUNTRY TEXT NOT NULL, PRIMARY KEY ( ADDRESS_RANGE_FROM ) );
このDDLを見ると2つの疑問が湧くと思います。
IPv4アドレスの整数表記変換
普段IPv4アドレスは、aaa.bbb.ccc.dddのように8bit毎に4つに区切った形式で表現します。が、これは普通の整数として表現することも可能です。192.168.0.1を例にすると、
として、普通の整数表現で示すことができます(もしあなたのルーターのIPアドレスが192.168.0.1であるならば、http://192.168.0.1ではなく、http://3232235521でも管理画面にアクセスできるはずです。)。
シェルスクリプト内でこの換算を行うなら、以下のようなシェル関数を定義すればこの変換を行えます*1。
#!/bin/bash ip2dec () { local a b c d ip=$@ IFS=. read -r a b c d <<< "$ip" # printf '%d\n' "$((a * 256 ** 3 + b * 256 ** 2 + c * 256 + d))" printf '%d\n' "$((a * 16777216 + b * 65536 + c * 256 + d))" }
ところで、元々8bit(=1byte)の数値4つで出来ていますから、変換後の整数も4byteに収まります。C言語で取り扱う場合、多くの処理系でintが4byteだと思いますが、当然ながら符号なし整数(unsigned int)として扱わないとおかしなことになります。前述のsqlite3のDDLではINTEGER型を指定していますが、内部的に符号あり整数として扱われるようです。ただし格納する値に適したバイト数(最大8バイト)で格納されることになっていますので、データ保持には何の問題もありません。
The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude of the value.
アプリとの連携・自動化
前述の通り、私は「どの国から私のネットワークに対して不審なアクセスを試行されているのか」を知るために、IP Geo Locationを必要としていました。故に、上記のような処理を経て加工したDB-IP Liteのcsvを基にインポートしたテーブルと、ルーターのログから抽出した不審なアクセス履歴を格納したテーブルを結合(join)して、そのクエリ結果をプロットするというような処理をcronから日次で実行するようにしています。
雑記
DB-IP Liteのように世の中に公開されているDBを使用すれば、たいした面倒もなく個人でも比較的簡単にIP Geo Locationが使えることが伝われば幸いです。
なお、冒頭にリンクを張った記事に関連してですが、nuroを手掛けるソニーネットワークコミュニケーションズ株式会社による国際移転IPアドレスは、直近では2019-01-16に92.202.0.0/15が追加されたことがJPNICの公開情報から判ります。
IPv4アドレス移転履歴 - JPNIC
無償のDB-IP Liteでも、少なくとも2月時点でこの国際移転は反映されており、日本のIPアドレスであると判定されます。
$ grep 92.202.0.0 dbip-country-lite-2019-02.csv 92.202.0.0,92.203.255.255,JP
また、nuroとは関係なく、本投稿記載時点で最も新しい国際移転IPアドレスとして203.191.136.0/21が2019-08-28に追加されています。これも、9月のDB-IP Liteに反映されています。
$ grep 203.191.136.0 dbip-country-lite-2019-09.csv 203.191.136.0,203.191.143.255,JP
要するに、冒頭でリンクを張った記事でnuroが責められていますが、別にnuroが悪いのではありません。強いて悪者を上げる必要があるのなら、nuroの国際移転IPアドレスを反映しない動画配信サービスまたは動画配信サービスが内部的に使用しているIP Geo Locationサービスの怠慢と言えるのではないでしょうか。無償で公開されているDB-IP Liteにも劣る精度のIP Geo Location情報を使って顧客を逃がすなんて…。
蛇足ながら、ユーザーの国によってコンテンツを見せない制限が必要であるかといった指摘も一部でありましたが、各動画配信サービス事業者は契約に基づき配信可能な権利を得ている国以外に配信することはできませんし、各国の法律にも従う必要があります。A国ではα社が配信可能な権利を得ているが、B国ではβ社が独占配信可能な権利を持っているとか、A国では認められている表現が、B国では禁止されているとか。そういった事情から、国ごとにコンテンツを見せたり見せなかったりの制御は必要で、IPアドレスによる制限は完璧ではないながらも手っ取り早く実現できるために導入されているのであろうと考えられます。
以上。