mikanmarusanのブログ

テクノロジーとかダイビングとか

PHP5.5.0 から導入されたパスワードハッシュ関数を使ってみた

概要

パスワードのハッシュ
パスワードハッシュ API は crypt() を手軽に使えるようにしたラッパーで、 パスワードの作成や管理を安全な方法で行えます。

crypt() を利用すればパスワードハッシュの生成は可能なんだけど、経験上煩雑なコードになりやすい気がして(自分がPHPを書くのが下手なだけかもしれないけど)ちょっと勉強してみた。

環境構築

vagrantUbuntu(raring)を用意し、php5.5.7をLaunchpadのPersonal Package Archiveでインストール。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 13.04
Release:    13.04
Codename:   raring

$ php -v
PHP 5.5.7-1+sury.org~raring+1 (cli) (built: Dec 12 2013 21:50:03) 

API一覧

まずは一覧から。4つのメソッドが公開されているので順に説明していく。

  • password_hash : パスワードハッシュを作る
  • password_get_info : 指定したハッシュに関する情報を返す
  • password_verify : パスワードがハッシュにマッチするかどうかを調べる
  • password_needs_rehash : 指定したハッシュがオプションにマッチするかどうかを調べる

password_hash

まずは hash 生成関数、最初にしてメイン。

string password_hash ( string $password , integer $algo [, array $options ] )

第1引数 $password は生パスワード、第2引数 $algo はハッシュアルゴリズム定数、第3引数は $options アルゴリズムがサポートするオプションを入力する。PHP5.5では PASSWORD_DEFALUT と PASSWORD_BCRYPT が同じ値を指しており、共にBCRYPTが利用される模様。本当かな?と思って調べてみる。

# PASSWORD_DEFALUT と PASSWORD_BCRYPTの定数を覗いてみる
<?php
  echo "[constants]" . PHP_EOL;
  echo " PASSWORD_BCYRPT: "  . PASSWORD_BCRYPT  . PHP_EOL;
  echo " PASSWORD_DEFAULT: " . PASSWORD_DEFAULT . PHP_EOL;
# 実行結果
[constants]
 PASSWORD_BCYRPT: 1
 PASSWORD_DEFAULT: 1

ということで、2つのアルゴリズムを利用して(同じだけど)生のパスワードのハッシュを作ってみる。

<?php
    $raw_passwd = '3kanmarusan_Passw0rd';

    echo 'PASSWORD_DEFAULT' . PHP_EOL;
    $hashed_passwd = password_hash($raw_passwd,
                                   PASSWORD_DEFAULT);
    echo ' hash:' . $hashed_passwd . PHP_EOL;

    echo 'PASSWORD_BCRYPT(cost12)' . PHP_EOL;
    $hashed_passwd = password_hash($raw_passwd,
                                   PASSWORD_BCRYPT,
                                   ['cost' => 12]); // コストも変えてみる
    echo ' hash:' . $hashed_passwd . PHP_EOL;
# 実行結果
PASSWORD_DEFAULT
 hash:$2y$10$fAO/bg1Ti9.yBM3wC3FyJeIfrIql9dVFx/dhTDZO.FjSSjylRRCLK
PASSWORD_BCRYPT(cost12)
 hash:$2y$12$OciCNOfWegjXIgyNIoO/ZOPLt6wPLWcnYTYFwmAGoN6.2dG/4itZi

$2yで始まっているので、共にBCRYPTっぽい。その次の$10とか$12がコストを表すので PASSWORD_DEFAULT はcost10のBCRYPTということになる。

ちなみに、パスワードハッシュ生成方法そのものは、phpソースコードgithub.com からダウンロードして確認することができる。300MB超えのためワイヤレス環境だと厳しい。

git clone https://github.com/php/php-src.git
cd git-src
git checkout php-5.5.7
cd ext/standard
vim password.c

少々ソースを読んでみると、デフォルトのアルゴリズムとなる PASSWORD_BCRYPT は、

  • ハッシュ文字列の最初の7バイトでアルゴリズムとコストを表す
  • /dev/urandom を利用して乱数ビット列を作り、Base64 に変換した最初の22バイトをソルトデータとする。
  • BLOWFISH アルゴリズムを利用して、ソルト+ストレッチング内蔵の60バイトのパスワードハッシュを生成。
  • crypt内部の挙動としては、実際は最初の7バイトとソルトデータの22バイトを合わせた29文字をソルトとしてハッシュ計算する模様(この辺りよくわかっていない)

というロジックになっているようだ(ざっくり)。

PASSWORD_DEFAULT と PASSWORD_BCRYPT の使い分け

