Как я должен реализовать «Аутентификацию на основе токенов» для набора веб-API безопасным способом, используя PHP и MySQL (без использования OAuth)?

Я разработал несколько веб-API на PHP, используя Slim Framework, который используется мобильными приложениями (iOS и Android) для обработки их запросов и получения необходимых данных.

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

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

После реализации «Аутентификации на основе токенов» должен быть следующий процесс:

  1. Когда пользователь впервые входит в систему, отправляя имя пользователя и пароль в запросе API входа в систему, один токен безопасности будет генерироваться после успешной проверки комбинации имени пользователя и пароля для этого конкретного пользователя. Этот токен безопасности также будет сохранен в некоторой таблице базы данных MySQL вместе с именем пользователя / паролем / некоторым хешированным значением для идентификации пользователя в дальнейшей обработке. Если проверка не пройдена, маркер безопасности не должен генерироваться, и пользователь также не должен входить в систему.

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

  3. Если по какому-либо запросу был отправлен недействительный токен, то в ответ должно быть отправлено сообщение «Пожалуйста, войдите в систему», и ресурс сайта не должен получить доступ.

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

С тех пор, как я работаю над «Аутентификацией на основе токенов» впервые в своей карьере, я могу ошибаться в своем вышеописанном подходе. Если я делаю что-то не так, исправьте мои ошибки.

Я нашел следующие ссылки, но те, которые я не нашел много полезного, так как им не хватает примера рабочего кода с пошаговым описанием:

PHP HMAC Restful API, который использует платформу Phalcon Micro

Пакет PEAR для JWT

Если бы вы могли предоставить мне весь код, который состоит из создания таблицы базы данных, соединения между PHP и MySQL, затем создания токена безопасности, проверки действительности токена безопасности для текущего вошедшего в систему пользователя и т. Д., Это было бы действительно здорово для меня.

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

N.B. :- Пожалуйста, не предлагайте мне использовать процесс аутентификации OAuth.

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

Для создания токена я использую эту функцию, которая принимает в качестве параметров данные пользователя

define('SECRET_KEY', "fakesecretkey");

function createToken($data)
{
/* Create a part of token using secretKey and other stuff */
$tokenGeneric = SECRET_KEY.$_SERVER["SERVER_NAME"]; // It can be 'stronger' of course

/* Encoding token */
$token = hash('sha256', $tokenGeneric.$data);

return array('token' => $token, 'userData' => $data);
}

Таким образом, пользователь может аутентифицировать себя и получить массив, который содержит токен (genericPart + его данные, закодированные) и его данные не закодированы:

function auth($login, $password)
{
// we check user. For instance, it's ok, and we get his ID and his role.
$userID = 1;
$userRole = "admin";

// Concatenating data with TIME
$data = time()."_".$userID."-".$userRole;
$token = createToken($data);
echo json_encode($token);
}

Затем пользователь может отправить мне свой токен + свои некодированные данные, чтобы проверить:

define('VALIDITY_TIME', 3600);

function checkToken($receivedToken, $receivedData)
{
/* Recreate the generic part of token using secretKey and other stuff */
$tokenGeneric = SECRET_KEY.$_SERVER["SERVER_NAME"];

// We create a token which should match
$token = hash('sha256', $tokenGeneric.$receivedData);

// We check if token is ok !
if ($receivedToken != $token)
{
echo 'wrong Token !';
return false;
}

list($tokenDate, $userData) = explode("_", $receivedData);
// here we compare tokenDate with current time using VALIDITY_TIME to check if the token is expired
// if token expired we return false

// otherwise it's ok and we return a new token
return createToken(time()."#".$userData);
}

$check = checkToken($_GET['token'], $_GET['data']);
if ($check !== false)
echo json_encode(array("secureData" => "Oo")); // And we add the new token for the next request

Я прав?

4

Решение

Основываясь на нашем обсуждении, вы можете сделать что-то похожее на OAuth2.0. Я бы порекомендовал реализовать полную спецификацию, но, поскольку это ваше приложение, вы можете внести изменения.

Вот график из RFC 6750

+--------+                               +---------------+
|        |--(A)- Authorization Request ->|   Resource    |
|        |                               |     Owner     |
|        |<-(B)-- Authorization Grant ---|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(C)-- Authorization Grant -->| Authorization |
| Client |                               |     Server    |
|        |<-(D)----- Access Token -------|  (Slim API)   |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(E)----- Access Token ------>|    Resource   |
|        |                               |     Server    |
|        |<-(F)--- Protected Resource ---|               |
+--------+                               +---------------+

В Slim у вас может быть всего три конечных точки:

POST имя пользователя / пароль:

/ OAuth / v1 / Authenticate /

возвращается { token: foo }

GET где {токен} — ваш уникальный токен

/ OAuth / v1 / маркер / {маркер}

возвращается { username: joe, permissions['page:admin','users:full'], expires: 123456}

УДАЛИТЬ передать {токен}

/ OAuth / v1 / маркер / отменить

отвечает с 200 OK и пустое тело.

Теперь, как это работает:

  • Когда они авторизуются, просто возвращайте объект JSON с вашим token, который клиент хранит в виде файла cookie.
  • Клиент передает это вашему серверу ресурсов в заголовке, как указано в RFC раздел 2.1 (который ищет пользовательские разрешения в базе данных / nosql / что угодно):

GET / ресурс HTTP / 1.1

 Host: server.example.com

Authorization: Bearer mF_9.B5f-4.1JqM

Ваш ресурсный сервер связывается с Slim API на серверной части, чтобы определить ваши разрешения. Затем сервер решает, что вам разрешено видеть.

Если вам не нравится отправлять его в качестве заголовка, см. раздел 2.2, который описывает, как отправить его в теле, или раздел 2.3 который отправляет его как запрос URI.

Это, очевидно, не должны быть разные серверы. Вы можете реализовать это как хотите.

1

Другие решения

Вы могли бы сделать это таким образом.

У каждого пользователя есть закрытый ключ (случайный буквенно-цифровой Икс длинная строка) — которая уникальна для каждого пользователя.

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

 Request:
GET /v1/products/coffee

Private key:
ww9k6fcysu30sbuzu7ez57z2kzvefyxwosrjcnwo

Затем я бы сгенерировал ключ запроса как;

hash_hmac("sha1", "GET /v1/products/coffee", "ww9k6fcysu30sbuzu7ez57z2kzvefyxwosrjcnwo");

Это дало бы мне ключ запроса: 45751dce6ef93655a71e7b82a6179591c346c2c1 за это GET только запрос Это также гарантирует, что клиент предназначен для этой конечной точки и не был подделан.

На приемном конце вы бы сделали то же самое hash_hmac Подпрограмма с закрытым ключом пользователя (например, в запросе необходимо указать свое имя пользователя — например, чтобы выполнить поиск для извлечения закрытого ключа) и сравнить два результата.

hash_hmac("sha1", $_SERVER['REQUEST_METHOD'] . " " . $_SERVER['REDIRECT_URL'], $user_private_key);

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

$bodyhash = md5(implode(",", $_POST));

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

0

По вопросам рекламы [email protected]