PHP куки: установка, чтение и удаление (HttpOnly, Secure, SameSite)

PHP куки: установка, чтение и удаление (HttpOnly, Secure, SameSite)

PHP куки: установка, чтение и удаление (HttpOnly, Secure, SameSite)

Эта статья — практическое руководство по теме «PHP куки: установка, чтение и удаление». Разберём реальный синтаксис функции setcookie, научимся безопасно работать с атрибутами HttpOnly, Secure и SameSite, а также избежим типичных ошибок вроде отправки cookie после вывода HTML. В конце вы получите набор готовых функций для повседневной разработки.

Что такое куки и когда их использовать

Куки (cookies) — это небольшие фрагменты данных (до ~4 KB каждый), которые браузер хранит по домену и отправляет на сервер с каждым запросом. В PHP их используют для:

  • запоминания настроек пользователя (тема, язык);
  • простой персонализации интерфейса;
  • корзины гостя без авторизации;
  • механик «запомнить меня» (с осторожностью и подписью).
  • Важно: не храните в cookies чувствительные данные (пароли, токены без подписи).

    Быстрый старт: установка cookie через setcookie

    Современный и рекомендуемый способ (PHP 7.3+): передавать массив опций.

    <?php
    $isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443);
    
    setcookie('theme', 'dark', [
        'expires'  => time() + 60 * 60 * 24 * 30, // 30 дней
        'path'     => '/',
        'domain'   => '',        // по умолчанию текущий хост
        'secure'   => $isHttps,   // true только по HTTPS
        'httponly' => true,       // cookie недоступны JS (document.cookie)
        'samesite' => 'Lax',      // Lax | Strict | None (см. ниже)
    ]);
    
    echo 'Куки установлена!';
    

    Правила:

  • setcookie должен вызываться до любого вывода (до HTML, echo, var_dump).
  • Если нужен вывод раньше — включайте буферизацию (ob_start) или перестройте логику ответа.
  • Для поддоменов используйте ‘domain’ => ‘.example.com’.
  • Для удаления важно указывать те же path и domain, что и при установке.
  • Чтение cookie в PHP

    Читать куки просто: данные приходят в суперглобальном массиве $_COOKIE. Помните, это строки, и их может не быть.

    <?php
    $theme = $_COOKIE['theme'] ?? null;
    if ($theme === 'dark') {
        // подгружаем тёмную тему
    }
    

    Обновление и удаление куки

    Обновление — это повторная установка с тем же именем и новыми параметрами.

    <?php
    // обновляем срок жизни темы ещё на 30 дней
    setcookie('theme', 'dark', [
        'expires'  => time() + 60 * 60 * 24 * 30,
        'path'     => '/',
        'secure'   => true,
        'httponly' => true,
        'samesite' => 'Lax',
    ]);
    

    Удаление — это установка прошедшей даты истечения. Обязательно совпадайте по path и domain с оригиналом!

    <?php
    setcookie('theme', '', [
        'expires'  => time() - 3600, // в прошлом
        'path'     => '/',
        'secure'   => true,
        'httponly' => true,
        'samesite' => 'Lax',
    ]);
    

    SameSite, HttpOnly и Secure — как выбрать

  • HttpOnly: защитит от кражи cookie через XSS, т.к. JS не сможет прочитать cookie. Включайте для большинства серверных куки.
  • Secure: cookie уйдёт только по HTTPS. В продакшене — всегда true.
  • SameSite:
  • Lax — безопасный дефолт: отправляется при переходах ссылкой, но не при большинстве cross-site запросов.
  • Strict — максимально строго, не уходит при переходе с другой вкладки/сайта (может ломать UX).
  • None — cookie отправляется во всех кросс-сайтовых запросах, но требуется Secure = true. Используйте только при реальной необходимости (встраивание, SSO).
  • Готовые утилиты для работы с cookies

    Сниппеты, которые можно скопировать в проект. Они учитывают HTTPS и сразу синхронизируют $_COOKIE в текущем запросе.

    <?php
    function is_https(): bool {
        return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
            || (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443);
    }
    
    function cookie_set(string $name, string $value, array $options = []): void {
        $defaults = [
            'expires'  => 0,       // 0 = до закрытия браузера
            'path'     => '/',
            'domain'   => '',
            'secure'   => is_https(),
            'httponly' => true,
            'samesite' => 'Lax',
        ];
        $opts = array_merge($defaults, $options);
        setcookie($name, $value, $opts);
        // Синхронизируем локально, чтобы сразу видеть новое значение
        $_COOKIE[$name] = $value;
    }
    
    function cookie_get(string $name, $default = null) {
        return $_COOKIE[$name] ?? $default;
    }
    
    function cookie_delete(string $name, array $options = []): void {
        $defaults = [
            'path'     => '/',
            'domain'   => '',
            'secure'   => is_https(),
            'httponly' => true,
            'samesite' => 'Lax',
        ];
        $opts = array_merge($defaults, $options);
        $opts['expires'] = time() - 3600; // прошлое
        setcookie($name, '', $opts);
        unset($_COOKIE[$name]);
    }
    
    // Пример использования:
    cookie_set('lang', 'ru', ['expires' => time() + 86400 * 365]);
    $lang = cookie_get('lang', 'en');
    // ...
    // cookie_delete('lang');
    

    Работа с массивами и сложными значениями

    Куки — строки. Для хранения структур используйте JSON + base64, а лучше — подпись (HMAC), чтобы нельзя было подделать значение на клиенте.

    <?php
    const APP_KEY = 'super-secret-key-change-me'; // храните в .env
    
    function cookie_set_json(string $name, array $data, array $options = []): void {
        $payload = base64_encode(json_encode($data, JSON_UNESCAPED_UNICODE));
        $sig = hash_hmac('sha256', $payload, APP_KEY);
        cookie_set($name, $payload.'.'.$sig, $options);
    }
    
    function cookie_get_json(string $name, $default = null) {
        $raw = cookie_get($name);
        if (!$raw || !str_contains($raw, '.')) return $default;
        [$payload, $sig] = explode('.', $raw, 2);
        $calc = hash_hmac('sha256', $payload, APP_KEY);
        if (!hash_equals($calc, $sig)) return $default; // подпись не совпала
        $json = base64_decode($payload, true);
        return json_decode($json, true) ?? $default;
    }
    
    // Пример: лёгкая персонализация
    cookie_set_json('prefs', ['theme' => 'dark', 'font' => '16px'], [
        'expires' => time() + 86400 * 30,
    ]);
    $prefs = cookie_get_json('prefs', ['theme' => 'light']);
    

    Если всё же требуется шифрование, используйте проверенные библиотеки (libsodium) и ротируйте ключи. Но чаще на практике достаточно подписи и «не хранить секреты в куки».

    Типичные ошибки и как их избежать

  • Вывод до setcookie: любая пробельная строка перед <?php или echo ломает заголовки. Решение: подключайте куки в самом начале скрипта, используйте буферизацию или контролируйте порядок вывода.
  • Отсутствие Secure в проде: по HTTP куки утекают. Включайте HTTPS и ставьте secure=true.
  • SameSite=None без Secure: браузеры блокируют такие куки. Добавьте secure=true.
  • Удаление без совпадения path/domain: старая куки не стирается. Укажите такие же атрибуты, как при установке.
  • Слишком большие данные: одну куки ограничивайте ~4 KB. Иначе обрезка и ошибки. Храните только то, что нужно.
  • Хранение персональных/секретных данных: избегайте, используйте серверное хранилище (сессии, БД), а в куки — только идентификаторы с подписью.
  • Path, Domain и поддомены

    Атрибут path ограничивает видимость куки в рамках URL-пути. Часто ставят ‘/’. Атрибут domain определяет доменную зону: пустой — текущий хост; ‘.example.com’ — доступно на всех поддоменах. Для удаления используйте те же значения.

    <?php
    // Доступно на всех поддоменах *.example.com
    cookie_set('u', '123', [
        'domain' => '.example.com',
        'path'   => '/',
    ]);
    

    Куки «remember me»: короткий паттерн

    Минимальная безопасная схема — не хранить логин/пароль, а хранить случайный токен + подпись, проверяя его по БД на стороне сервера. При утечке токен можно отозвать.

    <?php
    // генерация при входе
    $token = bin2hex(random_bytes(32)); // сохраняем hash(token) в БД
    cookie_set('remember', $token, [
        'expires'  => time() + 60*60*24*30,
        'samesite' => 'Lax',
    ]);
    
    // при посещении сайта
    $token = cookie_get('remember');
    if ($token) {
        // ищем hash_equals(hashDB, hash(token)) и если ок — авторизуем, обновляем токен
    }
    

    Чек-лист перед продакшеном

  • HTTPS включён, Secure=true для всех важный куки;
  • HttpOnly=true для куки, которые не нужны JS;
  • SameSite подобран под сценарий (обычно Lax);
  • Не храните в куки секреты и персональные данные;
  • Размеры и количество под контролем (браузеры ограничивают ~20–50 куки на домен);
  • Удаление совпадает по path/domain;
  • Локально валидируете и подписываете сложные значения (HMAC).
  • Куда двигаться дальше

    Если вы хотите системно прокачать навыки PHP и базы данных, рекомендую пройти практический курс с множеством проектов: Прокачать PHP на практике: курс «PHP и MySQL с Нуля до Гуру 3.0». Там вы отработаете работу с куки, сессиями, БД и безопасностью на реальных задачах.

    Теперь вы уверенно управляете куки в PHP — от базовой установки до безопасной подписи и удаления. Используйте приведённые шаблоны как основу для своих проектов и не забывайте о безопасности в продакшене.

    Источник

    НЕТ КОММЕНТАРИЕВ

    Оставить комментарий