password_hash には下記のことが書かれている。

  • PASSWORD_DEFAULT - bcrypt アルゴリズムを使います (PHP 5.5.0 の時点でのデフォルトです)。 新しくてより強力なアルゴリズムPHP に追加されれば、 この定数もそれにあわせて変わっていきます。 そのため、これを指定したときの結果の長さは、変わる可能性があります。 したがって、結果をデータベースに格納するときにはカラム幅を 60 文字以上にできるようなカラムを使うことをお勧めします (255 文字くらいが適切でしょう)
  • PASSWORD_BCRYPT - CRYPT_BLOWFISH アルゴリズムを使ってハッシュを作ります。これは標準の crypt() 互換のハッシュで、識別子 "$2y$" を使った場合の結果を作ります。 その結果は、常に 60 文字の文字列になります。失敗した場合に FALSE を返します。

こう書かれると、メンテナンスコストも考えると PASSWORD_DEFAULT を選ぶのが怖い気もするけれども、マニュアルの下の方にアルゴリズムの更新/追加/デフォルト化のリリースルールが書かれている。

注意: この関数がサポートするアルゴリズムの更新 (あるいはデフォルトのアルゴリズムの変更) は、必ず次の手順にのっとって行われます。

  • 新しく追加されたアルゴリズムがデフォルトになるまでには、 少なくとも一度は PHP のフルリリースを経ること。 つまり、たとえば、新しいアルゴリズムが 5.5.5 で追加されたとすると、 そのアルゴリズムがデフォルトになるのは早くても 5.7 以降ということになります (5.6 は、最初のフルリリースだからです)。 しかし、もし別のアルゴリズムが 5.6.0 で追加されたとすると、 そのアルゴリズムも 5.7.0 の時点でデフォルトになる資格を得ます。
  • デフォルトを変更できるのはフルリリース (5.6.0 や 6.0.0 など) のときだけで、リビジョンリリースでは変更できない。 唯一の例外は、現在のデフォルトにセキュリティ上の致命的な欠陥が発覚した場合の緊急リリースです。

先週(2014/1/23)、PHP5.6.0alpha1がリリースされたけれども、

  • パスワードハッシュAPIには大きな変更が無さそうなのでデフォルトが変更されるのはもう少し時間がかかりそう
  • このまま行けばPHP5.6ではアルゴリズム変更は無いので、少なくとも次のフルリリースとなる(であろう)PHP5.7を経て、PHP5.8まで変更はないと期待できる
  • パスワードハッシュAPIアルゴリズム更新ルールが明確
  • (後述しますが)パスワードハッシュそのものにアルゴリズムやコスト、ソルトなどが含まれており、異なるハッシュアルゴリズムが混在していもパスワードの検証は出来る。

を考えると、PASSWORD_DEFAULT でもあまり問題にならないのではないかと思っている(個人の見解です)。

とはいえ、アルゴリズムはどんどん強固なものに更新していく必要もあり、それをサポートする後述の機能(password_needs_rehash)も提供されていることを考慮すれば、少なくともハッシュをデータベース等のストレージに格納する場合は、カラム長にゆとりを持たせておくことは大事ですね(玉虫色の回答)。

BCRYPTのハッシュコスト

PASSWORD_BCRYPT のハッシュコストのデフォルトは10となっているが、先ほど12の場合を例示した。もちろんコストが高いほど強固になるけれども、ハードウェアにそれ相応の負荷をかけることになる。php.netには、ハッシュ計算にかかる時間から逆算して適切なコストを推定するプログラムが例示されていたうのでそれを紹介する。

# password_hash() で、適切なコストを探す例
<?php
$timeTarget = 0.2; 

$cost = 9;
do {
    $cost++;
    $start = microtime(true);
    password_hash("test", PASSWORD_BCRYPT, ["cost" => $cost]);
    $end = microtime(true);
} while (($end - $start) < $timeTarget);

echo "Appropriate Cost Found: " . $cost . "\n";

1.7GHz Core i5/4GBのMBAubuntu-13.04 を仮想環境で動かした場合は、ハッシュ計算を0.2秒ぐらいにすると、適切なコストは 12 ということになった。もちろんこの辺りは環境によって調整すべき。

password_get_info

先ほどの説明の通り、password_hashは、アルゴリズムやコスト、ソルトといった情報もハッシュに含めて返す。password_get_infoは、password_hashで生成した有効なハッシュを第1引数$hashで渡すと、ハッシュに関する情報の配列を返すメソッド。

array password_get_info ( string $hash )

先ほどBCRYPTのcost12で生成したハッシュ $2y$12$OciCNOfWegjXIgyNIoO/ZOPLt6wPLWcnYTYFwmAGoN6.2dG/4itZi について確認してみる。

<?php
    # password_hash で生成した有効なハッシュ(BCRYPT/cost12)
    $hash = '$2y$12$OciCNOfWegjXIgyNIoO/ZOPLt6wPLWcnYTYFwmAGoN6.2dG/4itZi';
    $info = password_get_info($hash);
    print_r($info);
Array
(
    [algo] => 1
    [algoName] => bcrypt
    [options] => Array
        (
            [cost] => 12
        )
)

algo の値が、PASSWORD_BCRYPT(1) を指しており、options にコストが格納されていることが分かる。

password_verify

