2011-07-23

[anyevent] 設定ファイルを SIGHUP で再読み込みする

最近は新進気鋭の Web Application Framework: Amon2 にも倣って、設定ファイルはPerlでハッシュリファレンスを返すように書いて、それをdoするようにしていて、シンプルで良いなと思っています。

AE_PerlIsm では、さらにブラックリスト追加などをTwitterへのStreming APIの接続を切らずに対応できるように、以下のようにシグナルにコールバック関数を登録する AE::signal 'HUP', \&callback を使って好きな時点で読み込めるようにしてみました。

この変更により、たとえば無名関数で書いたスパム判定ロジックを本体が動作したままアップデートすることも可能になります。

$config 内で $config を参照するのは循環参照でリークして良くない例なので修正。

hogehoge.pl (本体)

use strict;
use warnings;
use AnyEvent;
use AnyEvent::Twitter::Stream;

my $config_filename = 'hogehoge.conf.pl';
my $config;

reload_config();
$config or die;

main_loop();

sub main_loop {
    my $reload_config_t = AE::signal HUP => \&reload_config;

    my $streamer = AnyEvent::Twitter::Stream->new(
        username => $config->{username},
        password => $config->{password},

        on_tweet => sub {
            my $tweet = shift;
            return if $config->{is_spam}->($config, $tweet);

            ...
        },
    );

    AE::cv->recv;
}

sub reload_config {
    warn "reload_config";

    my $config_tmp = do $config_filename;

    if (defined $config_tmp) {
        $config = $config_tmp;
        warn "$config_filename: successfully loaded";
    } else {
        warn "$config_filename: syntax error: $@" if $@;
        warn "$config_filename: error: $!" if $!;
    }
}

hogehoge.conf.pl (設定ファイル)

use strict;
use utf8;
my $config = {
    username => 'hogehoge',
    password => 'foobar123',

    blacklist => [
        1111111,
        2222222,
        qr!3333*!,
    ],

    is_spam => sub {
        my ($config, $tweet) = @_;

        return $tweet->{screen_name} =~ /^spam/
            or $tweet->{screen_name} eq $config->{username};
    },
};

[windows] Aeroが落ちて画面がしょぼくなった場合の対処法

Windows 7などを使っていて、ごくまれにAero(dwm.exe)が死んで透過表現が無くなり、見た目が急にしょぼくなったことはありませんか?基本的には自動的に再起動されるはずですが、手動でもファイル名を指定して実行から、dwm を起動すればすぐに元通りになります。お試しあれ!

また、自動的に問題を発見するツール「透明度やその他の視覚効果に関する問題の発見と解決」を実行することで解決の手助けになるかもしれません。コントロールパネルの検索で見つかります。

2011-07-19

[python] datetime から unix epoch を得る

Python 2.5 で、datetime から、JavaScript で使いやすいミリ秒で表した UNIX 時刻を作る方法を考えてみました。

# coding=utf-8
import time, calendar, datetime, pprint

def datetime_to_epoch_1(dt):
    return int(time.mktime(dt.timetuple())) * 1000 + int(dt.microsecond / 1e3)

def datetime_to_epoch_1_utc(dt):
    return int(calendar.timegm(dt.timetuple())) * 1000 + int(dt.microsecond / 1e3)

def datetime_to_epoch_2(dt):
    delta = dt - datetime.datetime.fromtimestamp(0)
    return delta.days * 86400000 + delta.seconds * 1000 + int(delta.microseconds / 1e3)

def datetime_to_epoch_2_utc(dt):
    delta = dt - datetime.datetime.utcfromtimestamp(0)
    return delta.days * 86400000 + delta.seconds * 1000 + int(delta.microseconds / 1e3)

pp = pprint.PrettyPrinter()
now_local = datetime.datetime.now()
now_utc = datetime.datetime.utcnow()

pp.pprint([
    datetime_to_epoch_1(now_local),
    datetime_to_epoch_2(now_local),
    datetime_to_epoch_1_utc(now_utc),
    datetime_to_epoch_2_utc(now_utc),
    time.time(),
])
# [1311003732034L,
#  1311003732034L,
#  1311003732034L,
#  1311003732034L,
#  1311003732.0339999]

2011-07-17

[Node.js] 続 /dev/urandom を延々と出力する HTTP サーバー

この記事は過去の記事 [Node.js] /dev/urandom を延々と出力する HTTP サーバー の続きです。

ドキュメントをよく読んでいたら、Readable Stream のインスタンスメソッド pipe を使うと簡単に書けることが分かりました。このメソッドは引数に Writable Stream を渡すと、drain を監視しながら少しずつ流し込む、過去の記事と同様の動作を勝手にやってくれる働き者です。

さらにこれと同様の動作をする util.pump というのもありますがお勧めしないとのことです。
参考: Difference stream.pipe and util.pump?

var fs = require('fs'),
    http = require('http'),
    PORT = 10001;

