"Держите меня в системе" - лучший подход


Мое веб-приложение использует сеансы для хранения информации о пользователе после входа в систему и для сохранения этой информации при переходе со страницы на страницу в приложении. В этом конкретном приложении я сохраняю user_id, first_name и last_name человека.

Я хотел бы предложить опцию "Держать меня в системе" при входе в систему, которая поместит файл cookie на компьютер пользователя в течение двух недель, который перезапустит их сеанс с теми же данными, когда они вернутся в приложение.

Каков наилучший подход для этого? Я не хочу хранить их user_id в файле cookie, так как кажется, что это облегчит одному пользователю попытку подделать личность другого пользователя.

Author: Jimbo, 2009-08-31

12 answers

Хорошо, позвольте мне сказать прямо: если вы помещаете пользовательские данные или что-либо, полученное из пользовательских данных, в файл cookie для этой цели, вы делаете что-то неправильно.

Вот. Я сказал это. Теперь мы можем перейти к фактическому ответу.

Что плохого в хэшировании пользовательских данных, спросите вы? Ну, все сводится к поверхности воздействия и безопасности через неизвестность.

Представьте на секунду, что вы нападающий. Вы видите криптографический набор файлов cookie для "запомни меня" на своем сессия. Его ширина составляет 32 символа. Ничего себе. Это может быть MD5...

Давайте также представим на секунду, что они знают алгоритм, который вы использовали. Например:

md5(salt+username+ip+salt)

Теперь все, что нужно сделать злоумышленнику, - это применить грубую силу к "соли" (которая на самом деле не соль, но об этом позже), и теперь он может генерировать все поддельные токены, которые он хочет, с любым именем пользователя для своего IP-адреса! Но грубое принуждение к соли - это сложно, верно? Абсолютно. Но современные графические процессоры чрезвычайно хороши в этом. И если только вы используете в нем достаточную случайность (сделайте его достаточно большим), он быстро упадет, а вместе с ним и ключи от вашего замка.

Короче говоря, единственное, что защищает вас, - это соль, которая на самом деле защищает вас не так сильно, как вы думаете.

Но Подождите!

Все это было основано на том, что злоумышленник знает алгоритм! Если это секретно и сбивает с толку, тогда ты в безопасности, верно? НЕПРАВИЛЬНО. У этого направления мышления есть название: Безопасность через Неизвестность, на которую НИКОГДА не следует полагаться.

Лучший Способ

Лучший способ - никогда не позволять информации пользователя покидать сервер, за исключением идентификатора.

Когда пользователь войдет в систему, сгенерируйте большой (от 128 до 256 бит) случайный токен. Добавьте это в таблицу базы данных, которая сопоставляет токен с идентификатором пользователя, а затем отправьте его клиенту в файле cookie.

Что, если злоумышленник угадает случайный токен другого пользователя?

Хорошо, давайте немного посчитаем здесь. Мы генерируем 128-битный случайный токен. Это означает, что существуют:

possibilities = 2^128
possibilities = 3.4 * 10^38

Теперь, чтобы показать, насколько абсурдно велико это число, давайте представим, что каждый сервер в Интернете (скажем, 50 000 000 сегодня) пытается перебрать это число со скоростью 1 000 000 000 в секунду каждый. На самом деле ваши серверы расплавились бы под такой нагрузкой, но давайте разыграем это.

guesses_per_second = servers * guesses
guesses_per_second = 50,000,000 * 1,000,000,000
guesses_per_second = 50,000,000,000,000,000

Итак, 50 квадриллионов догадок в секунду. Это быстро! Верно?

time_to_guess = possibilities / guesses_per_second
time_to_guess = 3.4e38 / 50,000,000,000,000,000
time_to_guess = 6,800,000,000,000,000,000,000

Итак, 6.8 секстиллион секунд...

Давайте попробуем свести это к более дружественным числам.

215,626,585,489,599 years

Или даже лучше:

47917 times the age of the universe

Да, это в 47917 раз больше возраста Вселенной...

В принципе, он не будет взломан.

Итак, подведем итог:

Лучший подход, который я рекомендую, - хранить файл cookie из трех частей.

function onLogin($user) {
    $token = GenerateRandomToken(); // generate a token, should be 128 - 256 bit
    storeTokenForUser($user, $token);
    $cookie = $user . ':' . $token;
    $mac = hash_hmac('sha256', $cookie, SECRET_KEY);
    $cookie .= ':' . $mac;
    setcookie('rememberme', $cookie);
}

Затем для проверки:

