Monthly Archives: 12月 2012

FrameWorkへ食べさせたくないURLを事前にmod_rewrite

PHPのFrameWorkなどを使って組んでいると、/page/order/form/といったようなパスをmod_rewriteを使ってindex.php?mod=page&func=order&step=form というようなアクセスへ置き換えられてすべてFrameWorkのコントローラへ食べさせてしまいます。

単純な静的HTMLを置いているディレクトリがある場合は、事前にRewriteRuleに引っ掛けて –  [L] (何もしないで書き換え終了) としてしまって、制御が渡らないようにすると良いです。

RewriteEngine On
RewriteCond %{REQUEST_URI} ^/faq/
RewriteRule ^.*$ – [L]

~以下、本来のFrameWorkのRewriteRule

PHP5.3~の互換性警告を抑制

将来使えなくなる予定の非推奨関数を使っている場合に、PHP5.3から警告が出るようになったため、それを制御するための E_DEPRECATED が追加されました。

修正したほうがいいことは確かなのですが、とりあえず急ぎ移行する必要があるシステムでは、いっそ警告を抑制してしまったほうが手っ取り早いです。

php.iniに設定するなら以下のように

error_reporting = E_ALL & ~E_DEPRECATED & ~E_NOTICE

.htaccessに書く場合は数値で指定しないといけないので、E_ALL(30719) - E_DEPRECATED(8192) - E_NOTICE(8) = 22519

php_value error_reporting 22519

ただし、E_ALLの数値はPHPバージョンで変わるので要注意です。
PHP 5.4.xでは、32767
PHP 5.3.xでは、30719
PHP 5.2.xでは、6143
それ以前では、2047

mod_rewriteを使ってSSLオンオフを自動切換え

問合せコーナーの中はSSL有効で、他のコーナーはSSL無しで、というサイト作りが一般的だと思いますが、http と https で分けなければいけないため、ナビゲーションのリンクを絶対パスで書かなければならないというのが非常に不便です。

HTML上は普通に相対パスで記述しておき、SSLの必要なコーナーになったらhttpsへ、それ以外のコーナーになったらhttpへ自動的にページ遷移してくれるとメンテナンスが楽です。

以下のように.htaccessでRewiteしておけば、自動的に遷移してくれます。これなら、途中で別なコーナーもSSLにしたいと要望されてもリンク貼り直しも不要です。

HTTPS=on で/contact/~じゃないURLにアクセスがあったら、http://で始まるURLにリダイレクト。
逆に HTTPS=off で/contact/~というURLにアクセスがきたら、https://で始まるURLへリダイレクトさせます。

RewriteEngine On

RewriteCond %{HTTPS} on
RewriteCond %{REQUEST_URI} (\.html)?$
RewriteCond %{REQUEST_URI} !(^/contact/)
RewriteRule ^.*$ http://%{HTTP_HOST}%{REQUEST_URI} [R,L]

RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} (\.html)?$
RewriteCond %{REQUEST_URI} (^/contact/)
RewriteRule ^.*$ https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

もし、/order/もSSL対応させたければ (^/(contact|order)/) に変えればOK

わざわざ拡張子を.htmlに限定しているのは、自動切換え対象をhtmlだけに限定しておかなければ、html中で参照している/css/や/js/や/img/など、サイト全体で共通で使っているファイルまで勝手にSSL自動制御してしまうので、htmlはSSL有なのにcssはSSL無しなどと食い違ってしまうと、読み込みに失敗するからです。
?$ というのは、/で終わっている場合も想定しての指定です。

HTTPステータス

非常によく使う。よく出てくるものだけ。

301 Moved Permanently 恒久的移動 (ブックマークし直しを希望)
302 Found 一時的移動 (何らかの理由で別な場所に移動してほしい)
304 Not Modified  更新されていない
-----
400 Bad Request リクエスト内容がおかしい
401 Unauthorized 認証されていない
403 Forbidden アクセス権なし
404 Not Found 存在しない
405 Method Not Allowed 許可されていないGETまたはPOSTをした
407 Proxy Authentication Required Proxy認証が必要
408 Request Timeout 要求が終わらなかった (ヘッダが完了していない等)
-----
500 Internal Server Error サーバー内部のエラー発生
503 Service Unavailable 一時的過負荷

不正アタック排除にfail2ban

PostfixやDovecotやsshなどをサーバ上で立ち上げておくと、海外などから不正アクセスしようと頻繁にログインを試みるアクセスがきて、すぐにログがパンパンになってしまいます。いくら難解なパスワードを設定しているとは言っても、そのまま放置しておくのは気持ちが悪いし、そんなしょっちゅうログを覗くわけにもいきません。

そこで入れたいのが、fail2ban

設定したパターンに従ってサーバのログを監視し、「ログイン失敗」などが連続した場合に、その接続相手のIPアドレスを一時的にブロックしてくれます。ブロックする方法はhosts.denyへの追加か、iptablesへの追加が選べて、ブロックした場合にはメールで管理者へ連絡をすることも可能です。

CentOS6 なら yum install fail2ban でも導入可能 (設定ファイルをきちんと整備しないと稼動しませんが、そこはネットで調べてください)

fail2banが稼動していれば安心感が違います。管理者の皆さん、ぐっすり眠れますように(笑)

サーバにVPN入れて固定IPゲートウェイに

サーバのセキュリティを高めるため接続元のIPアドレスでアクセス制限を掛ける事は常ですが、外出先や、旅行先など、普段と違う場所で仕事をするときに、固定IPアドレスが使えなかったり、普段とIPが変わってしまうと仕事に支障が出てしまいます。

