cloudlog/application/migrations/275_encrypt_eqsl_passwords.php
Peter Goodhall 1f7c130c46 Encrypt stored eQSL passwords at rest
Adds migration 275 to convert eQSL password columns to TEXT and encrypt existing user/mapping passwords with an `enc:` prefix. Updates eQSL controller/model flows to decrypt on read, encrypt on create/update, and keep plaintext backward compatibility for legacy rows. User profile updates now avoid reusing stored ciphertext when no new password is submitted, and mapping updates report a clear error if secure password storage fails.
2026-07-18 22:53:51 +01:00

76 lines
2.5 KiB
PHP

<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_encrypt_eqsl_passwords extends CI_Migration
{
public function up()
{
$this->load->library('encryption');
$this->db->db_debug = false;
// Encrypted values are larger than legacy varchar(64).
$this->db->query('ALTER TABLE users MODIFY COLUMN user_eqsl_password TEXT DEFAULT NULL');
if ($this->db->table_exists('eqsl_mappings')) {
$this->db->query('ALTER TABLE eqsl_mappings MODIFY COLUMN eqsl_password TEXT NOT NULL');
}
$this->encrypt_existing_user_passwords();
$this->encrypt_existing_mapping_passwords();
$this->db->db_debug = true;
}
private function encrypt_existing_user_passwords()
{
$query = $this->db->query('SELECT user_id, user_eqsl_password FROM users WHERE COALESCE(user_eqsl_password, "") <> ""');
foreach ($query->result() as $row) {
$stored = (string) $row->user_eqsl_password;
if (strpos($stored, 'enc:') === 0) {
continue;
}
$encrypted = $this->encryption->encrypt($stored);
if ($encrypted === false || $encrypted === null) {
continue;
}
$this->db->where('user_id', (int) $row->user_id);
$this->db->update('users', array('user_eqsl_password' => 'enc:' . $encrypted));
}
}
private function encrypt_existing_mapping_passwords()
{
if (!$this->db->table_exists('eqsl_mappings')) {
return;
}
$query = $this->db->query('SELECT mapping_id, eqsl_password FROM eqsl_mappings WHERE COALESCE(eqsl_password, "") <> ""');
foreach ($query->result() as $row) {
$stored = (string) $row->eqsl_password;
if (strpos($stored, 'enc:') === 0) {
continue;
}
$encrypted = $this->encryption->encrypt($stored);
if ($encrypted === false || $encrypted === null) {
continue;
}
$this->db->where('mapping_id', (int) $row->mapping_id);
$this->db->update('eqsl_mappings', array('eqsl_password' => 'enc:' . $encrypted));
}
}
public function down()
{
$this->db->db_debug = false;
// Keep TEXT sizes on rollback to avoid accidental truncation.
// Password values remain encrypted and backward-compatible with runtime decrypt helpers.
$this->db->db_debug = true;
}
}