アラカン"BOKU"のITな日常

文系システムエンジニアの”BOKU”が勉強したこと、経験したこと、日々思うことを書いてます。

PHP+jQueryを使い、既存の静的HTMLに、アプリ側だけでアクセス制限をかける簡単なアイディア

大量(200位)の静的HTMLで作成されたコンテンツがありました。

 

元々、オープン公開するつもりだったものです。


ところが、会社の方針が変わって、ログインしたユーザ以外は参照できないようにアクセス制限をかけなければならなくなった。

 

なんとかしてくれ。


そんな相談がありました。

f:id:arakan_no_boku:20180427232928j:plain

 

種々の事情でアプリ側だけで対応することになりました

 

最初は、WEBサーバーの機能でやれば簡単じゃないの・・と答えてました。


だって、apatcheかnginxかしらないけど、Webサーバーでそのディレクトリにアクセス制限かけて、Basic認証かければいい話だから。

 

サーバーの管理者に頼めば、すぐできるような話じゃないの・・と。


ところが、相手はうかない顔。


理由は2つ。

  • フォーム認証の仕組みで運用している既存システム配下に置きたい。だから、単純にそこだけBasic認証にするだけでは駄目らしい。
  • サーバーの管理は外部の会社に委託しているが、このような作業は契約の範囲外なので有償対応になってしまうと言われた


まあ、ざっくり言えば、上司から金は使うなと言われ、タダでやってくれという交渉を外部の会社としても無理そうだから、頼む・・ってことです。

 

ということで、アプリケーション側だけでやる方法を考えることになりました。


例によって、以下の条件付き。

  • 時間も人出もかけられない。(ようするに一人でやって)
  • 既存の仕組みへの影響はないようにしてほしい。

 

結構、考えました。


やってしまえば、簡単な対応でしたけど、落とし穴もあり、ネタとして面白いので、備忘をかねて書いておきます。

 

既存のシステムにあわせてPHPで対応します

 

既存のPHPで組んだシステムがありました。


だから、ログイン画面やログインチェックの仕組みはそれを流用します。


DBにテーブル作って、HTMLの中身を全部ぶちこみ、リクエストに応じて、DBから読み出して表示するプログラムを最初は考えたのですが、やめになりました。

 

理由は3つです。

  • まだ頻繁に変更がかかる可能性がある生きているコンテンツだったこと
  • コンテンツの保守担当が、静的HTMLはかけても、DBやSQLについては全く知識がなくDBで管理する事自体を全力拒否されたこと
  • コンテンツ専用のメンテナンス画面を用意するなど大げさな仕組みを作るのは自分も含めて、みんなが嫌だったこと

 

で、すったもんだの結果、「ログインチェックをして、OKの場合はiframe内に対象のHTMLソースを表示するPHPプログラムを一本作る」に落ち着きました。

 

PHPプログラムの仕様です

 

作成するPHPのプログラム名は、「secchk.php」とします。


PHPのプログラムの置き場所は、https://hogehoge/のドキュメントルートです。


静的コンテンツを表示するためのURLはこんな感じを想定します。

https://hogehoge/secchk.php?ct=contents001.html


PHPプログラム自体の処理は大枠でこんな構造。

  1. URLパラメータから表示HTML名を得る(上記ならcontents001.html)
  2. ログイン中であることをチェック。違えば、ログインするように即すエラー画面。
  3. OKなら、contents001.html の本当の置き場所のURLに変換する。
  4. そのURLをiframeにわたして表示する。


ソースでエッセンスだけ抜き出して書くとこんな感じですかね。

//ここまででログインチェックがOKになった前提
if(isset($_GET['ct'])) {
$fname = $_GET['ct'];
}else{
$fname = 'error.html';
}
define("URL_PATH","http://hogedoc/doc/");
$disp_url = URL_PATH.$fname;
print("<iframe src=\"$disp_url\" width=\"100%\" height=\"800px\"></iframe>");

 

一応、これで表示はできます。

 

でも、iframeには落とし穴があります。

 

iframeの落とし穴を塞ぐ--その1

 

デフォルトのheightよりも長いコンテンツの時は、コンテンツにあわせてiframeの長さがのびてほしい。

 

普通はそう思います。

 

でも、iframeではそういう指定ができません。

 

これが落とし穴・・その1です。

 

普通にしてると、サイズが固定になって、スクロールバーがでてしまうのですね。

 

ちょっとみっともないのです。


なので、仕掛けが必要になります。

 

jquery-3.2.1.min.js とjquery.cookie.js を適当にダウンロードして、PHPソースを置くフォルダにjsフォルダを作って置いときます。

 

