PHP|MariaDB(MySQL)を使ったセッション管理(TLSなし・TLSあり)

今回の記事ではMariaDBをセッションデータストアとして使用する方法を学習していきます。本記事ではMariaDBサーバと別のMariaDBクライアントサーバの2台の設定が完了していることを前提に解説していきます。これらサーバのセットアップがまだの方は以下の記事を学習してからこちらに戻ってきてください。

あわせて読みたい
EC2インスタンスにMariaDBをインストールする MariaDBとはオープンソースでOracleのMySQLと高い互換性を持っているリレーショナルデータベース(RDMB)です。当記事では、AWSのEC2インスタンスにMariaDBをインストー...
あわせて読みたい
phpMyAdminのインストールと設定、そしてテーマを変えてみよう 今回の記事ではphpMyAdminをEC2インスタンスにインストールして別のインスタンスにインストールされたMariaDBにアクセスする手順を学習します。phpMyAdminはブラウザ上...
目次

MariaDBをPHPのセッションDBとして使う場合

PHPのセッションにMariaDBを使用する場合、2つ方法があります。

選択肢1)TLSを使用せずMariaDBを使用する。

TLSを使用せずMariaDBをPHPのセッションストアとして使用する場合は、安全な通信を行うために内部ネットワークのみでMariaDBサーバを使用する必要があります。MariaDB側でセキュリティーグループを使用してIPやネットワークの制限などをする必要があります。クライアントサーバ側のPHPではセッションハンドラーインターフェースを実装してそれをセッションハンドラーに渡すことでMariaDBをセッションストアとして使用できるようになります。

MariaDBのユーザを作成する際にはパスワードを設定していると思いますが、アクセスを許可するクライアントサーバは極力制限した方がよいでしょう。

選択肢2)MariaDBサーバとクライアントサーバ間で相互にTLSを使用する。

まずMariaDBサーバ側の設定ファイルにTLSに必要な証明書の設定をします。上記同様にクライアントサーバ側のPHPコードでセッションハンドラーインターフェースの実装します。ランタイム時にこの実装をセッションハンドラーに渡した上でTLSの証明書ファイルの設定をします。

次に、それぞれの設定方法を見ていきましょう。

TLS無しでMariaDBをセッションDBとして設定する

セッションテーブルの準備

phpMyAdminから以下のクエリでセッションテーブルを作成しておきましょう。

【データベースの作成】
CREATE DATABASE IF NOT EXISTS tutorial CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

【セッションテーブルの作成】
CREATE TABLE IF NOT EXISTS session (
    id VARCHAR(128) NOT NULL PRIMARY KEY,
    data BLOB NOT NULL,
    last_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci;

セッションデータを格納するフィールドは文字コードの問題が発生する可能性があるため、TEXT型よりBLOB型の方がおすすめです。

クライアントから非TLSでMariaDBサーバにアクセス

PHPからMariaDBに接続する場合はPHPのセッションハンドラーインターフェースの実装をする必要があります。既に第3者が作成したライブラリをComposerを使ってインストールする方法と自分で実装する方法があります。

Composerライブラリを使う

Composerで使用できるライブラリは自分で試してみてください。ここでは学習の為、外部ライブラリは使用せず、次項のセッションハンドラークラスの実装にフォーカスします。

セッションハンドラークラスの実装

sudo vi /var/www/index.php

<?php
error_reporting(E_ERROR);
ini_set('display_errors', 1);

// セッションハンドラーインターフェースの実装
class MySQLSessionHandler implements SessionHandlerInterface {
    private PDO $pdo;
    private int $ttl;

    public function __construct(PDO $pdo, int $ttl = 1440) {
        $this->pdo = $pdo;
        $this->ttl = $ttl;
    }

    public function open($savePath, $sessionName): bool {
        return true;
    }

    public function close(): bool {
        return true;
    }

    public function read($id): string {
        $stmt = $this->pdo->prepare("SELECT data FROM session WHERE id = :id AND expires_at > NOW()");
        $stmt->execute([':id' => $id]);
        return $stmt->fetchColumn() ?: '';
    }

    public function write($id, $data): bool {
        $expiresAt = date('Y-m-d H:i:s', time() + $this->ttl);
        $stmt = $this->pdo->prepare("
            INSERT INTO session (id, data, expires_at)
            VALUES (:id, :data, :expires_at)
            ON DUPLICATE KEY UPDATE data = :data, expires_at = :expires_at
        ");
        return $stmt->execute([
            ':id' => $id,
            ':data' => $data,
            ':expires_at' => $expiresAt
        ]);
    }

    public function destroy($id): bool {
        $stmt = $this->pdo->prepare("DELETE FROM session WHERE id = :id");
        return $stmt->execute([':id' => $id]);
    }

    public function gc($maxLifetime): int|false {
        $stmt = $this->pdo->prepare("DELETE FROM session WHERE expires_at < NOW()");
        $stmt->execute();
        return $stmt->rowCount();
    }
}

// MariaDBへの接続
$pdo = new PDO('mysql:host=MariaDBのサーバ又はIP;dbname=tutorial;charset=utf8mb4', 'poweruser', 'poweruser', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);

// ガーベッジコレクションの設定
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);

// 実装したセッションハンドラーを設定
$handler = new MySQLSessionHandler($pdo, 1800);
session_set_save_handler($handler, true);

// セッションの開始
session_start();

// セッションに値を保存
$_SESSION['user'] = 'MariaDB poweruser';
echo $_SESSION['user'];

ブラウザから上記ページにアクセスして、phpMyAdminのセッションテーブルにデータが作成されていれば成功です。

セッションのガーベッジコレクション(GC)

MariaDBサーバ側の設定

期限切れになったセッションデータの削除をする方法は2つあります。

PHPのセッション削除の以下の設定をコードに含める方法

下記の方法では100回に1回の確率で期限切れのセッションデータを削除します。

ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);

