Безопасный способ регистрации, входа и токена

задняя часть PHP внешний интерфейс Android iOS

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

Ради безопасности сначала установите три принципа:

  1. Во время передачи передача данных о конфиденциальности пользователя в открытом виде не допускается;
  2. Локально данные о конфиденциальности пользователя не могут быть сохранены в виде открытого текста;
  3. На сервере не разрешено сохранять данные о конфиденциальности пользователя в открытом виде;

Что касается сети, мы знаем, что будут перехватываться как POST-запросы, так и GET-запросы.Если HTTPS не используется, мы не можем предотвратить перехват пакетов.Если конфиденциальность пользователя передается в виде простого текста, последствия обсуждаться не будут.

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

Использование Keychain также сохраняется локально, но не в песочнице, поэтому пока игнорируйте его.

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

шифрование

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

Base64 - это не только шифрование, его основная цель - преобразовать последовательности двоичных данных в последовательности символов ASCII для передачи данных. Что такое бинарные данные? Все данные, хранящиеся на компьютере, являются двоичными данными.

Наиболее распространенным сценарием применения Base64 являются URL-адреса, поскольку URL-адреса могут состоять только из определенных символов ASCII. В настоящее время необходимо использовать кодировку Base64. Конечно, это только кодировка самих двоичных данных. Закодированные данные могут содержать+/=и другие символы, когда они фактически размещены в URL-адресе, также требуется URL-Encoding, что становится%XXрежим для устранения неоднозначности этих символов. Второй — преобразовать изображение в строку Base64.

Так что Base64 — это просто кодировка, а не шифрование.

Хорошо, теперь вернемся к нашей теме, давайте поговорим о взаимосвязи между входом в систему и регистрацией, что нужно делать этим трем модулям?

  • Регистрация: отправьте введенные пользователем личные данные на сервер, и сервер их сохранит;
  • Логин: отправьте личные данные, введенные пользователем, на сервер, и сервер сравнит их, чтобы подтвердить, есть ли у вас разрешение на вход;
  • токен: убедитесь, что пользователь вошел в систему;

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

  • Интерфейс регистрации сервера: Получите учетную запись и пароль от клиента и сохраните их в базе данных;
  • Интерфейс сервер-логин: Получите учетную запись и пароль от клиента, сравните его с базой данных, если он попал полностью, вход выполнен успешно, иначе вход не выполнен;
    • После успешного входа сгенерируйте или обновите токен и срок действия, сохраните его в базе данных и верните токен клиенту;
    • Сервер периодически очищает токен;
  • Модуль регистрации клиентов: отправка учетной записи и пароля в интерфейс регистрации сервера;
  • Модуль входа в систему: отправка учетной записи и пароля в интерфейс входа на сервер;
    • После успешного входа сохраните токен локально;
    • После выхода очистите токен;
  • Отправляемый номер счета и пароль необходимо зашифровать;
  • Зашифрованные учетная запись и пароль должны быть сохранены в базе данных;
  • При запросе конфиденциальных данных сверяйте токен, отправленный клиентом с сервером.Если это не удается, клиенту будет предложено войти в систему;

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

/*获取 get 请求传递的参数*/
$account = $_GET['account'];
$password = $_GET['password'];

/*创建数据连接*/
$db = new DataBase();

/*检查用户名是否存在*/
$is_exist = $db->check_user_exist($account);

if ($is_exist) {
    echo return_value(10001, false);
}
else {
    /*检查用户名是否添加成功*/
    $result = $db->add_user($account, $password);
    if ($result) {
        echo return_value(0, true);
    }
    else {
        echo return_value(20001, false);
    }
}

Вот что делает интерфейс входа на сервер:

/*获取 get 请求传递的参数*/
$account = $_GET['account'];
$password = $_GET['password'];

/*创建数据连接*/
$db = new DataBase();

/*是否命中用户名和密码*/
$should_login = $db->should_login($account, $password);

if ($should_login) {
    /*更新 token*/
    $token = $db->insert_token($account);
    if ($token == '') {
        echo response(40001, false);
    }
    else {
        $data = ['token' => $token];
        echo response(0, $data);
    }
}
else {
    echo response(30001, false);
}

Остальное не более чем разница в алгоритмах шифрования.Наиболее часто используется md5, поэтому после шифрования md5 он все равно не очень безопасен.Почему? Потому что сам по себе md5 небезопасен. Хотя md5 является необратимым хеш-алгоритмом, его сложно вычислить в обратном порядке, но если вы измените запрос, установка пароля будет простой, и ее легко взломать.

Например, мы используем md5 для шифрования пароля123456, соответствующий md5 равенe10adc3949ba59abbe56e057f20f883e, найдите расшифрованный веб-сайт md5, напримерcmd5.com/, пароль легко взломать...

С солью

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

// 伪代码
salt = '#^&%**(^&(&*)_)_(*&^&#$%GVHKBJ(*^&*%^%&^&'
password = '123456'
post_body = salt + password
print post_body.md5()
// ffb34d898f6573a1cf14fdc34d3343c0

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

Есть вероятность утечки

Теперь мы добавляем в пароль соль md5 на стороне клиента, и сервер сохраняет зашифрованный контент, но соль прописывается в исходном коде на стороне клиента, после декомпиляции исходного кода его можно найтиsaltЭта строка, то практика добавления соли бесполезна.

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

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

слишком зависимый

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

HMAC

В настоящее время наиболее распространенным методом должен быть HMAC. Алгоритм HMAC в основном используется для аутентификации. Отличие от добавления соли заключается в том, что соль перемещается на сервер. Что бы ни возвращал сервер, она используется в качестве соли.

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

