Как я должен реализовать «аутентификацию на основе токенов» для набора веб-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, а затем создания маркера безопасности, проверки правильности токена безопасности для текущего пользователя и т. Д., Это было бы действительно здорово для меня.

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

NB: – Пожалуйста, не предлагайте мне использовать процедуру аутентификации 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 

Я прав?

Это можно сделать так.

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

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

  Request: GET /v1/products/coffee Private key: ww9k6fcysu30sbuzu7ez57z2kzvefyxwosrjcnwo 

Затем я создавал бы такой ключ запроса;

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

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

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

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

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

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

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

Основываясь на нашем обсуждении, вы можете сделать что-то похожее на 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 / whatever):

GET / resource HTTP / 1.1

  Host: server.example.com Authorization: Bearer mF_9.B5f-4.1JqM 

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

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

Очевидно, что они не должны быть разными серверами. Вы можете реализовать его, как пожелаете.