Nginx で扱えるセキュアなリンクを作成する
あくまで、Nginx では、URLのアドレスおよび URLパラメータ 情報から認証を行うだけです。
実際のURLアドレスおよび URLパラメータ は、phpスクリプトなどの外部の動的なページなどで作成する必要があります。
ここでは、phpを用いて動的なページで 一時的なセキュアなリンク を作成してみます。
以下は、例として http://exmaple.com/securelink.php でアクセスできる securelink.php ファイルとします。
また、http://exmaple.com/secure/ へのアクセスは、セキュアなリンクからのみを許容し、以外をすべて403を返信するものとします。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test SecureLink</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
<?php
// 秘密鍵 になります。なんでもOKですので、ここでは日本語(UTF-8)で設定してみます。
$secret = '日本語でもOK?';
// セキュアなリンク からでないとアクセスできないURIを設定します。
$path = '/secure/';
// 公開鍵として、有効期限を time() + 秒 で設定します。
// -- この有効期限の設定の仕方は、Nginxではこの方法でしか認識できません。
$timestamp = time() + 3600; // 60(秒) x 60(分) = 3600(秒) = 1(時)
// "秘密鍵 + パス + 公開鍵" を Nginxで扱うことができる md5 で暗号化します。
$hash = base64_encode(md5($secret . $path . $timestamp, true));
// +,/,= は、URLパラメータとして扱えないので、置換します。
$hash = strtr($hash, '+/', '-_');
$hash = str_replace('=', '', $hash);
// セキュアなリンクを出力します。
$url = "{$path}?s={$hash}&t={$timestamp}";
echo '<a href="'.$url.'">シークレットリンク</a>';
?>
</body>
</html>
|
ここでの例では、http://exmaple.com/securelink.php へアクセスすると以下のようなシンプルなリンクだけのページが表示されるはずです。
<a href="/secure/?s=9TYDMnkdAZ_9VM3OQjMGjQ&t=1369388653">シークレットリンク</a>
|
上記のようなリンクを出力します。
次に、ここでの例では、http://exmaple.com/secure/ へアクセスした際に、認証OKの場合に出力されるべきページを準備しておきます。
以下の例は、ファイル名省略なので、http://exmaple.com/secure/index.html でアクセス可能な index.html ファイルとします。
<pre lang="php" escaped="true">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test SecureLink</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>おめでとうございます。</h1>
<p>おめでとうございます。<br />
あなたは、見事にこのページを見ることができました。
</p>
</body>
</html>
|
このページが表示されれば、以下のような文言のページが表示されるはずです。
Nginx でsecure_link、secure_link_md5ディレクティブ を使ってみる
Nginxで先のセキュアなリンクを正しいか否かを判断するには、secure_link、secure_link_md5 2つのディレクティブを使用することになります。
secure_link : セキュアなリンク情報(URI,URLパラメータ)から、認証に使用する情報を設定します。
secure_link_md5 : セキュアなリンク情報(URI,URLパラメータ)および秘密鍵から、 md5 で暗号化した情報を作成します。
ここで作成した情報が、先の secure_link で設定した情報と一致するか否かによって認証を行います。
まずは、これらのディレクティブを使ってみましょう。
編集するのは、server ディレクティブが設定されているファイルになります。ここでの例は、/etc/nginx/nginx.conf とします。
...
http {
...
server {
listen 80;
root /var/www/html;
server_name example.com;
...
index index.php index.html index.htm;
...
location /secure/ {
# MD5暗号化情報 公開鍵(有効期限)情報を認証情報として設定します。
# -- URLパラメータ s=XXXX , t=YYYY をそれぞれ設定しています。
secure_link $arg_s,$arg_t;
# 秘密鍵 + URI + 公開鍵(有効期限)でMD5暗号化情報を作成します。
# -- 先に設定された認証情報との比較結果が $secure_link へ出力されます。
secure_link_md5 日本語でもOK?$uri$arg_t;
# 認証結果が MD5暗号化情報の不一致なら $secure_link は何も設定されません。
# -- この場合、アクセス制限の 403 を返信します。
if ($secure_link = "") {
return 403;
}
# 認証結果が 有効期限切れなら $secure_link に 0 が設定されます。
# -- この場合、アクセス制限の 403 を返信します。
if ($secure_link = "0") {
return 403;
}
...
}
...
# phpが動作するように設定します。
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
...
}
...
}
|
形式 : secure_link md5_hash[,expiration_time]
md5_hash : URI あるいは URLパラメータ情報の MD5暗号化情報 を指定します。
expiration_time : URI あるいは URLパラメータ情報の 有効期限情報 を指定します。省略可で、省略時は、無期限と判断されます。
形式 : secure_link_md5 secret_token_concatenated_with_protected_uri
secret_token_concatenated_with_protected_uri : URL および URI 、URLパラメータ情報などの羅列で、ここで指定された情報から MD5暗号化情報 が作成されます。
つまり、先のphpスクリプトでセキュアなリンクを作成した情報の手順および順序と全く同じ手順および順序でなければなりません。
また、ここで、先の secure_link ディレクティブ の md5_hashで指定された情報と比較されます。
そのmd5_hash情報とここで作成される MD5暗号化情報 が不一致の場合、$secure_linkはブランクが設定されます。
さらに、先の secure_link ディレクティブ の expiration_timeで指定された日付情報と現在日時から有効期限の判定も行われます。
有効期限が過ぎている場合、$secure_linkは 0 が設定されます。
ここでは、日本語を編集していますので、必ず、文字コードをUTF8で保存しましょう。
これを間違えるとNginxの起動で失敗します。
上記のように設定、編集を終えたら、Nginxにて再読み込みを行います。
$ /etc/init.d/nginx reload
...
|
最後に確認してみる
先の例では、以下のような順番でアクセスすれば正しく表示できるはずです。
- http://exmaple.com/securelink.php へアクセスする
- “シークレットリンク” をクリックする
以下の画面が表示されればOKです。
また、失敗した場合は、以下のような画面が表示されるはずです。
もちろん、直に http://exmaple.com/secure/ へアクセスしても上記の403エラーが出力されます。
上記の設定例では、時間切れが1時間後となりますから、うまく表示できていたURLを1時間後にアクセスしてみてください。
同じように、403エラーが出力されるはずです。
一瞬ですが、この公開鍵(有効期限)は、誰でも作れますから、これが、なんでセキュアなの?なんて思うかもしれません。
つまり、最初に出力されたリンクの公開鍵(有効期限)だけを書き換えれば、永遠に使えるんじゃないの?なんて思うかもしれません。
<a href="/secure/?s=9TYDMnkdAZ_9VM3OQjMGjQ&t=1369388653">シークレットリンク</a>
<a href="/secure/?s=9TYDMnkdAZ_9VM3OQjMGjQ&t=1369400000">シークレットリンク</a>
|
この編集したリンクをクリックしても、ちゃんと403エラーが出力されます。
当たり前ですよね、これは、MD5の暗号情報の中に公開鍵(有効期限)も埋め込まれているので、そこで不一致となりはじかれます。
MD5の暗号情報を作成する php コード でも Nginxでの secure_link_md5 ディレクティブでも この 公開鍵(有効期限)を埋め込んでいるのがポイントです。
いかがだったでしょうか?
このセキュアなリンクって、意外と多いんですよね。
これは、認証もPHPでやれますから、Nginxでやる意味は、クラック対応、負荷対応になるんでしょうね。
情報の順番等々、簡単な間違いがなければ、それほど難しいものでもないので、必要な方は、是非、おすすめしたいところです。
コメントを投稿 :