Организация удаленного доступа к MySQL из PHP

Раздел: Программирование на PHP -> Базы данных

Рекомендуемое решение: PDO с настройками безопасности

Цель: обеспечить гибкое и безопасное удаленное подключение к MySQL из PHP.

Библиотека PDO (PHP Data Objects) предоставляет единый интерфейс для работы с базами данных и поддерживает множество драйверов. Для MySQL рекомендуется использовать драйвер mysqlnd, входящий в состав PHP. Основное преимущество PDO перед mysqli в случае удаленного подключения – встроенная поддержка SSL/TLS шифрования, подготовленных запросов и гибкая настройка обработки ошибок.

Пример подключения с использованием SSL:

$host = 'db.example.com';
$dbname = 'app_db';
$user = 'remote_user';
$pass = 'secure_pass';

$dsn = "mysql:host=$host;dbname=$dbname;charset=utf8mb4";
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::MYSQL_ATTR_SSL_CA       => __DIR__ . '/ssl/ca.pem',
    PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
];

try {
    $pdo = new PDO($dsn, $user, $pass, $options);
    echo "Соединение установлено";
} catch (PDOException $e) {
    error_log('Ошибка подключения: ' . $e->getMessage());
    die('Не удалось подключиться к базе данных.');
}

Function mysql php (функции mysql в php)

Пояснение шагов:

  • Формируется DSN строка с указанием хоста, имени базы и кодировки.
  • В массиве опций включается режим исключений для удобной обработки ошибок.
  • Указываются пути к файлу CA-сертификата для верификации сервера.
  • При ошибке происходит перехват исключения, логирование и завершение скрипта.

Возможные проблемы и их решения:

Ошибка "PDO::MYSQL_ATTR_SSL_CA не определена" – возникает, если PHP собран без поддержки MySQL SSL. Решение: перекомпилировать PHP с --with-pdo-mysql=mysqlnd или использовать библиотеку MySQL Native Driver.

Ошибка "SSL operation failed with code 1" – неверный путь к сертификату или сам сертификат не соответствует серверу. Решение: проверить расположение ca.pem, а также настройки MySQL (require_secure_transport).

Ошибка "Access denied for user" – у пользователя нет прав на удаленное подключение. Решение: выполнить на сервере MySQL GRANT ALL PRIVILEGES ON app_db.* TO 'remote_user'@'%' IDENTIFIED BY 'secure_pass'; FLUSH PRIVILEGES; или указать конкретный IP-адрес вместо '%'.

Вариант 1: Прямое подключение через mysqli без шифрования

Вопрос: "Как осуществить простое удаленное соединение к MySQL, если не требуется шифрование?"

$mysqli = new mysqli('192.168.1.100', 'web_user', 'password', 'site_db', 3306);
if ($mysqli->connect_errno) {
    echo "Ошибка соединения: " . $mysqli->connect_error;
} else {
    echo "Успех";
    $mysqli->close();
}

Php api mysql (api с mysql в php)

Пояснение: конструктор mysqli принимает хост, пользователя, пароль, имя БД и порт. При ошибке свойство connect_errno > 0. Вариант удобен для внутренних сетей, где трафик защищён на уровне сети.

Частые ошибки:

"Host '...' is not allowed to connect" – пользователю не разрешён удалённый доступ. Решение: выдать грант для '%' или конкретного IP.

"Can't connect to MySQL server on ... (111)" – MySQL не принимает внешние соединения. Решение: в my.cnf установить bind-address = 0.0.0.0 и перезапустить службу MySQL. Проверить, не блокирует ли фаервол порт 3306.

"Connection timed out" – сетевые проблемы или хост недоступен. Решение: проверить DNS, ping, маршрутизацию.

Вариант 2: Подключение через SSH-туннель

Вопрос: "Как обеспечить безопасное удалённое подключение к MySQL, если прямое соединение небезопасно или заблокировано?"

SSH-туннель перенаправляет локальный порт на удалённый сервер MySQL через зашифрованный SSH-канал. Сначала настраивается туннель:

ssh -L 3307:127.0.0.1:3306 user@remote-ssh-host -N

Table entry php (php: вставка записи в таблицу)

Теперь в PHP подключаемся к localhost:3307:

$mysqli = new mysqli('127.0.0.1', 'mysql_user', 'pass', 'dbname', 3307);

удаленная php mysql (удаленное подключение к mysql в php)

Трафик между PHP-скриптом и MySQL шифруется SSH. Нет необходимости в SSL-сертификатах. Вариант подходит для продакшн-сред, где требуется дополнительная безопасность.

Проблемы SSH-туннеля:

Туннель может разорваться. Решение: использовать опцию ServerAliveInterval, или автоматическое переподключение через autossh.

Необходимость поддерживать SSH-соединение постоянно. Решение: запустить туннель в фоне (ssh -f) или использовать systemd unit.

При использовании публичных ключей убедиться, что на сервере разрешена аутентификация по ключу.

Вариант 3: Постоянные соединения (persistent connections)

Вопрос: "Как уменьшить накладные расходы на установку соединения при множественных запросах?"

PHP поддерживает постоянные соединения через mysqli_pconnect или опцию PDO::ATTR_PERSISTENT => true. При повторном запросе к тому же хосту и пользователю PHP может использовать ранее открытое соединение.

$pdo = new PDO('mysql:host=db.example.com;dbname=test', 'user', 'pass', [
    PDO::ATTR_PERSISTENT => true,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

Пояснение: постоянные соединения полезны для скриптов с высоким трафиком, но могут вызывать блокировки, если не закрыты корректно.

Ограничения:

Не работают с SSL некоторыми конфигурациями (зависит от драйвера).

Могут привести к исчерпанию пула соединений MySQL (max_connections). Решение: использовать connection pool на стороне сервера (например, MySQL Router).

- Page php bd (база данных страницы на php)
- подключения базы данных php (подключение к базе данных php)

Дополнительные примеры удаленного подключения

Класс для управления соединением с автоматическим переподключением

Пример
<?php
class RemoteDB {
    private $pdo;
    private $config;

    public function __construct(array $config) {
        $this->config = $config;
        $this->connect();
    }

    private function connect() {
        $dsn = sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4',
            $this->config['host'],
            $this->config['dbname']
        );
        $options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::MYSQL_ATTR_SSL_CA => $this->config['ssl_ca'] ?? null,
        ];
        try {
            $this->pdo = new PDO($dsn, $this->config['user'], $this->config['pass'], $options);
        } catch (PDOException $e) {
            error_log('RemoteDB connect error: ' . $e->getMessage());
            throw $e;
        }
    }

    public function query(string $sql, array $params = []) {
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            return $stmt->fetchAll();
        } catch (PDOException $e) {
            $this->connect();
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            return $stmt->fetchAll();
        }
    }
}

$config = [
    'host' => 'db.example.com',
    'dbname' => 'app',
    'user' => 'user',
    'pass' => 'secret',
    'ssl_ca' => '/etc/ssl/certs/ca-certificates.crt',
];
$db = new RemoteDB($config);
$users = $db->query('SELECT * FROM users WHERE active = ?', [1]);
print_r($users);
Array ( [0] => Array ( [id] => 1 [name] => Иван [active] => 1 ) ... )

Класс RemoteDB инкапсулирует создание соединения, обработку ошибок, логгирование. Метод query при первой ошибке пытается переподключиться. Это повышает устойчивость к временным сбоям сети.

Автоматизация SSH-туннеля из PHP через proc_open

