JavascriptでIPアドレス制御をやる方法

2023年12月20日

Javascript

eyecatch とあるホームページ製作の案件をご依頼いただきました。 普通にホームページを作るという案件ではなく、いまあるシステムが古くなってきたので新しくしたいというご要望でした。 そして、できる限り以前の仕様を引き継ぎたいという事で、その会社さんには、もはやシステム内部が分かる人がいないとのことで、 内部調査から行うことになりました。 そして、システムの内部をおもむろに把握して、入り口にあるIPアドレス制御という機能があることに気が付きました。 そして、このIPアドレス制御をJavascriptだけで行う方法を模索した結果を本日のブログで紹介(備忘録)しておきたいと思います。

IPアドレス制御とは?

これは、その会社さんが、固定IPサービスというのを利用していて、その会社の中から外部のwebサイトを見ると、一定のIPアドレスであればアクセスができるという仕様です。 これによって、別のIPアドレスからアクセスしたら、エラーページや別のページに遷移すれば、情報セキュリティ対策になります。 多くの場合、ログイン機能などを設けているシステムがほとんどですが、この会社酸の場合は、全員のデータ置き場などのようにシステム(ホームページ)を使っているため、ある階層に対してこnIPアドレス制御処理を行っているという事でした。

ログインセキュリティとIPアドレス制御のセキュリティ強度の違い

ログインシステムは、多くの場合IDとパスワード(メールアドレスとパスワード)で行う場合が多く、システムでそのメールアドレスでログインした後、セッション情報などをロギングすることで、誰が何をしたという追跡が可能になります。 一方IPアドレス制御は、対象のIPアドレスであれば、サイトアクセスが可能になるという事で、特定サークルやコミュニティのオフライン場所などからのアクセスであれば、無条件でwebページを表示するというセキュリティになります。 この2つのセキュリティレベルの違いは段違いで、IPアドレス制御はでも、クッキーなどのセッションを使ってロギングすることは可能ですが、本人特定などと結びつけたい場合は、ログインシステムにした方がいいでしょう。 でも、今回は簡単なIPアドレス制御という事になったので、構築は楽な感じです。

まてよ、IPってJavascriptでは取れないんじゃないか?

そのとおりです、IPアドレスでは、アクセス元のIPアドレスって取得できません。 これはNodejsのようなサーバーサイドのアクセス情報から拾う事ができるモノで、webクライアントブラウザでのみ動いているJavascriptでは、取得できません。 これを可能にするために、便利なAPIサービスというものがあり、 ipinfo.io このサービスにJavascriptでアクセスすると、サーバーで取得可能な情報をたくさん得ることができます。 ちなみに、こんな情報がとれますよ。 ip: "***.***.***.***", hostname: "st2318.nas811.hoge-hoge.nttpc.ne.jp", city: "Yokohama", region: "Kanagawa", country: "JP", loc: "**.4333,***.6500", org: "AS2514 NTT PC Communications, Inc.", postal: "***-****", timezone: "Asia/Tokyo", asn: Object, asn: "AS2514", name: "NTT PC Communications, Inc.", domain: "nttpc.co.jp", route: "***.***.***.***/17", type:"isp", company: Object, name: "InfoSphere (NTTPC Communications, Inc.)", domain: "nttpc.ne.jp", type: "isp", privacy: Object, vpn: true, proxy: false, tor: false, relay: false, hosting: false, service: "", abuse: Object, address: "Uchikanda OS Bldg 4F, 2-12-6 Uchi-Kanda, Chiyoda-ku, Tokyo 101-0047, Japan", country: "JP", email: "hostmaster@nic.ad.jp", name: "Japan Network Information Center", network: "***.***.0.0-***.***.255.255", phone: "+81-3-5297-2311", これらをJavascriptでは、JSONデータで取得することが可能になります。 サーバーから、curlするもよし、 curl ipinfo.io Javascriptから、ajaxでアクセスするもよし。 const xhr = new XMLHttpRequest() xhr.open('get' , "https://ipinfo.io/json" , true) xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onload = (e => {console.log(e.target.response)}) xhr.send()