password_verifyは、指定したパスワードハッシュが生のパスワードとマッチするかどうか検証するメソッド。password_get_info でも説明した通り、パスワードハッシュの検証に必要なアルゴリズムやコスト、ソルトの情報もハッシュに含まれているので簡単に検証できる(他のデータソースから用意し・・・といった面倒なことがない)。

boolean password_verify ( string $password , string $hash )

第1引数 $password にユーザーから入力された生パスワードを、第2引数 $hash にストレージ等に格納しておいたパスワードハッシュを指定すると、パスワードハッシュ値アルゴリズムやコストにしたがってパスワードとそのハッシュが一致するかを検証する。

<?php
        $hash = '$2y$12$OciCNOfWegjXIgyNIoO/ZOPLt6wPLWcnYTYFwmAGoN6.2dG/4itZi';

        $correct_password = '3kanmarusan_Passw0rd'; // みかんまるさん
        var_dump(password_verify($correct_password, $hash));

        $incorrect_password = '2kanmarusan_Passw0rd'; // にかんまるさん?
        var_dump(password_verify($incorrect_password, $hash));
# 実行結果
bool(true)
bool(false)

password_hash で生成したパスワードハッシュにはアルゴリズムやコスト、ソルトがそれぞれ含まれているので、異なるハッシュアルゴリズムが混在しても問題なくパスワードの検証ができる。

password_needs_rehash

パスワードをハッシュで保存しているときに、パスワードハッシュの強度(アルゴリズムやコスト、ソルトやストレッチングの回数など)を変更したい場合がある。ハッシュで保存している=生パスワードを知り得ないので、ハッシュ強度の変更をオフラインで実行することはできない。

したがって、ユーザーの認証が成功した時に(ここで生のパスワードが分かるので)オンラインでハッシュの変更(rehash)をする必要が出てくる。これをうまく出来るようにするのが password_needs_rehash メソッド。

boolean password_needs_rehash ( string $hash , string $algo [, string $options ] )

第1引数$hashがチェック対象となるハッシュ。第2引数は$algoはハッシュアルゴリズムで、第3引数$optionsには、第2引数のハッシュアルゴリズムがサポートするオプションを指定する。第1引数の$hashアルゴリズムやオプションが第2引数以降とマッチしなかった場合は再ハッシュが必要ということで true が返却される。言葉にするとよく分からなくなるので書いてみる。同じ事を2度書くとかイケていないけれども、ここは分かりやすさ重視で書いてます。

<?php
    # BCRYPT, cost10(デフォルト)で生成したハッシュ
    $hash = '$2y$10$fAO/bg1Ti9.yBM3wC3FyJeIfrIql9dVFx/dhTDZO.FjSSjylRRCLK';

    if(password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12])) {
        echo "need rehash" . PHP_EOL;
    } else {
        echo "not need rehash" . PHP_EOL;
    }

    # BCRYPT, cost12で生成したハッシュ
    $hash = '$2y$12$OciCNOfWegjXIgyNIoO/ZOPLt6wPLWcnYTYFwmAGoN6.2dG/4itZi';

    if(password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12])) {
        echo "need rehash" . PHP_EOL;
    } else {
        echo "not need rehash" . PHP_EOL;
    }
# 実行結果
need rehash
not need rehash

上記の例の場合、はじめのハッシュはcost10のハッシュなのでrehashが必要と判定され、2番目のハッシュはcost12のハッシュなのでrehashは不要だと判定できている。

password_verify でも説明した通り、パスワードハッシュそのものにアルゴリズムやコスト、ソルトなどが含まれており、異なるハッシュアルゴリズムが混在していもパスワードの検証は出来るが、パスワードの強度は常に最新にしていく必要がある。下記の疑似コードで実現できると思う(試してない)。

<?php
    $raw_passwd = '(なにか)'; // ユーザーの入力
    $hash = '(なにか)'; // DBなどから持ってくる

    // まず認証しようず!
    if(password_verify($raw_passwd, $hash)) {
        // パスワード認証成功
        if(password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12])) {
            // rehashが必要
            $new_hash = password_hash($raw_passwd,
                                      PASSWORD_BCRYPT,
                                      ['cost' => 12]);

            // (新しいハッシュをDBに格納)
        }
    } else {
        // パスワード認証失敗
    }

まとめ

  • PHP5.5から採用されたパスワードハッシュAPIは便利
  • ソルト+ストレッチングを内蔵したパスワードハッシュが作れて再ハッシュも簡単

vagrant + fabric + cuisineで開発環境を自動生成する

はじめに

9/28のDevOps Days Tokyo 2013に参加してきた。

Vagrantをつかってみた というエントリだけでやりっ放しにするのはかっこよくないし、ブログを書くまでがDevOps Daysと幹事の方も言っていたので(意味が違う)、vagrant + fabric + cuisineを使って開発環境を自動生成してみる。