Пример
<?php
function sshTunnel(string $remoteHost, int $remotePort, string $sshUser, string $sshHost, int $localPort = 3306): resource {
    $cmd = sprintf('ssh -o StrictHostKeyChecking=no -o ServerAliveInterval=30 -L %d:127.0.0.1:%d %s@%s -N',
        $localPort, $remotePort, escapeshellarg($sshUser), escapeshellarg($sshHost)
    );
    $descriptors = [
        0 => ['pipe', 'r'],
        1 => ['pipe', 'w'],
        2 => ['pipe', 'w'],
    ];
    $process = proc_open($cmd, $descriptors, $pipes);
    if (!is_resource($process)) {
        throw new RuntimeException('Не удалось запустить SSH туннель');
    }
    fclose($pipes[0]);
    $err = stream_get_contents($pipes[2]);
    fclose($pipes[2]);
    sleep(1);
    $status = proc_get_status($process);
    if (!$status['running']) {
        proc_close($process);
        throw new RuntimeException('SSH туннель не стартовал: ' . $err);
    }
    return $process;
}

$tunnelProcess = sshTunnel('127.0.0.1', 3306, 'user', 'gateway.example.com', 3307);
$pdo = new PDO('mysql:host=127.0.0.1;port=3307;dbname=test', 'mysql_user', 'mysql_pass');
// работа с БД
proc_terminate($tunnelProcess);
proc_close($tunnelProcess);
 SSH-туннель создан, PHP подключается к localhost:3307. Вывод PDO-запроса (например, список таблиц).

Функция запускает SSH-процесс, проверяет его жизнеспособность, возвращает дескриптор для последующего завершения. Рекомендуется использовать только в контролируемой среде (например, разовое задание).

Настройка MySQL для обязательного SSL и подключение через PDO

Пример
# На сервере MySQL:
# В my.cnf:
[mysqld]
ssl-ca = /etc/mysql/ssl/ca.pem
ssl-cert = /etc/mysql/ssl/server-cert.pem
ssl-key = /etc/mysql/ssl/server-key.pem
require_secure_transport = ON

# SQL для создания пользователя с требованием SSL:
CREATE USER 'ssl_user'@'%' IDENTIFIED BY 'password' REQUIRE SSL;
GRANT ALL ON *.* TO 'ssl_user'@'%';
FLUSH PRIVILEGES;

# PHP код подключения:
$pdo = new PDO('mysql:host=db.example.com;dbname=test', 'ssl_user', 'password', [
    PDO::MYSQL_ATTR_SSL_CA => __DIR__ . '/ca.pem',
    PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
]);
// проверка шифрования
$stmt = $pdo->query('SHOW STATUS LIKE "Ssl_cipher"');
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo $row['Value']; // вывод используемого шифра
TLS_AES_256_GCM_SHA384

Пример показывает как принудительно включить SSL на стороне сервера и как клиент PHP подключается с проверкой сертификата.

Пример с mysqli и подготовленными запросами

Пример
$mysqli = new mysqli('remote-host.com', 'web_user', 'p@ss', 'store', 3306);
if ($mysqli->connect_errno) die($mysqli->connect_error);

$stmt = $mysqli->prepare('SELECT name, price FROM products WHERE category = ? AND price > ?');
$category = 'electronics';
$minPrice = 50;
$stmt->bind_param('sd', $category, $minPrice);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
    printf("%s: %.2f
", $row['name'], $row['price']);
}
$stmt->close();
$mysqli->close();
Смартфон X: 199.99
Ноутбук Y: 899.00

Использование подготовленных запросов предотвращает SQL-инъекции и повышает производительность при повторных вызовах.

Сравнение производительности: PDO vs mysqli при удаленном подключении

Пример
// PDO
$start = microtime(true);
$pdo = new PDO('mysql:host=...;dbname=test', 'u', 'p');
$pdo->query('SELECT 1');
echo 'PDO: ' . (microtime(true) - $start) . ' сек';

// mysqli
$start = microtime(true);
$mysqli = new mysqli(...);
$mysqli->query('SELECT 1');
echo 'mysqli: ' . (microtime(true) - $start) . ' сек';
PDO: 0.0082 сек
mysqli: 0.0076 сек

Разница незначительна. Выбор между PDO и mysqli зависит от потребностей в переносимости и дополнительных функциях (SSL, эмуляция подготовленных запросов).

Удаленное подключение к MySQL в PHP - comments

En
удаленная php mysql (php)