function rememberMe() {
    $cookie = isset($_COOKIE['rememberme']) ? $_COOKIE['rememberme'] : '';
    if ($cookie) {
        list ($user, $token, $mac) = explode(':', $cookie);
        if (!hash_equals(hash_hmac('sha256', $user . ':' . $token, SECRET_KEY), $mac)) {
            return false;
        }
        $usertoken = fetchTokenByUserName($user);
        if (hash_equals($usertoken, $token)) {
            logUserIn($user);
        }
    }
}

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

Теперь очень важно, чтобы SECRET_KEY был криптографическим секретом (сгенерированным чем-то вроде /dev/urandom и/или полученным из ввода с высокой энтропией). Кроме того, GenerateRandomToken() должен быть сильным случайным источником (mt_rand() недостаточно силен. Используйте библиотеку, такую как Randomlib или random_compat, или mcrypt_create_iv() с DEV_URANDOM)...

В hash_equals() заключается в предотвращении временных атак. Если вы используете версию PHP ниже PHP 5.6, функция hash_equals() не поддерживается. В этом случае вы можете заменить hash_equals() с помощью функции timingSafeCompare:

/**
 * A timing safe equals comparison
 *
 * To prevent leaking length information, it is important
 * that user input is always used as the second parameter.
 *
 * @param string $safe The internal (safe) value to be checked
 * @param string $user The user submitted (unsafe) value
 *
 * @return boolean True if the two strings are identical.
 */
function timingSafeCompare($safe, $user) {
    if (function_exists('hash_equals')) {
        return hash_equals($safe, $user); // PHP 5.6
    }
    // Prevent issues if string length is 0
    $safe .= chr(0);
    $user .= chr(0);

    // mbstring.func_overload can make strlen() return invalid numbers
    // when operating on raw binary strings; force an 8bit charset here:
    if (function_exists('mb_strlen')) {
        $safeLen = mb_strlen($safe, '8bit');
        $userLen = mb_strlen($user, '8bit');
    } else {
        $safeLen = strlen($safe);
        $userLen = strlen($user);
    }

    // Set the result to the difference between the lengths
    $result = $safeLen - $userLen;

    // Note that we ALWAYS iterate over the user-supplied length
    // This is to prevent leaking length information
    for ($i = 0; $i < $userLen; $i++) {
        // Using % here is a trick to prevent notices
        // It's safe, since if the lengths are different
        // $result is already non-0
        $result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i]));
    }

    // They are only identical strings if $result is exactly 0...
    return $result === 0;
}
 627
Author: ircmaxell, 2016-04-27 08:48:43

Уведомление о безопасности: Основывать файл cookie на хэше MD5 детерминированных данных - плохая идея; лучше использовать случайный токен, полученный из CSPRNG. Смотрите ответ ircmaxell на этот вопрос для более безопасного подхода.

Обычно я делаю что-то вроде этого:

  1. Пользователь входит в систему с помощью "держите меня в системе"
  2. Создать сеанс
  3. Создайте файл cookie под названием ЧТО-ТО, содержащее: md5 (соль+ имя пользователя+ip+соль) и файл cookie под названием Что-то другое, содержащее идентификатор
  4. Хранить файлы cookie в базе данных
  5. Пользователь делает что-то и уходит----
  6. Пользователь возвращается, проверяет наличие какого-либо файла cookie, если он существует, получает старый хэш из базы данных для этого пользователя, проверяет, совпадает ли содержимое файла cookie с хэшем из базы данных, который также должен совпадать с недавно вычисленным хэшем (для ip) таким образом: cookiehash==databasehash==md5 (соль+имя пользователя+ip+соль), если они это делают, переход 2, если они не переходят 1

Конечно, вы можете использовать разные имена файлов cookie и т.д. Также вы можете немного изменить содержимое файла cookie, просто убедитесь, что его нелегко создать. Вы можете, например, также создать user_salt при создании пользователя, а также поместить его в файл cookie.

Также вы можете использовать sha1 вместо md5 (или практически любой алгоритм)

 94
Author: Pim Jager, 2017-05-23 12:26:32

Введение

Ваше название "Держите меня в системе" - лучший подход затрудняет для меня понимание того, с чего начать, потому что, если вы ищете лучший подход, вам придется рассмотреть следующее:

  • Идентификация
  • Безопасность

Файлы cookie

Файлы cookie уязвимы, между распространенными уязвимостями для кражи файлов cookie в браузере и атаками с использованием межсайтовых сценариев мы должны согласиться с тем, что файлы cookie небезопасны. Чтобы помочь повысить безопасность, вы должны отметить, что php setcookies имеет дополнительные функции, такие как