定期実行によりデータを削除する方法

次項のCronieのインストール後、/etc/crontabに以下の設定を追加して下さい。

* * * * * root mysql -u poweruser -ppoweruser -e "DELETE FROM session WHERE expires_at < NOW()" tutorial > /etc/crontab.log 2>&1

-pオプションとパスワードの間にスペースは入れないでください。

Cronieのインストール

新しいAmazon Linxu 2023のEC2インスタンスは、旧来のAmazon Linux 2のインスタンスと違って、定期実行プログラムのcrondパッケージはインストールされていません。代わりに Amazon Linxu 2023 のインスタンスではcronieというパッケージをインストールする必要があります。

【cronieがインストール済みかを確認】
sudo dnf list installed cronie

【cronieパッケージが存在するか確認】
sudo dnf info cronie

【cronieをインストール】
sudo dnf install cronie -y

【システムブート時にcoronieも起動するように設定】
sudo systemctl enable crond.service

【cronieプログラムを再起動】
sudo systemctl restart crond

【cronieプログラムの実行状態を確認】
sudo systemctl status crond | grep Active
sudo systemctl status crond.service
->上記共にActiveと表示されればOKです。

定期実行処理については「Linux|定期処理(Cronジョブ)実行の設定方法」の記事を参考にして下さい。

あわせて読みたい
Linux|定期処理(Cronジョブ)実行の設定方法 本日の記事は、Webシステムの処理の自動化に大いに役立つ定期処理実行(Cronジョブ)の設定方法について学習していきます。クロン(Cron)とは定期処理の実行を管理する...

TLSをオンにしてMariaDBをセッションDBとして設定する

MariaDBサーバ側の設定

プライベート認証局の作成

【作業ディレクトリを作成して移動】
sudo mkdir /usr/local/etc/openssl
cd /usr/local/etc/openssl

【認証局用のプライベートキーを作成】
sudo openssl genpkey -algorithm RSA -out ca.key -aes256 -pkeyopt rsa_keygen_bits:4096
> この認証局用のプライベートキーにパスワードを設定します。

【認証局用の証明書を作成】
sudo openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/C=US/CN=Root-CA"
> 認証局のキー使用時にはパスワードが聞かれます。上記で設定したパスワードを入力します。

MariaDBサーバ用の証明書を発行

前項で作成した認証局の証明書とプライベートキーを使用してMariaDBサーバ用の証明書を発行します。

【MariaDBサーバ用のプライベートキーを作成】
sudo openssl genpkey -algorithm RSA -out server-mariadb-rsa.key -pkeyopt rsa_keygen_bits:4096

【MariaDBサーバ用の証明書への署名リクエスト(CSR:Certificate Signing Request)】
sudo openssl req -new -key server-mariadb-rsa.key -out server-mariadb-rsa.csr -subj "/C=US/CN=MariaDBサーバ用ドメイン名"

【証明書の発行】
sudo openssl x509 -req -in server-mariadb-rsa.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server-mariadb-rsa.crt -days 365

次に、MariaDBクライアントサーバ用の証明書を発行します。

【MariaDBクライアントサーバ用のプライベートキーを作成】
sudo openssl genpkey -algorithm RSA -out client-mariadb-rsa.key -pkeyopt rsa_keygen_bits:4096

【MariaDBサーバ用の証明書への署名リクエスト】
sudo openssl req -new -key client-mariadb-rsa.key -out client-mariadb-rsa.csr -subj "/C=US/CN=MariaDBクライアントサーバ用ドメイン名"

【証明書の発行】
sudo openssl x509 -req -in client-mariadb-rsa.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client-mariadb-rsa.crt -days 365

最後にディレクトリとファイルのパーミッションの変更を行います。

sudo chown -R mysql:mysql /usr/local/etc/openssl