DevOps Days TokyoではChefの話が多かったのだけど、こんなエントリ書いておきながらRubyはまだしっくりきていないし、Pythonのシンプルさが好みなので、Chef-SoloっぽいことができるPythonのライブラリを探してみると fabric というものがあるらしい。
そして、Chefといえば冪等性(何回同じ操作をしても同じ結果になること)だけど、cuisine を組み合わせれば冪等性が少しは担保できるらしい。

目的

  • Mac Book Air上に Ubuntu-12.04 LTSの仮想環境が5分で導入できること
    • AWSでもUbuntuを使っているので将来見据えて
  • git clone + vagrant up の2コマンドで完了できること
  • ついでだから Apache + Python (WSGI) でサンプルが動いていると面白い

やってみた

下準備

まずは、Virtual Box/vagrantの確認をしておく。以前、vagrantはruby gemでインストールしていたけれども、いまは Vagrant Downloads からダウンロードすればいい、楽チン。

Virtual Box: 4.2.0
vagrant: 1.3.3

Ubuntu自体は用意されているboxをそのまま利用

$ vagrant box add ubuntu-12.04 http://files.vagrantup.com/precise64.box
$ vagrant box list
ubuntu-12.04

fabricとcuisineのインストール

pipが入ってなければ、 sudo easy_install pip でインストールする。
easy_installだとインストールパッケージ一覧を出すのがしんどいし、pipの方が今風だからこっちにする。

$ sudo pip install fabric
$ vagrant plugin install vagrant-fabric
$ sudo pip install cuisine

Vagrantfile の記載

srcとvagrantディレクトリを作り、vagrantディレクトリの中で vagrant init <box名> を実行する。実行するとVagrantfileができる。

$ mkdir src vagrant (srcは後述のsynced_folderのところで使い道がある)
$ cd vagrant
$ vagrant init ubuntu-12.04

以下のようにVagrantfileを書き換える

# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
    # box名
    config.vm.box = "ubuntu-12.04"
    # ローカル環境でIPが使えるようにする
    config.vm.network :private_network, ip: "192.168.33.10"
    # synced_folder (親ホストのVagrantfileから見た時の ../src を 仮想環境の /src に割り当てる
    config.vm.synced_folder "../src", "/src", create: true, owner: 'vagrant', group: 'vagrant', mount_options: ['dmode=777,fmode=666']

    # プロビジョニング(パッケージの追加や設定の反映)
    # provision.py に記載した setup メソッドを実行する
    config.vm.provision :fabric do |fabric|
        fabric.fabfile_path = "./provision.py"
        fabric.tasks = ["setup"]
    end
end
synced_folder

親ホストのディレクトリを仮想ホストのディレクトリを共有する仕組み。
開発は親ホスト上のお好きなエディタやIDEで実施し、仮想サーバで実行することができる。

  config.vm.synced_folder "../src", "/src", create: true, owner: 'vagrant', group: 'vagrant', mount_options: ['dmode=777,fmode=666']

例えば前述のVagrantfileに記載した内容の場合、下記のような共有ディレクトリが生成される。

  • 親ホストの ../src ディレクトリ(Vagrantfileからみて) と 仮想サーバの /src を共有する
  • 仮想サーバに /src が存在しなければ生成する(create: trueのところ)
  • ディレクトリオーナーとグループは vagrant にし、パーミッションは777、生成されるファイルのパーミッションは666になる。

ちなみにVagrantfileが設置されるディレクトリは自動的に synced_folder となり仮想サーバの方では /vagrant というディレクトリで参照できる。
なお、synced_folder については、下記記事が分かりやすかった。

プロビジョニングを記載する

Vagrantfileのコメントにも記載している通り、provision.py にプロビジョニングを記載する。
cuisine の *_ensure あたりを使うと冪等性が担保できるし、file I/Oをうまく使えばかなりの割合で冪等なプロビジョニングができると想像できる(sudoでコマンドを流すのは便利だが冪等性を担保しにくくなる。)

from fabric.api import run
from fabric.api import sudo
from fabric.utils import puts
from fabric.colors import red, green
from fabric.context_managers import *

import cuisine

cuisine.select_package("apt")

def setup():
        _setup_ubuntu()
        _setup_devtools()
        _setup_packages()
        _configuration_apache2()
        _restart_daemons()

# 初期設定系(なんとなく)
def _setup_ubuntu():
        puts(green('Setting Operation System'))
        sudo("cp /usr/share/zoneinfo/Japan /etc/localtime")
        sudo("apt-get update")

# 開発パッケージ系
# cuisine.package_ensure がいい感じ
def _setup_devtools():
        puts(green('Installing Devtools'))
        cuisine.package_ensure('vim')
        cuisine.package_ensure('python-setuptools')

# アプリケーション
def _setup_packages():
        puts(green('Installing Packages'))
        cuisine.package_ensure('libapache2-mod-wsgi')
        cuisine.package_ensure('apache2-mpm-worker')