Bool setcookie (строка $имя [, строка $значение [, int $срок действия =0[, строка $путь [, строка $домен [, bool $безопасный=ложь [, bool$httponly=ложь ]]]]]] )

  • безопасный (с использованием HTTPS-соединения)
  • httponly (Уменьшить кражу личных данных с помощью атаки XSS)

Определения

  • Токен (непредсказуемая случайная строка длиной n, например, /dev/urandom)
  • Ссылка (непредсказуемая случайная строка длиной n, например, /dev/urandom)
  • Подпись (Сгенерируйте хэш-значение с ключом, используя метод HMAC)

Простой Подход

Простым решением было бы:

  • Пользователь вошел в систему с помощью команды Запомнить меня
  • Файл cookie для входа, выданный с токеном и подписью
  • Когда возвращается, подпись проверено
  • Если подпись в порядке.. то имя пользователя и токен ищутся в базе данных
  • если неверно.. вернитесь на страницу входа в систему
  • Если действителен, автоматически войдите в систему

В приведенном выше примере обобщены все примеры, приведенные на этой странице, но их недостатки заключаются в том, что

  • Невозможно узнать, были ли украдены файлы cookie
  • Злоумышленник может получить доступ к конфиденциальным операциям, таким как смена пароля или данных, таких как личные и личные данные. информация и т.д.
  • Скомпрометированный файл cookie будет по-прежнему действителен в течение срока действия файла cookie

Лучшее Решение

Лучшим решением было бы

  • Пользователь вошел в систему и выбран параметр запомнить меня
  • Генерировать токен и подпись и хранить в файле cookie
  • Маркеры являются случайными и действительны только для одиночной аутентификации
  • Токен заменяется при каждом посещении сайта
  • Когда незарегистрированный пользователь посещает сайт подпись, токен и имя пользователя проверены
  • Запомнить меня логин должен иметь ограниченный доступ и не допускать изменения пароля, личной информации и т.д.

Пример Кода

// Set privateKey
// This should be saved securely 
$key = 'fc4d57ed55a78de1a7b31e711866ef5a2848442349f52cd470008f6d30d47282';
$key = pack("H*", $key); // They key is used in binary form

// Am Using Memecahe as Sample Database
$db = new Memcache();
$db->addserver("127.0.0.1");

try {
    // Start Remember Me
    $rememberMe = new RememberMe($key);
    $rememberMe->setDB($db); // set example database

    // Check if remember me is present
    if ($data = $rememberMe->auth()) {
        printf("Returning User %s\n", $data['user']);

        // Limit Acces Level
        // Disable Change of password and private information etc

    } else {
        // Sample user
        $user = "baba";

        // Do normal login
        $rememberMe->remember($user);
        printf("New Account %s\n", $user);
    }
} catch (Exception $e) {
    printf("#Error  %s\n", $e->getMessage());
}

Используемый класс

class RememberMe {
    private $key = null;
    private $db;

    function __construct($privatekey) {
        $this->key = $privatekey;
    }

    public function setDB($db) {
        $this->db = $db;
    }

    public function auth() {

        // Check if remeber me cookie is present
        if (! isset($_COOKIE["auto"]) || empty($_COOKIE["auto"])) {
            return false;
        }

        // Decode cookie value
        if (! $cookie = @json_decode($_COOKIE["auto"], true)) {
            return false;
        }

        // Check all parameters
        if (! (isset($cookie['user']) || isset($cookie['token']) || isset($cookie['signature']))) {
            return false;
        }

        $var = $cookie['user'] . $cookie['token'];

        // Check Signature
        if (! $this->verify($var, $cookie['signature'])) {
            throw new Exception("Cokies has been tampared with");
        }

        // Check Database
        $info = $this->db->get($cookie['user']);
        if (! $info) {
            return false; // User must have deleted accout
        }

        // Check User Data
        if (! $info = json_decode($info, true)) {
            throw new Exception("User Data corrupted");
        }

        // Verify Token
        if ($info['token'] !== $cookie['token']) {
            throw new Exception("System Hijacked or User use another browser");
        }

        /**
         * Important
         * To make sure the cookie is always change
         * reset the Token information
         */

        $this->remember($info['user']);
        return $info;
    }

