Skip to main content

К сожалению, тестирование кода показало, что сессия кодеигнитера не самое лучшее место для хранения секретного кода.

Давайте отключим автоматическую загрузку сессий
в файле /application/config/autoload.php строку

$autoload['libraries'] = array('database', 'session',);

заменяем обратно на

$autoload['libraries'] = array('database',);

Все остальные настройки кук мы оставим как и прежде, они нам еще пригодятся.

Апи каптчи

Нам нужно какое-то апи для хранения и получения данных каптчи. Для начала будем хранить их в БД. Потом, если захотим, перепишем хранение на Мемкешед или редис.

Создаем файл /application/models/captcha_api.php

<?php
/** 
 * Работаем с капчей
 */

class Captcha_api extends CI_Model {
    /**
     * Время в секундах, пока каптча принимается
     */
    var $timeout = 1800;

    /**
     * сохранить новую каптчу
     */
    function save($captcha, $user_agent = null) {
        if (!$user_agent) {
            $user_agent = $this->user_agent();
        }
    
        $this->db->set('captcha', trim(strtolower($captcha)));
        $this->db->set('user_agent', substr($user_agent, 0, 50));
        $this->db->set('created', time());
        $this->db->insert('captcha');  
        
        if (mt_rand(0, 100) < 5) {
            $this->clean_old();
        }  
    }

    /**
     * проверим каптчу
     */
    function check($captcha, $user_agent = null) {
        if (!$user_agent) {
            $user_agent = $this->user_agent();
        }
        
        $this->db->where('captcha', trim(strtolower($captcha)));
        $this->db->where('user_agent', substr($user_agent, 0, 50));
        $this->db->where('created >', time() - $this->timeout);
        $query = $this->db->get('captcha');
        return $query->num_rows() > 0;
    }

    /**
     * удалить использованную каптчу
     */
    function delete($captcha, $user_agent = null) {
        if (!$user_agent) {
            $user_agent = $this->user_agent();
        }    
        
        $this->db->where('captcha', trim(strtolower($captcha)));
        $this->db->where('user_agent', substr($user_agent, 0, 50));
        $this->db->delete('captcha');        
    }

    /**
     * удалим старую
     */
    function clean_old() {
        $this->db->where('created <', time() - $this->timeout);
        $this->db->delete('captcha');
    }
    
    function user_agent() {
        return $_SERVER['HTTP_USER_AGENT'];
    }
}

Метод save сохраняет новую секретную строку в базу. Мы используем HTTP_USER_AGENT для дополнительной проверки того, кто запрашивает каптчу. Если бы были везде включены сессии (плохая практика), то можно было бы использовать ид сессии и так было бы надежнее.

Метод check проверяет наличие секретной строки.
Метод delete удаляет указанную секретную строку, например когда она использована. Если не удалять каптчу, то еще какое-то довольно продолжительное время эту каптчу можно будет повторно использовать в куче мест.

Поскольку мы храним данные в БД, то надо очищать ее от устаревших данных. Для этого служит метод clean_old, который мы иногда вызываем при записи новой секретной строки.

Таблица в которой мы храним данные вот такая

CREATE TABLE IF NOT EXISTS `captcha` (
  `captcha` varchar(10) NOT NULL,
  `user_agent` varchar(50) NOT NULL,
  `created` int(11) NOT NULL,
  KEY `captcha` (`captcha`),
  KEY `created` (`created`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;