# Apache2の設定
# 折角なので Apache設定ファイルを file_write で書き込み
# 設定ファイルの有効化とreloadをfabricのsudoで実行してみた
def _configuration_apache2():
        if not cuisine.file_exists('/etc/apache2/sites-enabled/hello'):
                cuisine.file_write(
                                location = '/etc/apache2/sites-available/hello',
                                content  = "WSGIScriptAlias /hello /src/hello.py"
                   "\n"
                   "<Directory /src>\n"
                   "  SetHandler wsgi-script\n"
                   "\n"
                   "  Order deny,allow\n"
                   "  Allow from all\n"
                   "</Directory>\n",
                                mode=None,
                                owner=None,
                                group=None,
                                sudo=True

                )
                sudo('a2ensite hello')
                sudo('/etc/init.d/apache2 reload')

# デーモンの再起動
def _restart_daemons():
        puts(green('Restarting Daemons'))
        cuisine.upstart_ensure('apache2')

../src ディレクトリには、Apache + mod_wsgiHello world が出力されるような hello.py を設置する。
Vagrantfileとprovision.py の設定により、http://192.168.33.10/hello で hello.py が実行される。

def application(environ, start_response):
    status = "200 OK"
    response_headers = [("Content-type", "text/plain")]
    start_response(status, response_headers)
    return ["Hello world! (mod_wsgi)"]

仮想サーバを起動

vagrant up を実行すれば、無事に仮想環境が立ち上がる。vagran ssh でログインすることができる。

$ vagant up
$ vagrant ssh
or
$ ssh vagrant@192.168.33.10 (pwはvagrant)

Vagrantの1.3.0からは、2回目以降の起動時にはプロビジョニングが実行されないので、2度目以降のupの場合は、下記を実行しなくてはならない。

$ vagant up --provision

親ホストの ../src ディレクトリで開発をすると、仮想サーバの /src 以下が自動的に変更されるので開発がらくちん。 ついでに../srcごとgithubにpushしちゃうとアプリケーション+仮想環境ごと複製できて飯がうまいうまい(ということで後述)

git(github)と連携する

githubに上げる時は、Vagrantfileと同じ場所に生成される .vagrant ディレクトリが追加されないように .gitignoreに記載することをお忘れなく(失敗すると面倒なことに)。

下記の通りに実行すると、仮想サーバを構築して Apache + PythonHello worldが!

$ git clone https://github.com/mikanmarusan/vagrant.git vagrant
$ cd vagrant/ubuntu/vagrant (ディレクトリ構成微妙)
$ vagrant up

まとめ

  • vagrantやっぱり便利
  • fabricとcuisineを使うとchef-soloっぽいのができる(けどシェルの高機能版って感じもする)
  • githubと連携できるとどこにいても同じ環境が作れる。

AWSでEC2を起動したらまずやること

東京リージョンなのにタイムゾーンがずれているので修正

$ date
Mon Aug 26 21:23:11 UTC 2013
$ sudo cp /usr/share/zoneinfo/Japan /etc/localtime 
$ date
Tue Aug 27 06:23:43 JST 2013

初代Let's Note CF-R1のHDDをSSDに換装する

http://ctlg.panasonic.jp/products/images/product/m/5/CF-R1MCAXR_5677.jpg

2003年2月発売の初代Let's Note CF-R1のHDDが廃れてきて(そりゃそうだ)、カラカラという音がひどくなってきていました。Windows XPのサポート期限の2014年4月までなんとか延命させたく、いっそのことSSDに換装をしてみたのでそのまとめです。

(せっかちさん向け)CF-R1のHDD→SSD換装のまとめ

  • まず起動が爆速、最新のMac Book Air並み
  • 体感的にはちょっと早くなったなー程度(CPUがMobile PentiumIII 866MHzと遅いからかな?)
  • CF-R1は元々ファンレスだったので、SSDにしたことで機械駆動部品が無くなり無音PC誕生、おめでとう
  • CF-R1のHDD換装と言えばピンの加工らしいが、億劫な自分はピン加工不要にしたいのでSSDを厳選した。
  • パフォーマンス
    • 左が換装前HDD、右が換装後のSSD。圧勝!

CF-R1のHDDは特殊HDD?

SSDに換装する前にもHDDを交換したいと思っていたのですが、どうも初代Let's Noteで採用されているHDDは省電力設計がされているため少々特殊なようで、先駆者達がかなり無茶されていました。

総じて、「IDE HDDの41ピンと44ピンを曲げる」のがコツらしく、さらにSSDの相性によっては認識されない場合があるので1ピンと42ピンを10KΩのカーボン抵抗でつなぐとかかなりHackされていて正直これはしんどいです。

トランセンドSSDをレッツノートで認識させる