    public function remember($user) {
        $cookie = [
                "user" => $user,
                "token" => $this->getRand(64),
                "signature" => null
        ];
        $cookie['signature'] = $this->hash($cookie['user'] . $cookie['token']);
        $encoded = json_encode($cookie);

        // Add User to database
        $this->db->set($user, $encoded);

        /**
         * Set Cookies
         * In production enviroment Use
         * setcookie("auto", $encoded, time() + $expiration, "/~root/",
         * "example.com", 1, 1);
         */
        setcookie("auto", $encoded); // Sample
    }

    public function verify($data, $hash) {
        $rand = substr($hash, 0, 4);
        return $this->hash($data, $rand) === $hash;
    }

    private function hash($value, $rand = null) {
        $rand = $rand === null ? $this->getRand(4) : $rand;
        return $rand . bin2hex(hash_hmac('sha256', $value . $rand, $this->key, true));
    }

    private function getRand($length) {
        switch (true) {
            case function_exists("mcrypt_create_iv") :
                $r = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
                break;
            case function_exists("openssl_random_pseudo_bytes") :
                $r = openssl_random_pseudo_bytes($length);
                break;
            case is_readable('/dev/urandom') : // deceze
                $r = file_get_contents('/dev/urandom', false, null, 0, $length);
                break;
            default :
                $i = 0;
                $r = "";
                while($i ++ < $length) {
                    $r .= chr(mt_rand(0, 255));
                }
                break;
        }
        return substr(bin2hex($r), 0, $length);
    }
}

Тестирование в Firefox и Chrome

enter image description here

Преимущество

  • Лучшая безопасность
  • Ограниченный доступ для злоумышленника
  • Когда файл cookie украден, действителен только для однократного доступа
  • Когда в следующий раз первоначальный пользователь зайдет на сайт, вы сможете автоматически обнаружить и уведомить пользователя о краже

Недостаток

  • Не поддерживает постоянное соединение через несколько браузеров (мобильных и веб-)
  • Файл cookie все еще может быть украден, потому что пользователь получает уведомление только после следующего входа в систему.

Быстрое Решение

  • Введение официального утверждения система для каждой системы, которая должна иметь постоянное соединение
  • Используйте несколько файлов cookie для аутентификации

Подход с использованием Нескольких Файлов Cookie

Когда злоумышленник собирается украсть cookes, единственное, что нужно сделать, это сосредоточиться на определенном веб-сайте или домене, например. example.com

Но на самом деле вы можете аутентифицировать пользователя из 2 разных доменов (example.com & fakeaddsite.com ) и сделайте его похожим на "Рекламный файл cookie".

  • Пользователь вошел в систему example.com с помни меня
  • Хранить имя пользователя, токен, ссылку в файле cookie
  • Храните имя пользователя, токен, ссылку в базе данных, например. Кэш памяти
  • Отправьте идентификатор рефренса через get и iframe в fakeaddsite.com
  • fakeaddsite.com использует ссылку для извлечения пользователя и токена из базы данных
  • fakeaddsite.com сохраняет подпись
  • Когда пользователь возвращает информацию о подписи выборки с помощью iframe от fakeaddsite.com
  • Объедините данные и выполните проверку
  • ..... вы знаете остальные

Некоторые люди могут задаться вопросом, как вы можете использовать 2 разных файла cookie? Ну, это возможно, представьте example.com = localhost и fakeaddsite.com = 192.168.1.120. Если вы проверите файлы cookie, это будет выглядеть так

enter image description here

С изображения выше

  • Текущий посещенный сайт является локальным хостом
  • Он также содержит файлы cookie, установленные из 192.168.1.120

192.168.1.120

  • Принимает только определенные HTTP_REFERER
  • Принимает подключение только от указанного REMOTE_ADDR
  • Нет JavaScript, нет контента, но он не содержит ничего, кроме подписанной информации и добавления или извлечения ее из файла cookie

Преимущество

  • в 99% случаев вы обманывали злоумышленника
  • Вы можете легко заблокировать учетную запись при первой попытке злоумышленника
  • Атака может быть предотвращено даже до следующего входа в систему, как и другие методы

Недостаток

  • Многократный запрос на сервер только для одного входа в систему

Улучшение

  • Готово использовать использование iframe ajax
 67
Author: Baba, 2013-07-12 12:49:03

Есть две очень интересные статьи, которые я нашел во время поиска идеального решения проблемы "запомни меня":

 24
Author: Stefan Gehrig, 2009-08-31 06:38:23

Я задал один из аспектов этого вопроса здесь , и ответы приведут вас ко всем ссылкам на файлы cookie на основе маркеров, которые вам нужны.