http.createServer(function (req, res) {
    var stream = fs.createReadStream('/dev/urandom');

    res.writeHead(200, {
        'Content-Type': 'application/octed-stream',
    });

    stream.pipe(res);

}).listen(PORT);

[perl] Imager で背景用タイル画像を動的に生成する

このブログをご覧の数少ない読者さんはもうご存知かもしれませんが、hidekiy.com の背景画像と配色その他を夏用にリニューアルしてみました。

ひとつ前のバージョンでは、Image::Imlib2 を使っていたのですが、HSV色空間への対応や、変数への書き出しのあたりがイマイチだと思っていたので、今回は新しく Imager を使ってみました。
$ cpanm Imager
で簡単にインストール出来る点と、丁寧なドキュメントを考慮すると、ImageMagickより良さそうな気がしました。

ソースコードは以下の通りです。

use strict;
use warnings;
use Imager;
use Plack::Request;

my $app = sub {
    my ($env) = @_;
    my $req = Plack::Request->new($env);

    my ($xsize, $ysize) = (60, 60);

    my $img = Imager->new(
        xsize => $xsize,
        ysize => $ysize,
    );

    $img->box(filled => 1, color => 'white');

    my $hue1 = rand(360);
    my $hue2 = $hue1 + 60;

    for my $i (0 .. 9) {
        my $color =  Imager::Color->new(
            hue => ($i % 2 ? $hue1 : $hue2),
            saturation => rand(0.1),
            value => 1,
        );

        $img->box(
            xmin => rand($xsize),
            ymin => rand($ysize),
            filled => 1,
            color => $color,
        );
    }

    my $data;
    $img->write(type => 'png', data => \$data);

    my $res = $req->new_response;
    $res->status(200);
    $res->content_type('image/png');
    $res->body([$data]);

    return $res->finalize;
};

出力例(60x60pxの画像を25個くっつけたもの)

動かし方: app.plで保存して、$ plackup app.pl

まず、$hue1, $hue2は、色相環で考えたときに60度離れた色相を2つ用意しています。
10回のループ中で、画像内のランダムな任意の一点から右下隅までを塗りつぶします。
塗りつぶすときの色は、用意した色相を交互に用いながら、彩度をランダムに変更することで、ちょっと色のくすみ方が変わります。

これをRGBで選ぶ方法でいい感じにするのは難しいので、HSV色空間を理解されると色指定が簡単になると思います。

[perl] 一部の CPAN モジュールに必要な devel パッケージ一覧

CPANモジュールをインストールしようとしても、エラーが出てインストールできないとき、そもそもCコンパイラが入っていなかったり、コンパイル時に必要となるヘッダファイル(hogehoge.h)が含まれている開発用パッケージ(foobar-devel)が入っていない場合があります。

ぜひこの記事を参考に、インストールはあっさり完了してしまって、速やかに本題に取り掛かかってください。

# 基本的に必要
yum install make gcc

# SSL
yum install openssl-devel
cpanm Net::SSLeay
cpanm LWP::Protocol::https

# 多倍長整数演算
yum install gmp-devel
cpanm Math::BigInt::GMP
cpanm Crypt::DH
cpanm Net::OpenID

# 多倍長整数演算
cpanm -v Math::Pari
最新のソースコードパッケージを自動で取ってくるかと聞くプロンプトには、'y'と答える
> Fetch? (y/n, press Enter) y

# XML
yum install libxml2-devel
cpanm XML::LibXML
cpanm Web::Scraper

# XML
yum install expat-devel
cpanm XML::Parser

# コンソールの制御用?
yum install readline-devel
cpanm Term::ReadLine::Gnu
cpanm Dvel::REPL

# JPEG
yum install libjpeg-devel
cpanm Imager::File::JPEG

# PNG
yum install libpng-devel
cpanm Imager::File::PNG

# 画像処理
yum install gd-devel
cpanm GD

# 圧縮
yum install zlib-devel
cpanm Compress::Zlib

見当たらない場合、ヘッダファイルの名前からパッケージを検索するには、yum providesを使います。Debian系なら、apt-file search です。

実行例:

$ yum provides '*/gmp.h'
Loaded plugins: fastestmirror, priorities, security
gmp-devel-4.3.1-7.10.amzn1.x86_64 : Development tools for the GNU MP arbitrary precision library
Repo        : amzn-main
Matched from:
Filename    : /usr/include/gmp.h

2011-07-08

Pyrit による解析から無線LANネットワークを守るには

Pyrit などによる総当たり攻撃から、個人用無線LANネットワークを守る方法についての記事です。

GIGAZINE さんの記事 無線LANの WPA / WPA2-PSK を GPU で超高速解析してパスワードを見つけるフリーのオープンソースソフト「Pyrit」 では対策の指針が示されていないので補足してみます。

簡単な対策法
暗号化方式として、WPA2-PSK (AES) を選択し、事前共有鍵 (PSK) として 63文字の鍵を設定します。鍵は安全な場所で管理してください。記憶する必要はありません。