CF-R1のHDDの仕様と対応方法のまとめ

  • 仕様
    • CF-R1のHDDは、東芝製の2.5インチ IDE HDD(自分の場合はMK4020GLSという型番でした)
    • 41ピンがロジック電源(3.3V)/42ピンが駆動用(5V)。※これが特殊
    • 市販のHDDは、41ピンがロジック電源(5V)/42ピンが駆動用(5V)なので、そのままだとロジック側電圧が足りなくて認識できないし、HDD内部で41ピンと42ピンが内部でショートしている場合はマザーボードがお亡くなりになる可能性がある
  • 対応方法
    • 41ピンと42ピンが内部でショートしている場合→41ピンは不要なので抜くか曲げて絶縁する(抜くと後で使い回せないので曲げる人が多い)
    • 41ピンと42ピンが内部でショートしていない場合→41ピンを42ピンとショートさせる
    • 44ピンは使わないので曲げておく

44ピンを曲げるのは、もともとIDEの44ピンはRESERVEDピンのため特段必要がなければ不要ということもあり、CF-R1のコネクタ側でマスクされているため(ピンが刺さらないようになっているので)、曲げておかないと刺さらないからです。ちなみにIDEのピンアサインは、通信用語の基礎知識 が分かりやすいです、これはブクマするべき。

以上の情報から、ピンの加工をせずにCF-R1に最適なSSD

  • 41ピンがハリボテ(ピンがない、もしくはあるけど絶縁されている)
  • 41ピンの内部と42ピンが内部でショートしていて、42ピンの5Vがロジック電源になるもの
  • 44ピンがない

となるのですが、探したらやっぱりあるんですね。
シー・エフ・デー販売 DRAM搭載のIDE接続SSD 64GB CSSD-PMM64WJ2

いざ換装

HDDの中身ををフリーソフトを利用してSSDに丸ごとコピーして(IDE-UDB変換器を利用)、あとはHDDとSSDを交換するだけです。


CF-R1を分解します。手順は、CF-R1 のディスクをSSDに換装する が参考になりました。


ディスクはウレタン?製のカバーに包まれているので取り外します(このカバーは後で使う)。HDDにDC+5V +3.3Vと書いてあるのが見えるでしょうか?


SSDに換装して、カバーを装着します(カバーがないとPCの中でSSDが動いてしまう)


SSDをHDDがあった場所に戻し、組み立て直して終了

まとめ

  • 初代Let's NoteのSSD換装が完了、パフォーマンス良好
  • ただしHDDの仕様が結構特殊なのでSSD選びが需要

JINS PCの件について考えてみた

おさらい

入力フォームに改ざん、外部に情報送信の可能性:JINSに不正アクセス、約1万2000件のクレジットカード情報流出の可能性 – @IT

JINS PCからの説明によると、流出の可能性があるのは、

  • カード番号
  • カード名義人名
  • セキュリティコード(カードの裏面などにある3〜4桁の数字)
  • カード有効期限

ネットだと、カード番号を暗号化せずに保存していたのでは?とか、セキュリティコードを保存していたのか!とかいろんな指摘を受けていたようだけれども、入力フォーム自体が改ざんされて、ユーザーが入力した情報が外部に送信されてたようだ。

JINSの不正アクセスによるカード情報流出、7件の不正利用を確認

おそらく、JINS PCの脆弱性を突いたXSSかなと推測してます。

問題はこれから? - チャージバックの発生

JINS PCの今回の件のように、流出したカード情報はどうなるのか?を考えてみる。

クレジットカードの対面取引では、署名やカードの暗証番号を使い、カードの所有者しか再現できないもしくは知り得ない情報を使う事で、カードの正規所有者であることを認証している。

一方、インターネット決済のような非対面取引では主に4つの項目(カード番号、カード名義人名、セキュリティコード、カード有効期限)が利用されているケースが多い。したがってクレジットカード情報が流出すると第三者利用(不正利用)されてしまう可能性がある。

実際にJINSの件でも、7件の不正利用が確認されているらしい(2013/03/18現在)

では、第三者による不正利用が起きるとどうなるのか?この場合はチャージバックが発生する。チャージバックとは、ユーザー(=カードホルダー)が不正利用等により利用代金の決済に同意しない場合に、クレジットカード会社が加盟店(お店)に対して支払いを拒絶するというもの

もちろん不正利用だけでなく、サービスそのものに不備があったりしてユーザーが支払いに同意せずにチャージバックになる(=お店側に不備がある場合)もあるけれども、不正利用のチャージバックはお店側にとってかなり痛い。

じゃ、どうすればいいの?

このままだとインターネットで商売したい加盟店はつらい。じゃどうしたらいいの?
例えば3Dセキュアによる認証をするという方法がとれる。

3Dセキュアとは、カード情報以外に各クレジットカード会社から発行されたインターネット専用のIDやパスワードを、ユーザーが入力しクレジットカード決済を行う仕組み。

下記のブランドあたりで提供されてたりします。

  • Visa: Visa認証サービス(三井住友VisaだとVPass)
  • Master: SecureCode
  • JCB: J/Secure

3Dセキュアを利用すれば、盗難されたり情報が流出したカードや偽造されたカードに対しても、本人しか知り得ないID/パスワードを入力するため不正利用の可能性を大きく減らすことができる。