В принципе, вы не сохраняете идентификатор пользователя в файле cookie. Вы храните одноразовый токен (огромную строку), который пользователь использует для восстановления своего старого сеанса входа в систему. Затем, чтобы сделать его действительно безопасным, вы запрашиваете пароль для тяжелых операций (например, для изменения самого пароля).

 6
Author: Dan Rosenstark, 2017-05-23 12:10:33

Я бы рекомендовал подход, упомянутый Стефаном (т.Е. следовать рекомендациям в Лучшей практике использования файлов cookie для постоянного входа ), а также рекомендовал бы вам убедиться, что ваши файлы cookie являются файлами cookie HttpOnly, поэтому они недоступны для потенциально вредоносного JavaScript.

 5
Author: Walter Rumsby, 2010-02-20 22:40:25

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

 4
Author: Jani Hartikainen, 2009-08-30 21:52:54

Старая тема, но все еще серьезная проблема. Я заметил несколько хороших ответов о безопасности и избегании использования "безопасности через неизвестность", но фактических технических методов, приведенных в моих глазах, было недостаточно. Вещи, которые я должен сказать, прежде чем я предложу свой метод:

  • НИКОГДА не храните пароль открытым текстом...НИКОГДА!
  • НИКОГДА не храните хэшированный пароль пользователя более чем в одном месте в вашей базе данных. Серверная часть вашего сервера всегда способна извлекать хэшированный пароль из таблицы пользователей. Хранить избыточные данные вместо дополнительных транзакций БД не более эффективно, верно обратное.
  • Ваши идентификаторы сеанса должны быть уникальными, поэтому никакие два пользователя не могут когда-либо совместно использовать идентификатор, следовательно, назначение идентификатора (может ли идентификационный номер вашего водительского удостоверения когда-либо совпадать с другими лицами? Нет.) Это создает уникальную комбинацию из двух частей, основанную на 2 уникальных строках. Ваша таблица сеансов должна использовать идентификатор в качестве PK. Чтобы разрешить нескольким устройствам быть доверенный для автоматической регистрации используйте другую таблицу для доверенных устройств, которая содержит список всех проверенных устройств (см. Мой пример ниже) и отображается с использованием имени пользователя.
  • Нет смысла хэшировать известные данные в файл cookie, файл cookie можно скопировать. То, что мы ищем, - это соответствующее пользовательское устройство для предоставления достоверной информации, которую невозможно получить без того, чтобы злоумышленник не поставил под угрозу компьютер пользователя (опять же, см. Мой пример). Это, однако, означало бы, что законный пользователь тот, кто запрещает статической информации своей машины (т. Е. MAC-адресу, имени хоста устройства, агенту пользователя, если она ограничена браузером, и т.д.) Оставаться согласованной (или в первую очередь подделывает ее), не сможет использовать эту функцию. Но если это вызывает беспокойство, учтите тот факт, что вы предлагаете автоматическую регистрацию пользователям, которые идентифицируйте себя уникальным образом, поэтому, если они откажутся быть известными, подделав свой MAC, подделав своего агента пользователя, подделав/изменив имя хоста, скрываясь за прокси-серверы и т. Д., То они не идентифицируются и никогда не должны аутентифицироваться для автоматической службы. Если вы хотите этого, вам нужно изучить доступ к смарт-картам в комплекте с клиентским программным обеспечением, которое устанавливает личность используемого устройства.

Как бы то ни было, есть два отличных способа автоматического входа в вашу систему.

Во-первых, дешевый, простой способ, который перекладывает все это на кого-то другого. Если вы создадите поддержку своего сайта, войдя, скажем, в свою учетную запись Google+, вероятно, у вас есть оптимизированная кнопка google+, которая позволит пользователю войти в систему, если он уже вошел в Google (я сделал это здесь, чтобы ответить на этот вопрос, так как я всегда входил в Google). Если вы хотите, чтобы пользователь автоматически входил в систему, если он уже вошел в систему с помощью надежного и поддерживаемого средства проверки подлинности, и установил флажок для этого, попросите ваши клиентские сценарии выполнить код за соответствующей кнопкой "вход с помощью" перед загрузкой, просто убедитесь, что сервер хранит уникальный идентификатор в таблице автоматической регистрации, в которой указано имя пользователя, идентификатор сеанса и средство проверки подлинности, используемое для пользователя. Поскольку эти методы входа используют AJAX, вы все равно ждете ответа, и этот ответ является либо проверенным ответом, либо отклонением. Если вы получите подтвержденный ответ, используйте его как обычно, а затем продолжайте загружать вошедшего в систему пользователя как обычно. В противном случае вход в систему не удался, но не сообщайте об этом пользователю, просто продолжайте, как не вошли в систему, они заметят. Это делается для предотвращения злоумышленника, который украл файлы cookie (или подделал их в попытке повысить привилегии), узнав, что пользователь автоматически входит на сайт.