Но есть еще одна проблема, как мы уже упоминали ранее,Это очень опасно после получения соли.Если соль получена с сервера, то и пакет будет захвачен.Лучше прописать это в исходниках.По крайней мере декомпилировать сложно.Как решить эту скрытую Опасность??

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

Теперь отправляем запрос:

GET http://localhost:8888/capsule/register.php?account=joy&password=789

Получив запрос, сервер делает следующее:

/*获取 get 请求传递的参数*/
$account = $_GET['account'];
$password = $_GET['password'];  //123456

/*创建数据连接*/
$db = new DataBase();

/*制作一个随机的盐*/
$salt = salt();

/*检查用户名是否存在*/
$is_exist = $db->check_user_exist($account);

if ($is_exist) {
    echo response(10001, false);
}
else {

    /*将密码进行 hmac 加密*/
    $password = str_hmac($password,  $salt);

    /*检查用户名是否添加成功*/
    $result = $db->add_user($account, $password);

    if ($result) {
        $data = ['salt'=>$salt];
        echo response(0, $data);
        //echo response(0, true);
    }
    else {
        echo response(20001, false);
    }
}

Сейчас на сервере есть:

account: joy
password: 05575c24576

Результат, полученный клиентом:

{
  "rc": 0,
  "data": {
    "salt": "5633905fdc65b6c57be8698b1f0e884948c05d7f"
  },
  "errorInfo": ""
}

Так что же делать клиенту дальше? ПучокsaltСделайте локальное сохранение, используйте тот же hmac для пароля, введенного пользователем при входе в систему, тогда вы можете передать пароль сервера.password: 05575c24576После проверки инициируйте запрос на вход:

GET http://localhost:8888/capsule/login.php?account=joy&password=789 
// fail
GET http://localhost:8888/capsule/login.php?account=joy&password=05575c24576 
// success

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

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

// 伪代码
func login(account, password) {
    //如果有盐
    if let salt = getSalt() {
        //将密码进行 hmac,请求登陆接口
        network.login(account, password.hmac(salt))
    }
    else {
        //请求 getSalt 接口,请求参数为账户+应用标识
        network.getSalt(account + bundleId, { salt in
            //将盐保存在本地,再次调用自身。
            savaSalt(salt)
            login(account, password)
        })
    }
}

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

/*获取 get 请求传递的参数*/
$account = $_GET['account'];
$password = $_GET['password'];  //123456
$bundle_id = $_GET['bundleId'];

/*创建数据连接*/
$db = new DataBase();

/*制作一个随机的盐*/
$salt = salt();

/*检查用户名是否存在*/
$is_exist = $db->check_user_exist($account);

if ($is_exist) {
    echo response(10001, false);
}
else {
    /*将密码进行 hmac 加密*/
    $password = str_hmac($password,  $salt);

    /*检查用户名是否添加成功*/
    $result = $db->add_user($account, $password);

    if ($result) {

        /*检查秘钥是否保存成功*/
        $save_salt = $db->save_salt($salt, $account, $bundle_id);

        if ($save_salt) {
            $data = ['salt'=>$salt];
            echo response(0, $data);
        }
        else {
            echo response(20001, false);
        }
    }
    else {
        echo response(20001, false);
    }
}

При этом нам нужно создать getsaltИнтерфейс:

/*获取 get 请求传递的参数*/
$account = $_GET['account'];
$bundle_id = $_GET['bundleId'];

/*创建数据连接*/
$db = new DataBase();

/*获取秘钥*/
$salt = $db->get_salt($account, $bundle_id);

if ($salt == '') {
    echo response(40001, false);
}
else {
    $data = ['salt'=>$salt];
    echo response(0, $data);
}

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

блокировка устройства

Некоторые приложения имеют функцию блокировки устройства, например QQ. Эта функция предназначена для привязки учетной записи к устройству. Если другие люди знают учетную запись пользователя и пароль, но устройство не совпадает, они не могут войти в систему. Как добиться Это?

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

Повышение безопасности для отдельных пользователей

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

  1. возвращается при регистрацииsaltУтечка может произойти при захвате пакета;
  2. После замены устройства полученныйsaltУтечка может произойти при захвате пакета;
  3. сохранено локальноsalt, его можно получить через путь к файлу;
  4. Даже если человек, перехватывающий пакет, не знает пароля, он может войти через символы, зашифрованные hmac;

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

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

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

Поэтому нам нужен динамический сид и время без связи между сервером и клиентом.

HMAC+время

Как используется это динамическое начальное число?

  1. Клиент отправляет запрос на регистрацию, а сервер возвращаетsalt, сохраните пароль после hmac;
  2. сохранение клиентаsalt;
  3. Клиент отправляет запрос на вход, параметром является пароль после hmac плюс текущее время;
  4. Сервер получает запрос на вход и сравнивает пароль в базе данных с текущим временем;

Код клиента:

// 秘钥
const salt = ''
// 当前时间,精确到分钟
const currentTime = '201709171204'
// 用户输入的密码
let password = '123456'
// (hmac+currentTime).md5
password = (password.hmac(salt) + currentTime).md5()
network('login', {method: 'GET', params: {password:password}})

Код сервера:

function should_login($account, $password)
{
    $account = mysqli_real_escape_string($this->connection ,$account);
    $password = mysqli_real_escape_string($this->connection, $password);
    $user = $this->get_user($account);
    if ($user == null) {
        return false;
    }
    $password_local = $user['password'];
    if ($password_local == '') {
        return false;
    }
    $password_local = md5($password_local.current_time());
    if ($password_local == $password) {
        return true;
    }
    else {
        return false;
    }
}

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

Умный, ты должен быть в состоянии думать, что этоРеализация срока действия проверочного кода в течении 5 минут.

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