とはいえ、3DセキュアのID/パスワードをユーザーが忘れてしまうと購入機会を損失してしまう可能性もあるし、そもそもブランドによっては対応していないところも多いので、日本ではあまり進んでいない(海外だと結構使われている)印象。

まとめ

  • JINSの件は、入力フォーム自体が改ざんされてユーザーの入力情報が外部に
  • 流出したカード情報は、非対面取引で不正利用される可能性が高い
  • 不正利用はチャージバックとなり加盟店(お店)は痛い
  • 3Dセキュアを利用すれば不正利用を減らすことができるかも、ただしデメリットもある。
    • もっと流行ればいいのに!

Vagrantを使ってみた

Vagrant

会社でMac Book Proがどうのこうので盛り上がっておりまして、Windowsでないと動かないアプリがあるとかないとかで盛り上がってます。ま、VirtualBoxとかで仮想化すればいいじゃんなんて思って調べてたら、Vagrant なんて凄いものが出てるんですね(びっくり)。

Vagrant

Vagrant を一言で言えば、Oracle VirtualBox を操作するコマンドラインツールってところでしょうか? rubyで記述されており、gemで簡単にインストールできたので紹介します。

VirtualBoxGUICUIで操作したいアレゲな人向けなツールです。

導入(for OS X Mountain Lion)

1) Virtual Boxをインストール

Virtual Boxは https://www.virtualbox.org/wiki/Downloads でダウンロードして通常通りインストールします。

2) Vagrantをインストールします。

Vagrantはrubyで書かれているのでgemでインストールできます。Gemfileに書いてbundlerでもいいけど。

$ gem install vagrant
$ vagrant -v
Vagrant version 1.0.7
3) ゲストOSの導入

ココに、vagrant用のOSイメージ(.boxファイル)一覧があるので選ぶ。
自分は、Scientific Linuxを入れてみました。

$ vagrant box add SFLinux http://lyte.id.au/vagrant/sl6-64-lyte.box
[vagrant] Downloading with Vagrant::Downloaders::HTTP...
[vagrant] Downloading box: http://lyte.id.au/vagrant/sl6-64-lyte.box
[vagrant] Extracting box...
[vagrant] Verifying box...
[vagrant] Cleaning up downloaded box…

にしてもSFLinuxっていう名前にしたのは失敗。

4) 初期化(init)して起動(up)
$ vagrant init SFLinux
(中略)
$ vagrant up
[default] VM already created. Booting if it's not already running...
[default] Resuming suspended VM...
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] VM booted and ready for use!

早すぎワロタ

5) ログイン

起動は vagrant ssh コマンドで行います。

$ vagrant ssh
Last login: Wed Sep 19 12:08:50 2012 from 10.0.2.2

[vagrant@localhost ~]$ uname -a
Linux localhost.localdomain 2.6.32-279.el6.x86_64 #1 SMP Thu Jun 21 07:08:44 CDT 2012 x86_64 x86_64 x86_64 GNU/Linux
[vagrant@localhost ~]$ cat /etc/redhat-release 
Scientific Linux release 6.3 (Carbon)

なにこれすごい、ここまで所要時間10分

(念のため)終了

終了時は別のターミナルから、haltコマンドを実行します。

$  vagrant halt
[default] Attempting graceful shutdown of VM...

すると、vagrant sshしたターミナルで仮想マシンがシャットダウンされます。

[vagrant@localhost ~]$ 
Broadcast message from vagrant@localhost.localdomain
	(unknown) at 0:07 ...

The system is going down for halt NOW!

[vagrant@localhost ~]$ 
[vagrant@localhost ~]$ Connection to 127.0.0.1 closed by remote host.
Connection to 127.0.0.1 closed.

まとめ

というかCUI厨として超魅力的

YConnect(Yahoo! JAPAN)への認証をする「omniauth-yahoojp」を作ってみた

omnauth-yahoojpとは

railsとか各種ID(facebook, twitter, mixi, Googleなどなど)の認証機能を追加してくれるomniauthのYConnect(Yahoo! JAPAN)版を突貫で作ってみました(テストとか適当)。Github で公開しています。

omniauth-yahoojp - Github

ベースはomnioauth-oauth2で、Strategyという形でYConnectの機能を追加し、YConnectを最低限に利用できるようにしています。

git clone してもらい、gem build & gem install すれば利用できますが、自分でもだんだん面倒くさくなってきたのもあって、勢いで rubygems にもあげてます。

omniauth-yahoojp(0.1.0) - rubygems.org

gem install omniauth-yahoojp

ominiauth-yahoojpは、YConnect(OAuth2.0 + OpenID Connect)で認証・認可をした後に、UserInfoAPIにアクセスして、(なけなしの)ユーザー属性情報を取得する処理を簡単にします(これしかできないという)

事の発端