Это дешево, и некоторые могут также посчитать это грязным, потому что он пытается подтвердить вашу потенциально уже зарегистрированную личность в таких местах, как Google и Facebook, даже не сообщая вам об этом. Однако его не следует использовать для пользователей, которые не запрашивали автоматическую регистрацию на вашем сайте, и этот конкретный метод предназначен только для внешней аутентификации, например, в Google+ или ФБ.

Поскольку внешний аутентификатор использовался для того, чтобы сообщить серверу за кулисами, был ли пользователь проверен, злоумышленник не может получить ничего, кроме уникального идентификатора, который сам по себе бесполезен. Я уточню:

  • Пользователь "Джо" посещает сайт в первый раз, идентификатор сеанса помещен в файл cookie "сеанс".
  • Пользователь "джо" Входит в систему, повышает привилегии, получает новый идентификатор сеанса и обновляет "сеанс" cookie.
  • Пользователь "джо" выбирает автоматическую регистрацию с помощью Google+, получает уникальный идентификатор, помещенный в файл cookie "keepmesignedin".
  • Пользователь "Джо" зарегистрирован в Google, что позволяет вашему сайту автоматически регистрировать пользователя с помощью Google в вашем бэкэнде.
  • Злоумышленник систематически пробует уникальные идентификаторы для "keepmesignedin" (это общедоступная информация, предоставляемая каждому пользователю) и не входит ни в какое другое место; пробует уникальный идентификатор, присвоенный "джо".
  • Сервер получает уникальный идентификатор для "Джо", извлекает совпадение в БД для учетной записи Google+.
  • Сервер отправляет Злоумышленнику для входа в систему страница, на которой выполняется AJAX-запрос в Google для входа в систему.
  • Сервер Google получает запрос, использует свой API, чтобы увидеть, что злоумышленник в данный момент не вошел в систему.
  • Google отправляет ответ, что в настоящее время в этом соединении нет пользователя, вошедшего в систему.
  • Страница злоумышленника получает ответ, скрипт автоматически перенаправляет на страницу входа в систему со значением POST, закодированным в URL.
  • Страница входа в систему получает значение POST, отправляет файл cookie для "keepmesignedin" в пустое значение и действительный до даты 1-1-1970, чтобы предотвратить автоматическую попытку, в результате чего браузер злоумышленника просто удалит файл cookie.
  • Злоумышленнику предоставляется обычная страница входа в систему при первом входе.

Несмотря ни на что, даже если злоумышленник использует несуществующий идентификатор, попытка должна завершиться неудачей при всех попытках, за исключением случаев, когда получен проверенный ответ.

Этот метод может и должен использоваться в сочетании с вашим внутренним средством аутентификации для тех, кто входит на ваш сайт с помощью внешнего аутентификатор.

=========

Теперь, для вашей собственной системы аутентификации, которая может автоматически подписывать пользователей, вот как я это делаю:

В БД есть несколько таблиц:

TABLE users:
UID - auto increment, PK
username - varchar(255), unique, indexed, NOT NULL
password_hash - varchar(255), NOT NULL
...

Обратите внимание, что имя пользователя может содержать 255 символов. У меня есть серверная программа, ограничивающая имена пользователей в моей системе 32 символами, но у внешних аутентификаторов имена пользователей с их @domain.tld могут быть больше, поэтому я просто поддерживаю максимальную длину адреса электронной почты для максимального совместимость.

TABLE sessions:
session_id - varchar(?), PK
session_token - varchar(?), NOT NULL
session_data - MediumText, NOT NULL

Обратите внимание, что в этой таблице нет поля пользователя, потому что имя пользователя при входе в систему находится в данных сеанса, а программа не допускает нулевых данных. session_id и session_token могут быть сгенерированы с использованием случайных хэшей md5, хэшей sha1/128/256, меток даты и времени со случайными строками, добавленными к ним, а затем хэшированными, или как вам угодно, но энтропия вашего вывода должна оставаться настолько высокой, насколько это допустимо, чтобы смягчить атаки грубой силы даже при выходе из строя. основание и все хэши, сгенерированные вашим классом сеанса, должны быть проверены на совпадения в таблице сеансов, прежде чем пытаться их добавить.

