暗号化する場合のmysqlのテーブル

アカウント管理の続き。passwordの保存については前回書いた。emailの暗号化だが、mysqlのAES_ENCRYPT()関数とAES_DECRYPT関数を使えば良いらしい。MySQLのDocumentにも今のところ一番安全と書かれている。

MySQLに暗号化したデータを保存するには、BLOB型にしなければいけないと。そんなわけで、MySQLのテーブルは、以下のようになる。テーブル名はaccountにしよう。

  • id int primary key auto_increment
  • account varchar(128)
  • p_hash varchar(256)
  • email TINYBLOB

p_hashはpasswordの保存のところで作った、salt付きパスワードのハッシュを入れる。暗号化したemailはTINYBLOB型のemailに入れる。sha1のhash256バイトもないだろ。とか色々ツッコミどころはあるが、その辺は適当。TINYBLOBは256バイト。e-mailアドレスでそんなに長い奴いないし。後で逆算してe-mailアドレスの文字数制限でリミットかけないといけない。その上のBLOB型になると、65536バイトなのででかすぎ。

<?php
class Account{
  const SALT='awSEdrFTgyHUjiKOlp';
  const CRYPT_KEY="AHAHAHA";
  function registration($account,$password,$email)
    // password hashを作る
    $pwd_hash = sha1(self::SALT.$password);

    // sql queryを作る
    $sql = "INSERT INTO account (account,phash,email)".
       " VALUES('$account','$pwd_hash',AES_ENCRYPT('$email','".self::CRYPT_KEY."'))";
    $db = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD);
    mysql_query($sql,$db);
    mysql_close($db);
  }
}
?>

んで、暗号化するまでは良いとして、じゃぁ、引っ張ってくるときどうするの?。って話だ。

  $sql="SELECT AEC_DECRYPT(email,'".self::CRYPT_KEY."') as email ".
          "FROM account WHERE account='$account'";

こんな感じで、復号化してあげる。

では、次にWHERE節で探す場合は?。

 $sql="SELECT * FROM account ".
     "WHERE email=AES_ENCRYPT('$email','".self::CRYPT_KEY."')";

暗号化したものを探すので、AES_ENCRYPT()で暗号化して、比較してあげれば良いのね。

じゃ、次に、Likeとかはどうすんだ?。

  $sql="SELECT * FROM account ".
    "WHERE AES_DECRYPT(email,'".self::CRYPT_KEY."')".
    " LIKE '%$keyword%'";

こう。今度は、復号化してからキーワードと比較しれあげれば良い。後は一緒でしょ。

mysql_fetch_xxとかは適当に。

ここで疑問、MySQLからデータが奪われてもe-mailは暗号化されているし、passwordも塩付きhashになってるから、まぁ大丈夫。でも・・。CRYPT_KEYがphpファイルに平文で書いてあるってどうなの?。phpのソースファイルは見られないって前提なのか?。と。そういわれりゃSSLの秘密鍵だってどっかのディレクトリの中にあるし、サーバーのフロントエンドがやられたら終わり。ということは、このCRYPT_KEYはSSLの秘密鍵のようにApacheの公開ディレクトリ以外のところに置いて、人から見られないようにしておくのがベターだよね。

passwordの保存

ちょっと、釣りのサイトをリアルタイム化したいので、アカウント管理を作りたい。条件は、以下の通り、

  1. アカウント名
  2. e-mail
  3. password

と、まぁ、普通のアカウント管理だわな。当然ながら、ユーザー別にidを作らなきゃいけないし、passwordもそのまま平文って分けにはいかない。e-mailも暗号化して保存したいよね、当然。そんなわけで、mysql,phpで作る。

んで、どうするのかと、アカウント名は面倒なのでアルファベットと_くらいでOK。ユニークじゃなきゃならないけど、その辺は一回チェック入れれば良いだけだから問題ない。そのアカウント名と関連付けるidはMySqlの自動インクリメントに任す。

パスワードの処理だが、普通に入力されたパスワードをhashにして保存しておけば良いのかと思っていた。

<?php
//登録
function registration($account , $password){
  $pwd_hash = sha1($password);
  save($account,$pwd_hash);
}
//login
function login($account,$password){
  $pwd_hash = load($account);
  if($pwd_hash == sha1($password)){
    return true; // OK
  }else{
    return false;//NG
  }
}
?>

まぁこんな感じ。ところが、このhashだとダメなんだと。レインボーテーブルってのが、ネットのどこかに有って、作られそうなhashを根こそぎテーブルにしてあるんだそうな。。なんてこったい。

で、解決策はsaltってのを使って塩加減を調整するそうな。ユーザーからの入力は単純な物が多いかもしれないけど、何文字かゴミを付けてからhash化すれば、レインボーテーブルって奴にも引っかかり難いと、そう言う事らしい。んで、さっきのを改造すると、

<?php
const SALT='awSEdrFTgyHUjiKOlp';
//登録
function registration($account,$password){
  $pwd_hash_s = sha1(SALT.$password);
  save($account,$pwd_hash_s);
}
//Login
function login($account,$password){
  $pwd_hash_s = load($account);
  if($pwd_hash_s == sha1(SALT.$password)){
    return true;  //OK
  }else{
    return false;  //NG
  }
}
?>

ま、こんな感じ。違いはSALTをくっつけてhashを作っているところ。linuxのパスワードもこんな感じでやっているらしく、今のところ問題は起きてなさそう。