APIも便利だが、できるだけ自サーバーで固めよう

APIのデメリットは、よほどしっかりした契約を結んでいないとサーバードラブルなどの責任は追ってくれないという事です。 なので、今回は、公開するサーバーにPHPのAPIモジュールを設置し、JavascriptでそこにアクセスしてIPアドレスのみを抽出するようにするように設計しました。 そしてできたPHPが次のモノです。 <?php $res = [ "ip" => $_SERVER['REMOTE_ADDR'], "port" => $_SERVER['SERVER_PORT'], "server" => $_SERVER['SERVER_NAME'], "uri" => $_SERVER['REQUEST_URI'], "user_agent" => $_SERVER['HTTP_USER_AGENT'], ]; echo json_encode($res , JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); シンプルでしょ? これをサーバーに設置すれば、同じサーバーで、上記のajaxでアクセスすれば、jsonが返ってくる仕組みができます。 どうしても、他のサーバーからも便利にアクセスして使いたい場合は、Apacheサーバーを使っているのであれば、次の分を.htaccessに記述するだけで、"access-control-allow-origin"のエラーを回避することができます。 <IfModule mod_headers.c> Header set Access-Control-Allow-Origin * </IfModule>

Javascriptでの設定

次にJavascriptでは、アクセスを許可するJsonデータを作成しておきます。 同じコードの中に直接オブジェクトデータとして記述しておいても良いのですが、できるだけ別管理にした方がいろんな方面で良いでしょう。

ip.json

123.11 123.22 111.222.333.444 改行区切りで複数のIPアドレスを登録しておきます。 IPアドレスの特性を利用して、前方一致にできるのがポイントですね。 フルの固定IPであれば、一番下の行のように、書けばいいだけです。

check_ip.js

class CheckIp{ constructor(){ this.load_ip() } // 許可IPの読み込み load_ip(){ const xhr = new XMLHttpRequest() xhr.open('get' , "ip.json" , true) xhr.setRequestHeader('Content-Type', 'text/json'); xhr.onload = this.loaded_ip.bind(this) xhr.send() } loaded_ip(e){ if(!e || !e.target || !e.target.response){return} this.ip = e.target.response.split("\n") this.check() } // 許可IPにマッチするか確認 check(){ const xhr = new XMLHttpRequest() xhr.open('get' , "http://www.ttc.t.u-tokyo.ac.jp/api/api.php" , true) xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onload = this.checked.bind(this) xhr.send() } checked(e){ if(!e || !e.target || !e.target.response){return} const user_info = JSON.parse(e.target.response) let flg = false for(const ip of this.ip){ if(!ip){continue} const reg = RegExp(`^${ip}`) if(user_info.ip.match(reg)){ flg = true break } } if(flg){ this.init() } else{ this.error() } } // 許可されている場合、正常表示 init(){ console.log('正常表示') } // 許可されていない場合、topに戻る error(){ console.log("エラー表示") } } switch(document.readyState){ case 'complete': case 'interactive': new CheckIp() break default: window.addEventListener('DOMContentLoaded' , (()=>new CheckIp())) } jsonファイルの階層などは任意で変更しておけば、このJavascriptをページ内で実行するだけで、許可されているかどうかの判定を行うことが可能になります。

あとがき

Javascriptだけじゃないじゃん!と気がついた人、APIはおまけで付けておいただけで、開発環境からも外しておけるので、基本的にはJavascriptだけで判定できる仕様にしています。 もちろん、こうしたセキュリティを実行する場合、ある程度のセキュリティお作法を身に着けておく必要があります。 でも、簡易なIP判定レベルのこうした構築ができるというのも悪くないスキルだと思いますよ。 今回のコードを少し改造すると、もっと便利で上位のセキュリティ対策もできると思うので、Javascriptだけでもかなりの設計ができることがわかります。 でもまあ、金銭の絡むサイトには、Javascriptだけじゃなく、サーバーサイドでしっかりしたセキュリティ防御をすることをオススメしますけどね。

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。