TABLE autologin:
UID - auto increment, PK
username - varchar(255), NOT NULL, allow duplicates
hostname - varchar(255), NOT NULL, allow duplicates
mac_address - char(23), NOT NULL, unique
token - varchar(?), NOT NULL, allow duplicates
expires - datetime code

MAC-адреса по своей природе должны быть УНИКАЛЬНЫМИ, поэтому имеет смысл, чтобы каждая запись имела уникальное значение. С другой стороны, имена хостов могут быть законно продублированы в отдельных сетях. Сколько людей используют "Домашний компьютер" в качестве одного из имен своих компьютеров? Имя пользователя берется из данных сеанса серверной частью, поэтому манипулировать им невозможно. Что касается токена, тот же метод генерации токенов сеанса для страниц следует использовать для генерации токенов в файлах cookie для автоматической регистрации пользователя. Наконец, добавляется код даты и времени, когда пользователю потребуется повторно подтвердить свои учетные данные. Либо обновите эту дату и время при входе пользователя, сохранив ее в течение нескольких дней, либо заставьте ее истечь независимо от последнего входа, сохранив ее только на месяц или около того, в зависимости от того, что диктует ваш дизайн.

Это мешает кому-либо систематически подменяя MAC и имя хоста для пользователя, которого они знают, автоматически выполняет вход. НИКОГДА попросите пользователя сохранить файл cookie со своим паролем, открытым текстом или иным способом. Попросите, чтобы маркер создавался заново при каждой навигации по страницам, так же, как и маркер сеанса. Это значительно снижает вероятность того, что злоумышленник может получить действительный файл cookie токена и использовать его для входа в систему. Некоторые люди попытаются сказать, что злоумышленник может украсть файлы cookie у жертвы и повторить сеанс атака для входа в систему. Если бы злоумышленник мог украсть файлы cookie (что возможно), они, безусловно, скомпрометировали бы все устройство, а это означает, что они могли бы просто использовать устройство для входа в систему в любом случае, что полностью противоречит цели кражи файлов cookie. Пока ваш сайт работает по протоколу HTTPS (что необходимо при работе с паролями, номерами учетных записей или другими системами входа), вы предоставляете пользователю всю возможную защиту в браузере.

Следует иметь в виду одну вещь: данные сеанса срок действия не должен истекать, если вы используете автоматическую регистрацию. Вы можете прекратить возможность ложного продолжения сеанса, но проверка в системе должна возобновить данные сеанса, если это постоянные данные, которые, как ожидается, будут продолжаться между сеансами. Если вам нужны как постоянные, так и непостоянные данные сеанса, используйте другую таблицу для постоянных данных сеанса с именем пользователя в качестве PK, и пусть сервер извлекает их, как обычные данные сеанса, просто используйте другую переменную.

После входа в систему если это было достигнуто таким образом, сервер все равно должен проверить сеанс. Именно здесь вы можете кодировать ожидания для украденных или скомпрометированных систем; шаблоны и другие ожидаемые результаты входа в данные сеанса часто могут привести к выводам о том, что система была взломана или файлы cookie были подделаны для получения доступа. Именно здесь ваш специалист по ISS может установить правила, которые приведут к блокировке учетной записи или автоматическому удалению пользователя из системы автоматической регистрации, удерживая злоумышленников достаточно долго для пользователя чтобы определить, как злоумышленнику это удалось и как их отключить.

В качестве заключительного замечания, убедитесь, что любая попытка восстановления, изменение пароля или сбой входа после порогового значения приводят к отключению автоматического входа до тех пор, пока пользователь не подтвердит правильность и не подтвердит, что это произошло.

Прошу прощения, если кто-то ожидал, что в моем ответе будет указан код, здесь этого не произойдет. Я скажу, что я использую PHP, jQuery и AJAX для запуска своих сайтов, и я НИКОГДА не использую Windows в качестве сервер... когда-либо.

 3
Author: user253780, 2016-01-07 22:31:29

Мое решение таково. Он не на 100% пуленепробиваемый, но я думаю, что он спасет вас в большинстве случаев.

Когда пользователь успешно вошел в систему, создайте строку с этой информацией:

$data = (SALT + ":" + hash(User Agent) + ":" + username 
                     + ":" + LoginTimestamp + ":"+ SALT)

Зашифруйте $data, установите тип HttpOnly и установите файл cookie.

