«Keep Me Logged In» - лучший подход - PullRequest
239 голосов
/ 31 августа 2009

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

Я хотел бы предложить опцию «Keep Me Logged In» при входе в систему, в которой на компьютер пользователя будут помещены файлы cookie на две недели, которые будут перезапускать сеанс с теми же данными, когда они возвращаются в приложение.

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

Ответы [ 12 ]

690 голосов
/ 24 июня 2013

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

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

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

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

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

md5(salt+username+ip+salt)

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

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

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

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

Лучший путь

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

Когда пользователь входит в систему, генерирует большой (от 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 раз возраста вселенной ...

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

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

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

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() на функцию хронометража безопасного сравнения:

/**
 * 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;
}
94 голосов
/ 31 августа 2009

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

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

  1. Пользователь входит в систему с «держать меня в системе»
  2. Создать сессию
  3. Создайте cookie с именем SOMETHING, содержащим: md5 (salt + username + ip + salt) и cookie с именем нечтоElse, содержащим id
  4. Хранить куки в базе данных
  5. Пользователь делает вещи и уходит ----
  6. Пользователь возвращает, проверяет что-то cookie, если оно существует, получает старый хеш из базы данных для этого пользователя, проверяет, совпадает ли содержимое куки-файла с хешем из базы данных, который также должен совпадать с вновь вычисленным хешем (для ip), таким образом: cookieHash == databaseHash == md5 (соль + имя пользователя + ip + соль), если они это делают, переходите к 2, если они не переходят к 1

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

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

74 голосов
/ 24 июня 2013

Введение

Ваш заголовок «Keep Me Logged In» - лучший подход затрудняет мне знать, с чего начать, потому что, если вы смотрите на лучший подход, вам придется учитывать следующее:

  • Определение
  • Безопасность

Печенье

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

.

bool setcookie (строка $ name [, строка $ value [, int $ expire = 0 [, строка $ path [, строка $ domain [, bool $ secure = false [, bool $ httponly = false]]]]]])

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

Определения

  • Token (непредсказуемая случайная строка длины n, например. / Dev / urandom)
  • Ссылка (непредсказуемая случайная строка длины n, например. / Dev / urandom)
  • Подпись (Генерация ключевого хеш-значения с использованием метода HMAC)

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

Простое решение будет:

  • Пользователь вошел в систему с помощью Запомнить меня
  • Логин 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

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

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

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

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

enter image description here

С картинки выше

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

192.168.1.120

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

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

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

Недостаток

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

Улучшение

  • Готово использовать iframe использовать ajax
24 голосов
/ 31 августа 2009

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

6 голосов
/ 31 августа 2009

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

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

5 голосов
/ 21 февраля 2010

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

4 голосов
/ 08 января 2016

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

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

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

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

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

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

  • Пользователь 'joe' впервые заходит на сайт, идентификатор сеанса помещен в файл cookie 'session'.
  • Пользователь 'joe' Вход в систему, повышение привилегий, получение нового идентификатора сеанса и возобновление сеанса cookie.
  • Пользователь "Джо" выбирает автоматическую регистрацию с помощью Google +, получает уникальный идентификатор, помещенный в cookie "keepmesignedin".
  • Пользователь "joe" имеет Google, чтобы держать его в системе, что позволяет вашему сайту автоматически входить в систему с помощью Google в вашем бэкэнде.
  • Атакующий систематически пытается использовать уникальные идентификаторы для «keepmesignedin» (это общедоступные знания, которые раздаются каждому пользователю), и нигде не подписывается; пытается уникальный идентификатор, присвоенный 'Джо'.
  • Сервер получает уникальный идентификатор для 'joe', извлекает совпадение в БД для учетной записи Google +.
  • Сервер отправляет Attacker на страницу входа, которая выполняет запрос AJAX для входа в Google.
  • Сервер Google получает запрос, использует свой API, чтобы увидеть, что Attacker не вошел в систему.
  • 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 и использовать его для входа в систему. Некоторые люди пытаются сказать, что злоумышленник может украсть куки у жертвы и выполнить атаку воспроизведения сеанса для входа в систему. Если бы злоумышленник мог украсть куки (что возможно), он, несомненно, скомпрометировал бы все устройство, то есть он мог бы просто использовать устройство для входа в систему в любом случае, что полностью противоречит цели кражи куки. Пока ваш сайт работает по протоколу HTTPS (что необходимо при работе с паролями, номерами CC или другими системами входа в систему), вы предоставляете пользователю всю защиту, которую вы можете использовать в браузере.

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

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

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

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

4 голосов
/ 31 августа 2009

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

2 голосов
/ 28 ноября 2016

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

enter image description here

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

2 голосов
/ 21 апреля 2014

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

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

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

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

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

    б) Генерация уникальной случайной строки: bigKey

    в) Сохранить куки: bigUserID: bigKey

    d) В таблице «Запомнить меня» добавьте запись с: UserID, IP Address, bigUserID, bigKey

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

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

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

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

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

  • вы можете получить чей-то ноутбук и подделать его IP-адрес с помощью cookie.
  • вы могли бы каждый раз подделывать разные IP-адреса и угадывать все это - но с двумя большими строками, чтобы соответствовать, это было бы ... делать вычисления, аналогичные приведенным выше ... Я понятия не имею ... огромные шансы
...