MariaDBの設定ファイルの変更

/etc/my.cnf

【以下を追記】
-
[mysqld]
ssl-ca=/usr/local/etc/openssl/ca.crt
ssl-cert=/usr/local/etc/openssl/server-mariadb-rsa.crt
ssl-key=/usr/local/etc/openssl/server-mariadb-rsa.key

# mTLSに必要
require_secure_transport = ON
-
【MariaDBを再起動】
sudo systemctl restart mariadb

MariaDBの再起動に失敗する場合は以下のログファイルを確認してください。
/var/log/mariadb/mariadb.log

MariaDBの接続ユーザの設定で証明書を必須にする

【MariaDBのプロンプトに入ります】
sudo mysql

【poweruserに証明書の設定を追加】
ALTER USER 'poweruser'@'%' REQUIRE SUBJECT '/C=US/CN=クライアントサーバ用ドメイン名' AND ISSUER '/C=US/CN=Root-CA';

REQUIREを解除する場合は以下を実行します。
ALTER USER ‘poweruser’@’%’ REQUIRE NONE;

クライアント側の設定

証明書のコピー

クライアントサーバ上にもMariaDBサーバ上に作成したのと同じ場所にTLS証明書用のディレクトリを作成します。上記MariaDBサーバ側で作成した、認証局の証明書、MariaDBクライアントサーバ用のプライベートキーと証明書の3ファイルをコピーして配置します。

【作業ディレクトリを作成して移動】
mkdir /usr/local/etc/openssl
cd /usr/local/etc/openssl

証明書ディレクトリのパーミッションを変更します。

sudo chown -R apache:apache /usr/local/etc/openssl

phpMyAdminの設定ファイルの変更

先にphpMyAdminのTLS接続設定をしておきます。

ホスト名の下に以下の部分を追加してください。そして再度 poweruser で phpMyAdmin にログインしてみましょう。

/var/www/html/phpMyAdmin/config.inc.php

$cfg['Servers'][$i]['host'] = 'MariaDBサーバのドメイン又はIP';
# 以下を追記
$cfg['Servers'][$i]['ssl'] = true;
$cfg['Servers'][$i]['ssl_key'] = '/usr/local/etc/openssl/client-mariadb-rsa.key';
$cfg['Servers'][$i]['ssl_cert'] = '/usr/local/etc/openssl/client-mariadb-rsa.crt';
$cfg['Servers'][$i]['ssl_ca'] = '/usr/local/etc/openssl/ca.crt';

phpMyAdminで以下を実行することでもTLS接続ができていることが確認できます。
【TLSのバージョン】
SHOW SESSION STATUS LIKE ‘Ssl_version’;
【TLSの暗号の種類】
SHOW STATUS LIKE ‘Ssl_cipher’;

index.phpの変更

次に、PHPのコードからMariaDBにTLS接続できるか確認していきます。

上記コードのPDOインスタンス化の箇所を以下の様に変更しましょう。

$pdo = new PDO(
    "mysql:host=MariaDBサーバドメイン名又はIP;dbname=tutorial;charset=utf8mb4",
    "poweruser",
    "poweruser",
    [
        PDO::MYSQL_ATTR_SSL_CA     => '/usr/local/etc/openssl/ca.crt',
        PDO::MYSQL_ATTR_SSL_CERT   => '/usr/local/etc/openssl/client-mariadb-rsa.crt',
        PDO::MYSQL_ATTR_SSL_KEY    => '/usr/local/etc/openssl/client-mariadb-rsa.key',
        PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
    ]
);

phpMyAdminからセッションデータが作成されるかを確認してみましょう。データが作成されていれば成功です。

以下のコードを実行することでもTLS接続ができていることが確認できます。

// TLSのバージョン
$stmt = $pdo->query(“SHOW STATUS LIKE ‘Ssl_version'”);
print_r($stmt->fetch());

// TLSの暗号の種類
$stmt = $pdo->query(“SHOW STATUS LIKE ‘Ssl_cipher'”);
print_r($stmt->fetch());

まとめ

今回の記事ではMariaDBをPHPのセッションストアとして使用する方法を学習しました。大きく分けてTLSを使用しない方法と使用する方法を学びました。まずは、セッションテーブルの準備をしましたね。そして、MariaDBをセッションストアとして使用する場合、PHPではセッションハンドラインターフェースの実装が必要でしたね。TLSの準備として認証局の作成、MariaDBサーバ用の証明書の作成、クライアントサーバ用の証明書の作成をしました。TLSを使用する場合、MariaDBの接続ユーザに証明書を必須とする設定もしましたよね。phpMyAdminとPHPのコードそれぞれでTLS接続を確認しました。これでMariaDBをセッションストアとして使用できるようになりましたね。

【関連記事】
PHP|Redisを使ったセッション管理(TLSなし・TLSあり)

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次