Yahoo! JAPANで開催されたOpen Hack Day Japan(2013/2/16〜2/18)の前哨戦、Open Hack Day Conference(2013/2/9)でYahoo! JAPANの代表としてYConnectの話をしました。

会場にて @ 氏から、Rubyのライブラリないの?なんて話になり、おまけにtwitterでは下記のようにいつのまにかレースになっており(出来レース感もあるけど)とりあえず作ってみました(githubで公開中)

使い方

sinatraとかもありますが(よくわからないので)今回は、Ruby on Railsでの使い方を例示します。

1. Gemfileに追記する
gem 'omniauth-yahoojp'
2. Gemfileに基づいて必要なgemをインストールする
bundle install
3. config/initializers/omniauth.rb

config/initializers/omniauth.rbに以下を記載します。これは他のStrategy(facebook, twitter, mixi, Googleなどなど)でも同じ書き方です。

Rails.application.config.middleware.use OmniAuth::Builder do
        provider :yahoojp, ENV['YAHOOJP_KEY'], ENV['YAHOOJP_SECRET'], 
        {
                :scope => 'openid profile email address'
        }
end

client_idやclient_secretを直接記載しても良いですが、エレガントさに欠けるのでここは環境変数からとるようにします。

第4パラメータは Authorization エンドポイントで利用するパラメータです。UserInfoAPI で取得したい属性の scope を指定するほか、ログイン画面や同意画面の挙動を変更する :display, :prompt も指定できます。

4. config/routes.rb

Railsの中からYConnectで認証をする場合は、通常YConnectのAuthorization Codeエンドポイントにリダイレクトさせるのですが、omniauth-yahoojpではomniauth-yahoojpが提供している /auth/yahoojp に下記のような方法で飛ばします。

viewだとこんな感じ

<%= link_to "Yahoo! JAPAN でlogin", "/auth/yahoojp" %>

controllerだとこんな感じ

redirect_to /auth/yahoojp

YConnectから戻ってくる時は、 /auth/yahoojp/callback に自動的に戻ってくるようになっているので、これをコントローラにマッピングするために config/routes.rb に以下を記載します。

match '/top' => 'sessions#top' # toppage
match '/auth/:provider/callback' => 'sessions#callback' #戻り先

これで SessionController クラスの callback メソッドが呼ばれるようになります。/top はなんとなくスタートページ(YConnectへのキック元)としました。

5. SessionControllerを記述する
rails g controller sessions

で SessionControllerクラス を作り、下記を記載します。

class SessionsController < ApplicationController
        def top
                render 'sessions/top'
        end

        def callback
                auth = request.env['omniauth.auth']

                @user_id = auth.uid

                @name  = auth.info.name
                @email = auth.info.email
                @first_name = auth.info.first_name
                @last_name  = auth.info.last_name

                @token         = auth.credentials.token;
                @refresh_token = auth.credentials.refresh_token;
                @expires_at    = auth.credentials.expires_at;

                render 'sessions/callback'
        end
end

認証結果は、 request.env['omniauth.auth'] の中に入ってきます。uid()でuser_idを、info()でUserInfoAPIで得られる属性が取得できます。

6. viewを記載

SessionsControllerにおいてrenderで指定したHTMLファイル(erb)を設置します。

app/views/sessions/top.html.erb

<h1>Hello! YConnect (Authorization Code Grant Flow)</h1>
<div id="user_nav">
<%= link_to "Yahoo! JAPAN でlogin", "/auth/yahoojp" %>
</div>

app/views/sessions/callback.html.erb

<div id="user">
  <p>Hello, <%= @name %> さん</p>
  <div id="credentials">
 Credentials:
 <ul>
   <li>access_token: <%= @token %></li>
   <li>refresh_token: <%= @refresh_token %></li>
   <li>expires_at: <%= @expires_at %></li>
 </ul>
  </div>
  <div id="userinfo">
 UserInfo:
 <ul>
   <li>user_id:    <%= @user_id %></li>
   <li>email:      <%= @email %></li>
   <li>name:       <%= @name %></li>
   <li>first_name: <%= @first_name %></li>
   <li>last_name:  <%= @last_name %></li>
 </ul>
  </div>
</div>
7. やっと起動
rails server

思うこと

omniauth-oauth2は、内部的にrubygemのoauth2を使っているせいか、RFC6749RFC6750に完全に対応していないようです(コードのコメントではドラフト15になってますね)。

Authorization Code Grant Flow の token取得でBasic認証周りがつらい感じになっていたり、そもそもOpenID Connectに対応していないっぽいのでid_tokenが利用できない雰囲気に見えました。本当はできるのかな。。。

その他

rubyよりもpythonの方がかっこいいと思い(偏見)、rubyを全然やったこなかったので(言い訳)、全然なれていない感じで個人的にはだめだめな印象でございますので、なにかありましたらGitHubへpull requestください。

※あ、個人の創作物なので、所属組織とは関係ないよ!

ついでにとあるエンジニアのメモランダムというfacebookページもありますので、いいね!お願いします(宣伝)