そこで、レンタルサーバにVPNサーバを立ち上げれば、レンタルサーバが固定IPのゲートウェイとして使えて、そういうときにとても便利。(サーバからインターネットへ橋渡しする部分はiptablesでMASQUERADEを設定してパケットの発信元をサーバのグローバルIPになるよう書き換えて通過させます)

OpenVPN, PPTP(Poptop使用), L2TP/IPSec(Openswan使用)などいくつかのVPNサーバを使ったことがありますが、OpenVPNはセキュリティは高いですがクライアント側にソフトが必要。PPTPとL2TPならどのOSでも標準機能で接続できますが、PPTPは軽い代わりに暗号化に難あり。
今の一番のお勧めはL2TP/IPSecでしょう。昔はカーネルリコンパイルが必要で挫折した記憶があったのですが、今はOpenswanを使えば、ソフトの設定だけで立ち上げられます。

設定手順はあちこちのサイトに情報があるので省略しますが、どのサイトでも一番苦労しているのが、/etc/ipsec.d/l2tp-psk.conf の設定のようで、このファイルの設定内容によっては接続できないOSが出てくる模様です。

そこで、私が身内の端末も借りて試行錯誤した結果、一番Bestだと思われたl2tp-psk.conf を書いておきます。
(一応、WinXP, Win7, WinVista, MacOSX, iOS5, Android3 すべてで接続を確認できました)
※ forceencaps=yes があるとXPでNG、ike=3des-md5 esp=3des-md5があるとMacでNGでした。

conn L2TP-PSK-NAT
    rightsubnet=vhost:%priv
    also=L2TP-PSK-noNAT
    forceencaps=no
    dpddelay=5
    dpdtimeout=20
    dpdaction=clear
conn L2TP-PSK-noNAT
    authby=secret
    pfs=no
    auto=add
    keyingtries=3
    rekey=no
    ikelifetime=8h
    keylife=1h
    type=transport
    left=%defaultroute
    leftnexthop=%defaultroute
    leftprotoport=17/%any
    right=%any
    rightprotoport=17/%any

追記:これでもAndroid4.0.4では接続ができませんでした。でも、調べてみると、それはAndroid4のIPSecのバグらしいです。うーん。

PHP5を入れたら最初に設定

だいたいこんな感じ。date.timezone = Asia/Tokyo が最近のバージョンで増えた項目なので要注意
vi /etc/php.ini

; upload_max_filesize = 2M ←PDFなどをUPするなら増やす
; display_errors = Off ←デバッグ中ならOnに
output_handler = mb_output_handler
auto_detect_line_endings = On
date.timezone = Asia/Tokyo
; extension=imagick.so ←ImageMagic入れてたら組み込む
mbstring.language = Japanese
mbstring.detect_order = auto
mbstring.http_input = auto
mbstring.internal_encoding = UTF-8
; mbstring.http_output = SJIS-win ←(携帯用なら)cp932の意味
mbstring.http_output = UTF-8
mbstring.encoding_translation = On
mbstring.substitute_character = none;

CentOS環境を日本語に

まず最初にOS環境を日本語に変えましょう。

vi /etc/sysconfig/i18n

LANG="C" となっているはずなので、LANG=ja_JP.UTF-8 に変更。再起動などは不要。シェルに入り直せば反映されます。

ls -la でファイル一覧を表示して、「10月」などと日本語表記されていればOK

JavaScriptでゆっくり1件ずつ処理

JavaScriptで、ゆっくり1件ずつ処理させたい。でも、JavaScriptにはsleep関数にあたるものがありません。

たとえば、Ajaxなどでサーバ側の処理を1件ずつゆっくり呼び出すなど。そういったJavaScript側でsleepを挟みたくなるような場面はよくあります。

ネット検索してよく出てくる例は、「待ちたい場面で、一定秒数過ぎるまで、for空ループでクルクル回す」というもの。。
でも、それではブラウザに無駄な負担を掛け続けてしまいます。

setTimeout を使って一定秒数経つごとに1件分の処理を呼び出すほうが正解でしょう。

<span id="result">ここにカウント表示</span>

<script type="text/javascript">
var wait = 1000;	// 1000分の1秒単位で指定
var i = 0;
var result = document.getElementById('result');
// 1秒おきに30までカウントしてみる。
function func1(){
	if (i < 30) {
		i++;
		result.innerHTML = i;
		// 次の1回を仕込む ※wait秒後に実行
		setTimeout("func1()", wait);
		return;
	} else {
		result.innerHTML = '終了';
	}
}
// 最初の1回を仕込む ※即時実行
setTimeout("func1()", 0);
</script>

サンプル実行
ここにカウント表示

「ページの有効期限が切れました!」

アレですね。PHPで最も有名で、最も頭を悩ませるアレですね。
実に多い → Google検索 「PHP 有効期限切れ」 約 3,760,000 件

ようは、余計なお世話はいらん!Perlで書いたCGIと同じように何も制御しないで欲しい!というのが皆の意見だったわけですが。。

そのためには、session_cache_limiter に、やれ “private_no_expire” を指定しろだの、”public, must-revalidate” だの。いやいや “none” だの… それは想定外の値だったときに、処理がスルーされるという仕様を逆手にとったすり抜けだから良くないだの…

諸説いわれてきましたが、ようやくマニュアルに明記されました。
PHP: session_cache_limiter – Manual

キャッシュリミッタを '' にすると、 キャッシュヘッダの自動送信を完全に無効化します。

「何もしない= ’’ 」です!はい。終了(^_^)