それで、secchk.phpのお尻の方(PHP命令の外。</body>の直前あたり)に以下のような記述を追加します。

<script src="js/jquery-3.2.1.min.js"></script> 
<script src="js/ifexp.js"></script>

 

ifexp.jsは自分で作ります。
中身はこうです。

('iframe')
.on('load', function(){
try {
$(this).height(0);
if(this.contentWindow.document.documentElement.scrollHeight > 800){
$(this).height(this.contentWindow.document.documentElement.scrollHeight);
}else{
$(this).height(800);
}
} catch (e) {
}
})
.trigger('load');

 

こうしておくと、HTMLをiframe内で表示する時に高さを自動調節してくれます。

 

iframeの落とし穴を塞ぐーーその2

 

上記の対応で表示できるようにはなりました。

 

でも、ブラウザには「ソースの表示」という機能があります。


それで表示して丹念にみると、

<iframe src="https://hogedoc/doc/contents001.html"  width="100%" height="800px"></iframe>


みたいに、iframeに渡されたURLががっつり表示されてしまいます。


このURLをブラウザに貼れば、セキュリティをパスして表示できてしまうようでは、まったく意味がありません。

 

これが落とし穴その2です。

 

なのでURLを直接たたいても表示できない仕掛けが必要です。

 

方針は簡単です。

 

<body>の中の表示させたくないコンテンツを<div>タグで囲んで、それを非表示(display:none;)にしておくのです。


今回は元々、<div class=out-f></div>で全体を括ってあったので、ここはCSSの変更だけでOKでした。

 

そして、表示用プログラムの「secchk.php」の中で、ログインOKなら、Cookieにそれを有効期限10秒くらいでセットし、HTML側でそのCookieがあるかどうかをチェックして、OKなら、display:none;を解除してやれば良いわけです。


こうしておけば、PHPプログラムを通してしか表示することができません。

 

実際のソースの抜粋です。

 

まずPHP側でログインチェックOKになったら、以下のようにcookieを10秒間だけの有効期限で発行します。

 

$expire = time() + 10;
setcookie('5Xa9Dfgt03', '5Xa9Dfgt03', $expire);
 

HTML側に埋め込む表示用のJavaScriptは、こんな感じです。

 

Cookieの存在チェックがOKなら、display:noneを解除しています。

if($.cookie('5Xa9Dfgt03')){
$(".out-f").css({'cssText': 'display:block !important;'});
}else{
//エラー処理。エラーページへの遷移とか。
}

 

上記はポイントの部分だけ抜き出してますが、iframeの中で表示させるときは、<script></script>で囲ってあるだけでは実行されません。

 

上記を、読み込み時に実行するように、例えば以下で囲っておくことはお忘れなく。

(function(){
   // 上記の処理
}());

 

そして、それを「sec.js」とかで保存したす。

 

上記が「即時関数」の形であることに注意してください。

 

そして、「sec.js」を置く位置は、bodyの<div class=out-f>の直後です。

<div class=out-f>

<script src="js/sec.js"></script>

 

こうする理由は、InternetExprorer対策です。

 

他の位置においたり、即時関数ではなく、よくある「$(function(){ });」の形で囲ったりすると、文末あたりに「2018/05/03追記」で書いた現象に苦しむことになります。

 

あ、そうそう。

 

</head>の前あたりで必要なライブラリは読み込んでおいてください。

 

<script src="js/jquery-3.2.1.min.js"></script>
<script src="js/jquery.cookie.js"></script>

 

今回は、pythonの使い捨てスクリプトを作って200ファイル一気に埋め込みましたが、HTMLに上記3行を貼り付けるだけなら、HTMLしかさわれなくてもOKですから、今後のメンテナンスの心配もありません。

 

これでiframeの穴は塞げました。

 

2018/05/03追記

>Divタグのdisplayを切り替えて表示した時に、iframeの内容に合わせた高さの自動調節がInternetExprorerで、効かなくなる現象が発生するみたいです。

>それも同じページで発生したりしなかったりです。

Chromeでは何の問題もないのですけれどね。

デバッグしてみたら、IEの場合、iFrameがLoadされた時点で「contentWindow.document.documentElement.scrollHeight」が生成されていない場合があって、そのときに例外が発生してるみたいです。

>只今、対応方法調査中です。

2018/05/08追記

>なんとか、対応方法を見つけました。

>それにあわせて、上記記事を一部修正しました。


まあ、いろいろな制限事項の中、最小限の手間でできることをした。

 

そういう感じでしかないですが、まあまあ・・じゃないですかね。

 

ではでは。