詳細
SSID ステルス機能や、MAC アドレス限定接続機能、プライバシーセパレータなど子機間通信を制限する機能は、対策したような気分以外において全く意味がないので無視してよいです。WPA2-PSK において、攻撃者に対する唯一にして最大の抵抗策は、十分な強度の事前共有鍵 (PSK: Pre Shared Key) を設定することのみによってなされます。

そのような鍵の作り方は、手ごろな所で、OpenSSL 付属の openssl コマンドを rand オプションを付けて実行すれば、OS の乱数生成機由来の暗号学的に安全な乱数で作った文字列を取得できます。

$ openssl rand -base64 100
PyPTFpJyRrj94+AzhVKqs4ipUbZXJ7/RCefcAbk9mEiPr8H78tsVjUaLDOvT1OmP
QYkZGqdGfc2Ng0UmCZFFE4FgYuphU9uTb5hXzV5FM6vmbfi8cMmKC6aoCzDx7h01
KuFi9g==

出力はデフォルトではバイナリ列なので、-base64 オプションを追加して Base64 でエンコードしてみました。これを事前共有鍵の ASCII 指定での最大長63文字つかえば、6bit * 63 = 378bit となるので、最終的な 256bit の鍵を作るにあたっては十分な強度になっています。

OpenSSL を信用しない方は以下のようにしても良いです

$ head --bytes 100 < /dev/urandom | base64
w/I2+XY9Vymu8feYIbWn9T/L7yZk0ilfYObwoJDaRS3/FJ/0r5HSawyObGAG50aAcmr+Qqwh0D2x
SEIvwR/VM5bJRvpawoc0k9Fp6JV2igLhgFRqdSFXmMKxGSqNR5V7Sdtjpg==

Windows の方は PowerShell を使って以下のようにすると良いです

PS> $byteArray = New-Object Byte[] 100
$rngProvider = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
$rngProvider.GetBytes($byteArray)
[System.Convert]::ToBase64String($byteArray)
/PppxyEFLAMlbr0WaIMSwFQ15RdtCIbZ3+csAYkVRuWwqX7nydT8/Rv6Trvan1O0p+HnPLJbF4k4SIEyjB3eMbiUie2aPNu0iT72bLbfZOoMgGTDkAJbmEj
86byd24vqXo9k6w==

64文字の16進数文字列で 256bit の鍵を直接指定する方法もありますが、アクセスポイント側の実装が怪しい場合があるようです。

注意
C言語のの rand() 関数などで作れる疑似乱数には、乱数種 (長く見積もって 64bit) 以上の乱雑さがないので、これに由来する鍵は使ってはいけません。参考: 擬似乱数

結論
辞書もしくは組み合わせ生成による総当たりで割られるような弱い鍵を使ってはいけません。暗号鍵の運用上の問題で、暗号そのものの持つ機能が発揮されないと、設計よりずっと弱い保護しかなされず、攻撃に敗れます。無線LANルーターはWPA2の次の仕様では、事前共有鍵として、20文字以下の短い鍵の使用を拒否するべきです。

毎秒数十万回?
毎秒100万回 (1e+6/s) は、毎年 3.15e+13 回で、鍵全体が 2^256 (PSK を ASCII で指定した場合、関数 PBKDF2 によって、256bit の鍵になります) = 1.16e+77 個分をこの速さで全体を調べるには 3.68e+63 年となり全然歯が立ちません。しかし、鍵が弱いことが分かっている場合 (小文字で8文字と分かっているなど) は、全範囲を調べる必要がなくなるので、現実的な時間で破られる場合があります。

アルファベット小文字で 8文字の鍵を全部調べるのにかかる時間を計算してみると 26^8 / 1e+6 / (3600*24) = 2.4日となり、全部調べる必要がある場合は最悪の場合で、半分調べれば鍵を見つける可能性が 50% になるので、平均 1.2日で破ることができます。

参考
The twilight of Wi-Fi Protected Access « Pyrit Pyrit作者の記事
WPA key calculation - From passphrase to hexadecimal key

2011/7/9 追記
GIGAZINE さんで記事が追加され、この記事が紹介されました。ありがとうございます。
無線LANのWPA/WPA2-PSKを総当たりで突破する「Pyrit」の実際の解析速度と自衛手段について

2012/3/16 追記
鍵の作り方について長らくめんどくさい方法を推薦していましたが、正直なところ、ごちゃごちゃっとキーボードを叩いて 63文字分の鍵を作ってもおそらく十分な強度があります。作った後適当に大文字にして数字や記号も散りばめておきましょう。

2012/12/14 追記
Pyrit のやっていることは結局のところ GPU Accelerated PBKDF2 ということになるので、ASCII で強い鍵が設定されていることが分かっているときは PBKDF2 のことは忘れて 256bit AES をそのまま破る方に尽力した方が速く破れると思います。