PHPでパスワードを生成

(1/1)
PHPでパスワードを生成
これから、短くても実用的なプログラムの作り方を通じて、PHPプログラミングの初歩を紹介していく。
まず最初に、パスワードを生成するプログラムをつくってみることにする。

(2024年2月10日)記事「変数」を追記

目次

サンプル・プログラムのダウンロード

圧縮ファイルの内容
password.phpサンプル・プログラム本体。
password.batWindows用バッチ・プログラム本体。
password.php 更新履歴
バージョン 更新日 内容
1.2.0 2023/11/05 パスワードに使う文字の並びを変数に分離
1.1.0 2023/01/06 関数名などを読みやすいものに改訂
1.0.0 2004/09/26 初版
まずは、上の「ダウンロード」をクリックしてPHPのソースプログラムをダウンロードしてほしい。
解凍できたら、password.php というファイル名で Apacheの仮想ディレクトリが通っているディレクトリにセーブする。
なお、これ以降の画面に表示している行番号は説明の便宜上のものであり、ソースプログラムには含んでいない。

   1: <?php
   2: /** password.php
   3:  * パスワードを生成するPHPプログラム
   4:  *
   5:  * @copyright   (c)studio pahoo
   6:  * @author      パパぱふぅ
   7:  * @動作環境    PHP 4/5/7/8
   8:  * @参考URL     https://www.pahoo.org/e-soul/webtech/php01/php04-01.shtm
   9: */
  10: /**
  11:  * マイクロ秒を使って乱数器に種まきする。
  12: */
  13: function sowSeeds() {
  14:     list($usec, $sec) = explode(' ', microtime());
  15:     mt_srand((float)$sec + ((float)$usec * 100000));
  16: }
  17: 
  18: /**
  19:  * パスワードを生成する。
  20:  * @param   int     $length      生成するパスワードの長さ
  21:  * @param   string  $characters  パスワードに使う文字の並び(半角文字)
  22:  * @return  string  パスワード
  23: */
  24: function getPassword($length, $characters) {
  25:     $num = strlen($characters- 1;
  26: 
  27:     //$charactersからランダムに1文字ずつ選んで$passwordに結合していく
  28:     $password = '';
  29:     for ($i = 0$i < $length$i++) {
  30:         $position = (int)mt_rand(0, $num);
  31:         $password = $password . substr($characters, $position, 1);  //1文字追加
  32:     }
  33:     return $password;
  34: }
  35: 
  36: //メインプログラム ========================================================
  37: //パスワードに使う文字の並び(変更可能)
  38: $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  39: 
  40: //長さ10文字、英大文字・数字混在のパスワードを生成、表示する.
  41: print getPassword(10, $characters);
  42: 
  43: /*
  44: ** バージョンアップ履歴 ===================================================
  45:  *
  46:  * @version 1.2.0 2023/11/05  パスワードに使う文字の並びを変数に分離
  47:  * @version 1.1.0 2023/01/06  関数名などを読みやすいものに改訂
  48:  * @version 1.0.0 2004/09/26  初版
  49: */
  50: ?>

サンプル・プログラムの実行方法

ブラウザから https://localhost/***/password.php (*** はセーブした仮想ディレクトリ名)と入力すると、数字+英大文字から成る10桁のパスワードが表示される。
これがPHPプログラムである。

PHPプログラムの始まりと終わり

PHPプログラムは <?php ... ?> の間に記述する。また、ファイルの拡張子は原則として .php を用いる。
他の記述法もあるが、基本は <?php ... ?> である。

コメント

ソース・プログラムには、あとで何を書いたか分からなくならないように、できるだけコメントを記しておこう。
PHPはC, C++と同様のコメント記法を採用している。
すなわち、
  • // から行末まではコメント(1行コメント)
  • /* ... */ の間はコメント(複数行コメント)
の2つである。password.php では、複数行コメントのみ用いている。意図的に@を使っているのだが、これについては次回述べることにする。

変数

  36: //メインプログラム ========================================================
  37: //パスワードに使う文字の並び(変更可能)
  38: $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  39: 
  40: //長さ10文字、英大文字・数字混在のパスワードを生成、表示する.
  41: print getPassword(10, $characters);
  42: 
  43: /*

PHPでは、ドル記号 $ に続く名前を変数として認識する。ここでは $characters が変数である。
変数名として使えるのは、文字またはアンダースコア _ からはじまり、任意の長さの文字、数字、アンダースコアが続くもので、長さに制約はない。文字として、英大文字・英小文字のほか、0x80~0xFFのコードで表される文字を利用することができる。
PHPはC言語などと異なり、変数を宣言することなく利用できる。また、変数にデータ型はなく、動的型付けである。

変数名がドル記号 $ で始まるのは他のプログラミング言語にないPHPの特徴だが、これは、初期のPHPでは機能拡張で関数が増えてきたことによると思われる。
もし変数名がドル記号 $ で始まる必要がなかったとする。
たとえば、"php.ini" の "extension=exif" を無効にした(コメントアウトした)状態で、"exif_read_data" を変数名として使うプログラムを作ったとする。このプログラムを "extension=exif" が有効な環境で動かそうとするとエラーになるだろう。なぜなら、"extension=exif" が有効な環境では、 exif_read_data  という名前の組み込み関数が存在するからだ。
ここでドル記号 $ で始まる "$exif_read_data" を変数名とすれば、いずれの環境でも正常に動作する。

変数名に 0x80~0xFFのコードが利用できることから、UTF-8 で書かれた日本語文字を変数名として使うことができる。
試しに日本語文字を使うのはいいが、常用することはお勧めできない。というのは、全角スペース " "(0xE38080)や全角セミコロン ";"(0xEFBC9B)も変数名として利用できてしまい、どこからどこまでが変数名なのか分かりにくく、不具合の原因となるからだ。

ユーザー定義関数

  18: /**
  19:  * パスワードを生成する。
  20:  * @param   int     $length      生成するパスワードの長さ
  21:  * @param   string  $characters  パスワードに使う文字の並び(半角文字)
  22:  * @return  string  パスワード
  23: */
  24: function getPassword($length, $characters) {
  25:     $num = strlen($characters- 1;
  26: 
  27:     //$charactersからランダムに1文字ずつ選んで$passwordに結合していく
  28:     $password = '';
  29:     for ($i = 0$i < $length$i++) {
  30:         $position = (int)mt_rand(0, $num);
  31:         $password = $password . substr($characters, $position, 1);  //1文字追加
  32:     }
  33:     return $password;
  34: }

PHPは、CやJavaなどと同様に、ユーザーが関数を定義することができる。関数とは、よく使う処理をまとめたもので、あらかじめPHP処理系に用意されている組み込み関数(後述)とユーザー定義関数がある。ユーザー関数の概念については、「4.2 ユーザー定義関数 - JavaScriptによるプログラミング入門」をご覧いただきたい。

ユーザー定義関数は、
function 関数名(引き数)  {
    処理1;
    処理2;
    ...;
   return 戻り値;
}

のように記述する。password.phpsowSeedsgetPassword がユーザー定義関数である。

関数名は、アンダーバーまたはアルファベットで始まる文字列で、2文字目以降は数字も混在してよい。英小文字・大文字は区別される。
引き数には、この関数内だけに通用する(スコープ)変数を指定する。
PHPの変数は必ず $ で始まる。次に、アンダーバーまたはアルファベットが続き、その次からは数字も混在してよい。英小文字・大文字は区別される。
引き数を指定しないときには何も書かない。Cのようなvoid指定は不要である。
戻り値は、その関数の計算結果/処理結果を値として返すもので、省略可能。省略したときは、BASICのサブルーチンのような働きをすると考えてもらえばよい。getPassword には戻り値があるが、sowSeeds にはない。

  15:     mt_srand((float)$sec + ((float)$usec * 100000));

PHPでは変数の型を意識する必要がなく、関数や引数に型を明示する必要はない。ただ、浮動小数演算しようとしたつもりが整数演算として解釈され、計算結果が間違う可能性がある。そこで sowSeeds では上記のように float と追記することで、変数が浮動小数であることをPHP処理系に指示している。

演算子

四則演算を行うのは、+ - * / で、他の言語と同じである。優先順位も同様。代入を行う = はとくに代入演算子と呼ばれる。文字列を結合する演算子は . (ピリオド)である。

PHPはデータ型の概念を持たないので、演算子によって結果が異なることがある。
たとえば、$a = 1, $b = 2のとき、$a + $b の結果は2であるが、 $a . $b の結果は11となる。数値として加算が行われたか、文字列として接続が行われたかの違いである。
そこで、計算結果を明らかにするために、明示的に型を指定することがある。これが「型指定演算子」で、int(整数)、float(単精度浮動小数)、double(倍精度浮動小数)、string(文字列)、array(配列)、object(オブジェクト)がある。

その他にも様々な演算子がある。
演算子や関数を用いた「式」はPHPプログラムの最小構成単位である。詳細はオンラインマニュアルを参照のこと。

組み込み関数

PHPは、非常に多くの組み込み関数を備えている。
画面表示やDBアクセスに始まって、数字に3桁区切りのカンマを付けたり、文字列からHTMLの外したりと、考えられるルーチンワークのほとんどは組み込み関数で用意されている。この点がPHPの大きな魅力である。
他方、これら組み込み関数はPHPのバージョンによって変化しており、参考書籍の情報は古いことが多い。そこで、最新のオンライン・マニュアル(日本語)を参考にしていただきたい。

password.php では、まず、12行目の関数  microtime  が組み込み関数である。これは、文字列 "msec sec" を返す。sec は現在時刻を Unix epoch (1970年1月1日 00:00:00)からの通算秒としたもので、msec は マイクロ秒の部分である。

関数  explode  は、指定した文字で文字列を分解し、各々を配列に入れて戻す組み込み関数だ。12行目では空白を区切り文字にして分解している。

関数  list  はPHPならではのユニークな組み込み関数で、関数  explode  のように戻り値が配列である場合、一気に複数の独立した変数に代入することができる。ここでは、変数 $usec にミリ秒が、変数 $sec に秒が代入される。
関数  mt_rand  は乱数の種(シード)を設定する関数である。乱数の乱れを向上させるため、後述の関数  mt_rand  を使う前に必ず呼び出すのが定石である。

文字列操作

22~31行目が、パスワードを生成するユーザー定義関数 make_password だ。引き数に、生成するパスワードの長さ $len と、パスワードに使用する文字種 $str を指定する。
実際に、この関数を呼び出すのは34行目で、10桁、英数大文字から成るパスワードを生成させている。

23行目の関数  strlen  は、引き数に与えられた文字列の長さを返す。ここでは、パスワードに使用する文字種の数から1を減じた値を変数 $l に代入している。

26行目の for 文は繰り返し処理を行う。
一般的に、for (式1; 式2; 式3) 文 の形で用いられ、初期値が (式1) で、(式2) が真である間、(文) を実行し、(式3) を実行する。
26行目では、変数 $i が0から始まり、$i$l 未満である間、27~28行目を実行、$i を1だけ増やす($i++)処理を行う。

27行目では、0以上 $l 以下の乱数を発生させ、$n に代入する。
28行目で、関数  substr  を使っているが、この関数は与えられた文字列から部分文字列を取り出すものである。ここでは、文字列 $str$n 番目から1文字だけ取り出して、変数 $ch に代入している。これは、すなわち、パスワードに使用する文字種から任意の文字を1文字だけ取り出すことを意味する。

そして、変数 $ch をパスワード文字列 $psw の後に結合させるという処理を、指定した長さ(回数)になるまで for 文によって繰り返し実行している。

プログラムの評価

こうしてできたプログラム password.php を実行し、パスワードの解き明かされにくさを評価するフリーソフト「Password Evaluation Soft」を実行したところ、ほぼ100%の割合で合格した。

ただし、ここで生成されたパスワードは、ネットワーク上を平文で流れるので、盗聴の可能性がある。それを防ぎたいのであれば、SSLなどの暗号化を別途用意する必要がある。なお、サンプル・プログラムを実行できる ぱふぅ家のホームページの通信はSSL暗号化されており、盗聴の可能性は低い。

補足:乱数

ここで、組み込みの乱数関数について補足しておく。

デジタル・コンピュータは、計算機としては同じ引き数を渡したら常に同じ戻り値を返すことができる。だが、このことから、原理的に一様乱数を発生することができない。
そこで、コンピュータでも一様乱数に近い疑似乱数を“計算”できるアルゴリズムが研究されており、なかでもサンプル・プログラムで使用した組み込み関数  mt_rand  が採用しているメルセンヌ・ツイスタは、少ない計算量で疑似乱数を発生することが証明されている。
メルセンヌ・ツイスタ (Mersenne twister、通称MT) は、1996年(平成8年)に、松本眞さんと西村拓士さんが発表したもので、その実装が修正BSDライセンスで公開されている。
なお、PHP 7.1.0未満ではメルセンヌ・ツイスタの実装に不具合があった。

それまでの疑似乱数は、いつかは繰り返しが起き、大量の乱数を使う大規模な数値計算では精度が悪くなることがあった。これを乱数の周期問題という。メルセンヌ・ツイスタ乱数は、その名の由来となった2486万2048桁に及ぶメルセンヌ素数を基に、きわめて長い周期であることが証明されている。
さらに、松本さんによれば「『1+1=0』という見たことがない異世界の数学」を使うことで、「異世界ででたらめな乱数ができる」のだが、こちらの世界に戻ってきた際に、もっとでたらめな乱数になっていてもおかしくないという考え方で作られている。
少女漫画家の奥さまを持ち(2020年史別)、野崎昭弘さんの名著『πの話』を呼んで育ったという松本さんは、もしかすると、私たちオタク寄りの数学者なのかもしれない。

PHPの  mt_rand  関数を使用するにあたり、事前に組み込み関数  mt_srand  を呼び出す必要はないのですが、ここでは周期性のリスクを低減するため、念のために呼び出している。

Windows:パスワードを生成しクリップボードに格納

   1: rem パスワードを生成し,クリップボードに格納する.
   2: @echo off
   3: php -f password.php | clip

"password.bat" は、サンプル・プログラムを使ってパスワードを生成し、それをクリップボードに格納するWindowsのバッチ・プログラムである。新しいログイン・パスワードを作りたいときに参考にしてほしい。

参考サイト

(この項おわり)
header