Perl で、サーバにモジュールがインストールされているか確認する方法
コマンドラインから、以下の 1行を実行する。
引数 -M と <モジュール名> の間には、スペースはいらない。
# perl -M<モジュール名> -e ''
もしモジュールがインストールされていれば何も起こらない。モジュールがインストールされていなければ Can't locate <モジュール名>.pm 云々というエラーが出る。
コマンドラインから、以下の 1行を実行する。
引数 -M と <モジュール名> の間には、スペースはいらない。
# perl -M<モジュール名> -e ''
もしモジュールがインストールされていれば何も起こらない。モジュールがインストールされていなければ Can't locate <モジュール名>.pm 云々というエラーが出る。
SecureRandom クラスを使う。以下は、16 バイトのランダムなバイト列を作る例。
import java.security.*;
...
public final static String RANDOM_ALGORITHM = "SHA1PRNG";
public final static int RANDOM_LENGTH = 16;
...
public byte[] getRandom() {
try {
SecureRandom random = SecureRandom.getInstance(RANDOM_ALGORITHM);
byte seed[] = random.generateSeed(RANDOM_LENGTH);
byte b[] = new byte[RANDOM_LENGTH];
random.setSeed(seed);
random.nextBytes(b);
}catch(Exception e) {
e.printStackTrace();
}
return b;
}
以下の encrypt メソッドの引数に String でテキスト文字列を渡すと、暗号化されたものがバイト列で返ってくる。復号は decrypt の引数にそのバイト列を渡す。両メソッド共、crypt_spec 引数に AES を指定すれば AES で暗号するし、DES を指定すれば DES で暗号化する。
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
// CHANGE THIS VALUE DEPENDING ON WHAT YOU WANT
public static final int MESSAGE_LENGTH = 1024;
/**
* @param text
* Message to encrypt
* @param secret_key
* Secret Key
* @param crypt_spec
* Crypt algorithm like "AES" or "DES"
* @return Encrypted Byte Strings
*/
public byte[] encrypt(String text, byte[] secret_key, String crypt_spec)
throws InvalidKeyException, IllegalBlockSizeException, IOException,
BadPaddingException {
SecretKeySpec sKey = new SecretKeySpec(secret_key, crypt_spec);
byte[] secret = new byte[MESSAGE_LENGTH];
try {
Cipher cipher = Cipher.getInstance(crypt_spec);
cipher.init(Cipher.ENCRYPT_MODE, sKey);
secret = cipher.doFinal(text.getBytes());
} catch (Exception e) {
System.out.println(this.getClass().getName()
+ ".encrypt: Exception:");
e.printStackTrace();
}
return secret;
}
/**
* @param b
* Encypted Byte Message
* @param crypt_spec
* Crypt Algorithm like "AES" or "DES"
* @return Decrypted String
*/
public String decrypt(byte[] b, byte[] secret_key, String crypt_spec)
throws InvalidKeyException, IllegalBlockSizeException, IOException,
BadPaddingException {
SecretKeySpec sKey = new SecretKeySpec(secret_key, crypt_spec);
byte[] secret = new byte[MESSAGE_LENGTH];
try {
Cipher cipher = Cipher.getInstance(crypt_spec);
cipher.init(Cipher.DECRYPT_MODE, sKey);
secret = cipher.doFinal(b);
} catch (Exception e) {
System.out.println(this.getClass().getName()
+ ".decrypt: Exception:");
e.printStackTrace();
}
return new String(secret);
}
PHP の場合は、CGI の環境変数は組み込み変数である $_SERVER に入る。以下の内容を env.php などのファイル名で保存し、ブラウザからアクセスする。
<?php
print '';
foreach($_SERVER as $key => $e) {
print '<tr><td>';
print "$key</td><td>$e";
print '</td></tr>';
}
print '
';
?>
いわゆるログイン処理において、ユーザーがウェブページのフォームから入力してきたユーザー名とパスワードをデータベースに保存されているものと照合し、認証するという仕組みはメンバー制サイトにはもはや欠かせないものとなっている。ここで、ユーザー名とパスワードは SSL で暗号化された通信路(チャネル)を通じてサーバに送るのは当然として、データベースにユーザー名とパスワードを生のまま入れていた場合は、もしデータベースが何者かにハッキングされたらパスワードがそのまま漏洩してしまうことになる。パスワードというものはある個人であればどのサイトでも共通にしていることも多いのではないだろうか? あなたはまさにこのエントリを読んでいるくらいなのだからそんなことはないかも知れないが、一般ユーザーのセキュリティに対するリテラシーはどうだろう?そのようなことを前提にすると、仮に(あってはならないことだが)自サイトからパスワードが漏れた場合、そのパスワードを使って悪意のあるものが次々に他のサイトへの認証を試みるシナリオもあると思う。また、データベースがハッキングされないまでも、管理者がデータベースを覗けばユーザーが設定したパスワードを簡単に見ることができるシステムは、やはりセキュリティ上問題であろう。
このような事態を避けるためにも、セキュリティの基本ではあるが、データベース中のパスワードはハッシュ文字列で保存しておくべきである。たとえデータベースの管理者がデータベースに保存されているハッシュ値を覗けたとしても、そのハッシュ値からのパスワード自体の復元はほぼ不可能だからだ。このような仕組みにすると、ユーザーがパスワードを忘れた場合はパスワードで照合できなくなるので、再設定の仕組みが必要である。このポイントを頭に入れて注意深く世のサイトがどのように作られているか、普段からアンテナをはって参考にして欲しい。
この方法は特に Perl だけにできるということではなく、もちろん言語を問わず、ハッシュ関数が用意されている Java や PHP にも使える。
たとえば、あるウェブサイトのコンテンツ (ウェブページ) が変更されたかどうかをチェックする処理を考えてみる。Perl で、文字列比較を行う場合には if($a eq $b) {...} などとすればいいだろう。ただしこれは $a と $b が比較的「小さな」文字列であった場合である。それでは HTML などのデータで、以前アクセスしたときと今回アクセスしたときで内容が変更されているかどうかを効率的にチェックするにはどうしたらいいのだろう。1つの解法としては、ハッシュ関数を使うことが考えられる。Perl の変数で「ハッシュ」というものが出てくるが、こちらは「ハッシュ関数」である。ハッシュ関数としては、以前は MD5 というものが多く使われていたが、コンピュータの処理速度の向上によって相対的にセキュリティが低下したので、より安全な SHA1 を利用する。
使用するクラス
use Digest::SHA1;
use Digest::SHA1 qw(sha1_hex); # sha1_hex 関数をインポートしておく
my $content = "ここに HTML などのコンテンツが入る";
my $digest = sha1_hex($content); # コンテンツのハッシュ値 (16進数)
print $digest;
SHA1 は、もともとあるデータから 160ビット (20 バイト) の固定長の一意のデータを生成する。このデータを「ハッシュ値」と言っているが、ハッシュ値は元のデータが 1文字でも違えば、値自体もまったく違うものとなることが約束されている。また、ハッシュ値から元のデータは復元できないようになっている (これを一方向関数という)。Digest::SHA1 モジュールには単に my $data = sha1($content); とすればハッシュ値をバイナリで出力してくれるが、視覚的に見たい場合、文字列として処理する場合 (たとえばファイル名にこのハッシュ値を使うこともできる) は sha1_hex を使う。sha1_hex を使った場合はハッシュ値が 16進数の文字列として出力されるため、固定長 40バイトのデータとなる。 この方法は特にPerl だけにできるということではなく、言語を問わず、ハッシュ関数が用意されている Java や PHP にも使える。
Apache::Session を使う。 パフォーマンスを少し測ってみたら CGI::Session よりも若干速いようである。
このモジュールは eval {...}; とセットで使うとよい。 - Perl で、例外処理をするような感じのもの。if($@) {...} のセットで使う。eval にはセミコロン(;) を忘れないこと。
例では、%session を $session としてハッシュで返している。
このモジールを使う利点は、ID の長さを自由に変えられることである。CGI::Session の場合は、ID の長さを変えたければ、自分で書かなければならない。一方で短所は、セッションの有効期限を設定できないこと。そのため、MySQL 中のsessions テーブルに timestamp フィールドを独自に追加して、自身の CGI の中か cron で適宜 reap する必要がある。また、セッションオブジェクト中の個別のデータにも有効期限を設けることはできない (CGI::Session はできる)。それでもこのモジュールが良いのは、パフォーマンスが CGI::Session よりも若干良いのと、ID の長さを変えられるからである。
use DBI;
use Apache::Session;
このモジュールは永続ストレージ (要はセッション情報をずっと溜めておけるデータベース) の種類によっていろいろな種類がある。
Apache::Session::MySQL
Apache::Session::Postgres
Apache::Session::File
Apache::Session::Oracle
Apache::Session::Sybase
Apache::Session::DB_File
Apache::Session::Flex 実行時にすべてのパラメータを指定する
以下の例では Apache::Session::Flex を使っている。
use DBI;
use Apache::Session::Flex;
$session = _getSession;
print $session->{_sessin_id}
sub _getSession {
my $db;
my ($database, $username, $password);
# 以上の変数には値を入れること。
$db = DBI->connect("DBI:mysql:$databasename",
$username,
$password
) || die 'Cannot connect db';
eval {
tie %session, 'Apache::Session::Flex', $sid, {
Store => 'MySQL',
Lock => 'MySQL',
Generate => 'MD5',
IDLength => 32,
Serialize => 'Storable',
Handle => $db,
LockHandle => $db,
};
}; # セミコロンを忘れないこと
if($@) { # $@: error message of eval
return undef;
}
# セッション ID の取得に成功。ここに処理を書く。
return \%session;
}