Когда пользователь вернется на ваш сайт, выполните следующие действия:

  1. Получить данные файлов cookie. Удалите опасные символы внутри файла cookie. Взорвите его с помощью символа :.
  2. Проверьте правильность. Если файл cookie старше X дней, затем перенаправьте пользователя на страницу входа в систему.
  3. Если файл cookie не устарел; Получите последнее время смены пароля из базы данных. Если пароль изменен после последнего входа пользователя, перенаправьте пользователя на страницу входа.
  4. Если пароль не был изменен недавно; Получите текущий агент браузера пользователя. Проверьте, является ли (currentuseragenthash ==cookieUserAgentHash). ЕСЛИ агенты одинаковы, перейдите к следующему шагу, иначе перенаправьте на страницу входа в систему.
  5. Если все шаги прошли успешно, авторизуйте имя пользователя.

Если пользователь выходит из системы, удалите этот файл cookie. Создайте новый файл cookie, если пользователь повторно войдет в систему.

 2
Author: trante, 2013-08-04 00:06:21

Я не понимаю концепции хранения зашифрованных данных в файле cookie, когда для взлома вам нужна его зашифрованная версия. Если я что-то упускаю, пожалуйста, прокомментируйте.

Я подумываю о том, чтобы использовать этот подход, чтобы "Помнить меня". Если вы видите какие-либо проблемы, пожалуйста, прокомментируйте.

  1. Создайте таблицу для хранения данных "Запомнить меня" отдельно от таблицы пользователей, чтобы я мог входить в систему с нескольких устройств.

  2. При успешном входе в систему (с галочкой "Запомнить меня"):

    A) Создайте уникальную случайную строку, которая будет использоваться в качестве идентификатора пользователя на этой машине: bigUserID

    Б) Сгенерировать уникальную случайную строку: bigKey

    C) Хранить файл cookie: bigUserID: bigkey

    D) В таблице "Запомнить меня" добавьте запись с: Идентификатором пользователя, IP-адресом, идентификатором bigUserID, BIGKEY

  3. При попытке получить доступ к чему-либо, требующему входа в систему:

    A) Проверьте наличие файла cookie и найдите bigUserID и BIGKEY с соответствующим IP-адресом адрес

    Б) Если вы его найдете, войдите в систему, но установите флажок в таблице пользователей "мягкий вход", чтобы при любых опасных операциях вы могли запрашивать полный вход.

  4. При выходе из системы отметьте все записи "Запомнить меня" для этого пользователя как просроченные.

Единственные уязвимости, которые я вижу, это;

  • вы можете завладеть чьим-то ноутбуком и подделать его IP-адрес с помощью файла cookie.
  • вы можете каждый раз подделывать другой IP-адрес и угадайте все это - но с двумя большими строками, которые должны совпадать, это было бы... выполнить расчет, аналогичный приведенному выше...Я понятия не имею... огромные шансы?
 2
Author: Enigma Plus, 2014-04-21 11:07:24

Я прочитал все ответы и все еще не мог понять, что я должен был делать. Если картинка стоит 1 тыс. слов, я надеюсь, что это поможет другим внедрить безопасное постоянное хранилище, основанное на Улучшенной практике использования файлов cookie для постоянного входа Барри Джаспана.

enter image description here

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

 2
Author: Josh Woodcock, 2016-11-28 13:29:30

Реализация функции "Держать меня в системе" означает, что вам нужно точно определить, что это будет означать для пользователя. В простейшем случае я бы использовал это для обозначения того, что сеанс имеет гораздо более длительный тайм-аут: 2 дня (скажем) вместо 2 часов. Для этого вам понадобится собственное хранилище сеансов, возможно, в базе данных, чтобы вы могли установить собственное время истечения срока действия для данных сеанса. Затем вам нужно убедиться, что вы установили файл cookie, который будет сохраняться в течение нескольких дней (или дольше), а не истекать, когда они закройте браузер.

Я слышу, как ты спрашиваешь: "Почему 2 дня? почему не 2 недели?". Это связано с тем, что использование сеанса в PHP автоматически отодвинет срок действия. Это связано с тем, что истечение сеанса в PHP на самом деле является временем простоя.

Теперь, сказав это, я, вероятно, реализовал бы более жесткое значение тайм-аута, которое я сохраняю в самом сеансе, и примерно через 2 недели или около того, и добавил бы код, чтобы увидеть это и принудительно аннулировать сеанс. Или, по крайней мере, вывести их из системы. Это будет означать, что пользователю будет предложено периодически входить в систему. Яху! делает это.

 0
Author: staticsan, 2009-08-31 06:00:10