JWT — как безопасный способ аутентификации и передачи данных — Разработка на

Что такое веб-токены json?

JSON Web Tokens (JWT) были введены в качестве метода безопасного обмена данными между двумя сторонами. Он был представлен в спецификации RFC 7519, разработанной рабочей группой инженеров Интернета (IETF).

Что такое jwt?

Если обратиться с этим запросом в Google, вероятнее всего, он выдаст что-то подобное:

… способ представления передаваемых данных …… объект JSON, который определен в RFC 7519 как безопасный способ обмена информацией между двумя сторонами …… открытый стандарт, который определяет компактный и автономный способ безопасной передачи информации …

Все эти определения верны, но они звучат чересчур научно и абстрактно. Попробуем дать JWT собственное описание.

Веб-токен JSON, или JWT (произносится “jot”), представляет собой стандартизированный, в некоторых случаях подписанный и/или зашифрованный формат упаковки данных, который используется для безопасной передачи информации между двумя сторонами.

Проанализируем эту формулировку.

Введение

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

Вам не обязательно иметь опыт работы с JSON Web Tokens (JWT), поскольку мы будем говорить об этом с нуля.

Для раздела, посвященного реализации, будет предпочтительнее, если у вас есть опыт работы с Express, Javascript ES6 и REST-клиентами.

1.0 / 2022-03-23

  • Drop support for PHP 5.3, 5.4, 5.5, 5.6, and 7.0
  • Add parameter typing and return types where possible

Authentication flow 🔑

JSON Server Auth adds a simple JWT based authentication flow.

Create and validate jwts from scratch with php

We’ll start a new PHP project by creating a /src directory and a simple composer.json file with just one dependency (for now): the DotEnv library which will allow us to keep our secret key in a .env file outside our code repository:

composer.json

We’ve also configured a PSR-4 autoloader which will automatically look for PHP classes in the /src directory.

We can install our dependencies now:

We have a /vendor directory, and the DotEnv dependency is installed (we can also use our autoloader to load our classes from /src with no include() calls).

Let’s create a .gitignore file for our project with two lines in it, so the /vendor directory and our local .env file will be ignored:

.gitignore

Next, we’ll create a .env.example file with one variable: SECRET to hold our secret key (used when generating and verifying JWTs):

.env.example

and we’ll copy .env.example to .env where we’ll fill in our actual secret key (it will be ignored by Git so it won’t end up in our repository).

We’ll need a bootstrap.php file which loads our environment variables (later it will also do some additional bootstrapping for our project).

bootstrap.php

Let’s create a simple tool generate_key.php which will generate a secret key for us, so we can put it in the .env file:

generate_key.php

If you run it in the command line, you should get an output like this:

Follow the instructions and add your secret key to the .env file (don’t worry, the key in the example above is not used anywhere).

Next we’ll build a tool to generate example JWTs (with a hardcoded payload that you can modify as you wish). First we’ll add a base64UrlEncode() function to our bootstrap.php file:

bootstrap.php

Here’s the generate_jwt.php tool:

generate_jwt.php

You can run the tool from the command line to get an output like this:

Example

useFirebaseJWTJWT;
useFirebaseJWTKey;

$key = 'example_key';
$payload = [
    'iss' => 'http://example.org',
    'aud' => 'http://example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000
];

/** * IMPORTANT: * You must specify supported algorithms for your application. See * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40 * for a list of spec-compliant algorithms. */$jwt = JWT::encode($payload, $key, 'HS256');
$decoded = JWT::decode($jwt, newKey($key, 'HS256'));

print_r($decoded);

/* NOTE: This will now be an object instead of an associative array. To get an associative array, you will need to cast it as such:*/$decoded_array = (array) $decoded;

/** * You can add a leeway to account for when there is a clock skew times between * the signing and verifying servers. It is recommended that this leeway should * not be bigger than a few minutes. * * Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef */JWT::$leeway = 60; // $leeway in seconds$decoded = JWT::decode($jwt, newKey($key, 'HS256'));

Example with a passphrase

useFirebaseJWTJWT;
useFirebaseJWTKey;

// Your passphrase$passphrase = '[YOUR_PASSPHRASE]';

// Your private key file with passphrase// Can be generated with "ssh-keygen -t rsa -m pem"$privateKeyFile = '/path/to/key-with-passphrase.pem';

// Create a private key of type "resource"$privateKey = openssl_pkey_get_private(
    file_get_contents($privateKeyFile),
    $passphrase
);

$payload = [
    'iss' => 'example.org',
    'aud' => 'example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000
];

$jwt = JWT::encode($payload, $privateKey, 'RS256');
echo "Encode:n" . print_r($jwt, true) . "n";

// Get public key from the private key, or pull from from a file.$publicKey = openssl_pkey_get_details($privateKey)['key'];

$decoded = JWT::decode($jwt, newKey($publicKey, 'RS256'));
echo "Decode:n" . print_r((array) $decoded, true) . "n";

Example with eddsa (libsodium and ed25519 signature)

useFirebaseJWTJWT;
useFirebaseJWTKey;

// Public and private keys are expected to be Base64 encoded. The last// non-empty line is used so that keys can be generated with// sodium_crypto_sign_keypair(). The secret keys generated by other tools may// need to be adjusted to match the input expected by libsodium.$keyPair = sodium_crypto_sign_keypair();

$privateKey = base64_encode(sodium_crypto_sign_secretkey($keyPair));

$publicKey = base64_encode(sodium_crypto_sign_publickey($keyPair));

$payload = [
    'iss' => 'example.org',
    'aud' => 'example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000
];

$jwt = JWT::encode($payload, $privateKey, 'EdDSA');
echo "Encode:n" . print_r($jwt, true) . "n";

$decoded = JWT::decode($jwt, newKey($publicKey, 'EdDSA'));
echo "Decode:n" . print_r((array) $decoded, true) . "n";

Example with rs256 (openssl)

useFirebaseJWTJWT;
useFirebaseJWTKey;

$privateKey = <<<EOD-----BEGIN RSA PRIVATE KEY-----MIICXAIBAAKBgQC8kGa1pSjbSYZVebtTRBLxBz5H4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3 3iuIYW4VQAzyqFpwuzjkDI 17t5t0tyazyZ8JXw KgXTxldMPEL95 qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4ehde/zUxo6UvS7UrBQIDAQABAoGAb/MXV46XxCFRxNuB8LyAtmLDgi/xRnTAlMHjSACddwkyKem8//8eZtw9fzxzbWZ/1/doQOuHBGYZU8aDzzj59FZ78dyzNFoF91hbvZKkg 6wGyd/LrGVEB Xre0JNil0GReM2AHDNZUYRv HYJPIOrB0CRczLQsgFJ8K6aAD6F0CQQDzbpjYdx10qgK1cP59UHiHjPZYC0loEsk7s hUmT3QHerAQJMZWC11Qrn2N ybwwNblDKv s5qgMQ55tNoQ9IfAkEAxkyffU6ythpg/H0Ixe1I2rd0GbF05biIzO/i77Det3n4YsJVlDckZkcvY3SK2iRIL4c9yY6hlIhs K9wXTtGWwJBAO9Dskl48mO7woPR9uD22jDpNSwek90OMepTjzSvlhjbfuPN1IdhqvSJTDychRwn1kIJ7LQZgQ8fVz9OCFZ/6qMCQGObqaGwHmUK6xzpUbbacnYrIM6nLSkXgOAwv7XXCojvY614ILTK3iXiLBOxPu5Eu13keUz9sHyD6vkgZzjtxXECQAkp4Xerf5TGfQXGXhxIX52yH N2LtujCdkQZjXAsGdmB2zNzvrlgRmgBrklMTrMYgm1NPcW bRLGcwgW2PTvNM=-----END RSA PRIVATE KEY-----EOD;

$publicKey = <<<EOD-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3 3iuIYW4VQAzyqFpwuzjkDI 17t5t0tyazyZ8JXw KgXTxldMPEL95 qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4ehde/zUxo6UvS7UrBQIDAQAB-----END PUBLIC KEY-----EOD;

$payload = [
    'iss' => 'example.org',
    'aud' => 'example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000
];

$jwt = JWT::encode($payload, $privateKey, 'RS256');
echo "Encode:n" . print_r($jwt, true) . "n";

$decoded = JWT::decode($jwt, newKey($publicKey, 'RS256'));

/* NOTE: This will now be an object instead of an associative array. To get an associative array, you will need to cast it as such:*/$decoded_array = (array) $decoded;
echo "Decode:n" . print_r($decoded_array, true) . "n";

Guarded routes 🚥

Guarded routes exist at the root and can restrict access to any resource you put after them :

How jwt verifier works

The Okta JWT Verifier can be installed through composer:

The library requires a JWT library and a PSR-7 compliant library. You can install an existing one like this:

Alternatively, you can also provide your own implementation. To create your own adaptor, just implement the Okta/JwtVerifier/Adaptors/Adaptor in your own class.

Installation

Use composer to manage your dependencies and download PHP-JWT:

composer require firebase/php-jwt

Optionally, install the paragonie/sodium_compat package from composer if your
php is < 7.2 or does not have libsodium installed:

composer require paragonie/sodium_compat

Jwt payload 📇

The access token has the following claims :

JSON Server Auth provides generic guards as route middlewares.

To handle common use cases, JSON Server Auth draws inspiration from Unix filesystem permissions, especialy the numeric notation.

  • We add 4 for read permission.
  • We add 2 for write permission.

Of course CRUD is not a filesystem, so we don’t add 1 for execute permission.

Learn more about php, jwts, and secure authentication

You can find the whole code example here: GitHub link

If you would like to dig deeper into the topics covered in this article, the following resources are a great starting point:

License

3-Clause BSD.

Module usage 🔩

If you go the programmatic way and use JSON Server as a module, there is an extra step to properly integrate JSON Server Auth :

⚠️ You must bind the router property db to the created app, like the JSON Server CLI does, and you must apply the middlewares in a specific order.

const jsonServer = require('json-server')
const auth = require('json-server-auth')

const app = jsonServer.create()
const router = jsonServer.router('db.json')


app.db = router.db


app.use(auth)
app.use(router)
app.listen(3000)

New lines in private keys

If your private key contains n characters, be sure to wrap it in double quotes “”
and not single quotes ” in order to properly interpret the escaped characters.

Other properties

Any other property can be added to the request body without being validated :

Payload или полезные данные

Вторым блоком идет eyJ1c2VyX2lkIjoxLCJleHAiOjE1ODEzNTcwMzl9

Это есть полезные данные, так же закодированные в Base64. После раскодирования получим:

Permisssions rewriter

The custom rewriter is accessible via a subproperty :

Refresh token

Основной токен, про который шла речь выше, обычно имеет короткий срок жизни – 15-30 минут. Больше давать не стоит.

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

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

У него, обычно, нет какой-то структуры и это может быть некая случайная строка.

Setup permissions 💡

Of course, you don’t want to directly use guarded routes in your requests.
We can take advantage of JSON Server custom routes feature to setup resource permissions ahead.

Create a routes.json file :

Tests

Run the tests using phpunit:

$ pear install PHPUnit
$ phpunit --configuration phpunit.xml.dist
PHPUnit 3.7.10 by Sebastian Bergmann.
.....
Time: 0 seconds, Memory: 2.50Mb
OK (5 tests, 5 assertions)

The structure of a jwt

Let’s get down to the nitty-gritty details of handling JWTs now. The definition:

“A JSON Web Token (JWT) is a JSON object that is defined in RFC 7519 as a safe way to represent a set of information between two parties. The token is composed of a header, a payload, and a signature.”

So a JWT is just a string in this format:

The header component of the JWT contains information about how the JWT signature should be computed.

Use jwts for access tokens in php

Okta uses JWT access tokens for its implementation of Oauth 2.0. They are signed using private JSON Web Keys (JWK).

The high-level overview of validating an access token looks like this:

  • Retrieve and parse your Okta JSON Web Keys (JWK), which should be checked periodically and cached by your application
  • Decode the access token, which is in JSON Web Token format
  • Verify the signature used to sign the access token
  • Verify the claims found inside the access token

Okta provides a library (Okta JWT Verifier) for PHP which can be integrated into any PHP project to provide seamless verification of Okta access tokens.

Using cached key sets

The CachedKeySet class can be used to fetch and cache JWKS (JSON Web Key Sets) from a public URI.
This has the following advantages:

  1. The results are cached for performance.
  2. If an unrecognized key is requested, the cache is refreshed, to accomodate for key rotation.
  3. If rate limiting is enabled, the JWKS URI will not make more than 10 requests a second.

Десериализованная форма

В несериализованном виде JWT состоит из заголовка и полезной нагрузки, которые являются обычными JSON-объектами.

Заголовок

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 – это первая часть токена – есть заголовок. Она закодирована в Base64 и если её раскодировать, получим строку:

Зачем 2 токена?

Представим ситуацию, когда у нас каким-то образом украли Access токен. Да, это уже плохо и где-то у нас брешь в безопасности. Злоумышленник в этом случае сможет им воспользоваться не более чем на 15-30 минут. После чего токен “протухнет” и перестанет быть актуальным. Ведь нужен второй токен для продления.

Если украли Refresh токен, то без Access токена (который недоступен в JS) продлить ничего нельзя и он оказывается просто бесполезным.

Защита веб-токенов

Как уже упоминалось выше, JWT использует два механизма для защиты информации: подписи и шифрование. Их описывают стандарты безопасности JSON Web Signature (JWS) и JSON Web Encryption (JWE).

Использование jwt с express

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

Будет два типа пользователей – администраторы и участники .
Администраторы смогут просматривать и добавлять новые книги, а участники

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

Для начала в вашем терминале инициализируйте пустой проект Node.js с
настройками по умолчанию:

 $ npm init -y 

Затем давайте установим фреймворк Express:

 $ npm install --save express 

Использование библиотек

Разумеется, JSON токены не кодируются вручную. Существует множество библиотек, предназначенных для этого. Например, jsonwebtoken:

const jwt = require('jsonwebtoken');
const secret = 'shhhhh';
// шифрование
const token = jwt.sign({ foo: 'bar' }, secret);
// проверка и расшифровка
const decoded = jwt.verify(token, secret);
console.log(decoded.foo) // bar

Этот код создает подписанный JWT с использованием секретного слова. Затем он проверяет подлинность токена и декодирует его, применяя тот же секрет. Подпись и другие механизмы безопасности будут разобраны далее.

Неподписанные json токены

Заголовок описывает криптографические операции, которые применяются к веб-токену. Но в некоторых случаях подпись и шифрование могут отсутствовать. Обычно это происходит, когда JWT является частью некоторой уже зашифрованной структуры данных. В заголовке такого неподписанного токена заявка alg должна быть равна none:

{
   "alg": "none"
}

Подпись jwt

Цель подписи заключается в том, чтобы дать возможность одной или нескольким сторонам установить подлинность токена. Помните пример подделки идентификатора пользователя из cookie для получения доступа к чужой учетной записи? Токен можно подписать, чтобы проверить, не были ли изменены данные, содержащиеся в нем.

Самый распространенный алгоритм подписи – HMAC. Он объединяет полезную нагрузку с секретом, используя криптографическую хеш-функцию (чаще всего SHA-256). С помощью полученной уникальной подписи можно верифицировать данные. Это схема называется разделением секрета, поскольку он известен обеим сторонам: создателю и получателю. Таким образом, и тот, и другой могут генерировать новое подписанное сообщение.

Другой алгоритм подписи – RSASSA. В отличие от HMAC он позволяет принимающей стороне только проверять подлинность сообщения, но не генерировать его. Алгоритм основан на схеме открытого и закрытого ключей. Закрытый ключ может использоваться как для создания подписанного сообщения, так и для проверки.

Открытый ключ, напротив, позволяет лишь проверить подлинность. Это важно во многих сценариях подписки, таких как Single-Sign On, где есть только один создатель сообщения и много получателей. Таким образом, никакой злонамеренный потребитель данных не сможет их изменить.

Полезная нагрузка

Полезные данные – часть токена, в которой размещается вся необходимая пользовательская информация. Как и заголовок, полезная нагрузка представляет собой обычный объект JSON. Здесь ни одно поле не является обязательным. Обычно используются уже рассмотренные служебные заявки iss, sub и aud, а также специфические для приложения данные. Например, вот так выглядит JWT во фреймворке OpenID:

Постскриптум

В своей реализации Refresh токена использовал общую длину 24 знака. Первые 6 знаков – это дата его “протухания”, следующие 12 знаков – случайно сгенерированные данные. И в конце 6 знаков – это часть Access токена последней части сигнатуры.

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

Дата содержит год, месяц, день, час и минуты. Хранится в ASCII

Кодирование даты на Golang:

// приводим к целочисленному числу uint32. Итого 32 бита.
// расчет простой: год 12 бит, месяц 4 бита, день 5 бит и т.д. Таким образом в аккурат умещаемся в 32 бита или 4 байта.
date := uint32(year<<20) | uint32(month<<16) | uint32(day<<11) | uint32(hour<<6) | uint32(minute)

// в цикле кодируем байты в ASCII. 1 знак это шесть бит. Итого и получаем шесть знаков даты по таблице ASCII – печатные знаки.
for n := 0; n < 6; n {
b6Bit = byte(date>>i) & 0x3F
sBuilder.WriteByte(byte8bitToASCII(b6Bit))

}

Всю реализацию на Go можно изучить на Github-е

Преимущество использования jwt перед традиционными методами

Как мы обсуждали ранее, JWT может содержать всю информацию о самом
пользователе, в отличие от аутентификации на основе сеанса.

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

<img src="https://rukovodstvo.net/data:image/svg xml,” loading=”lazy” alt=”web_application_architecture”>

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

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

Приложения

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

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

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

Проверка токена

Для проверка токена необходимо проделать ту же операцию.

Берем склейку заголовок данные, кодируем с помощью алгоритма HMAC-SHA256 и нашего приватного ключа. А далее берем сигнатуру с токена и сверяем с результатом кодирования. Если результаты совпадают – значит данные подтверждены и можно быть уверенным, что они не были подменены.

Сервис книг

После этого давайте создадим файл books.js для нашего сервиса книг.

Начнем с импорта необходимых библиотек и настройки приложения Express:

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

const books = [
    {
        "author": "Chinua Achebe",
        "country": "Nigeria",
        "language": "English",
        "pages": 209,
        "title": "Things Fall Apart",
        "year": 1958
    },
    {
        "author": "Hans Christian Andersen",
        "country": "Denmark",
        "language": "Danish",
        "pages": 784,
        "title": "Fairy tales",
        "year": 1836
    },
    {
        "author": "Dante Alighieri",
        "country": "Italy",
        "language": "Italian",
        "pages": 928,
        "title": "The Divine Comedy",
        "year": 1315
    },
];

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

Поскольку наши книги должны быть видны только аутентифицированным пользователям. Мы должны создать промежуточное ПО для аутентификации.

Перед этим создайте секрет маркера доступа для подписания JWT, как и раньше:

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

На этом этапе давайте создадим промежуточное ПО Express, которое будет обрабатывать процесс аутентификации:

В этом промежуточном ПО мы считываем значение заголовка authorization. Поскольку заголовок авторизации имеет значение в формате Bearer [JWT_TOKEN], мы разделили значение пробелом и выделили токен.

Затем мы проверили токен с помощью JWT. После проверки мы присоединяем объект пользователя к запросу и продолжаем. В противном случае мы отправим клиенту ошибку.

Мы можем настроить это промежуточное ПО в обработчике запроса GET следующим образом:

Давайте загрузим сервер и проверим, все ли работает правильно:

Сериализованные json токены

JSON web token в сериализованной форме – это строка следующего формата:

[ Header ].[ Payload ].[ Signature ]

Заголовок (header) и полезная нагрузка (payload) присутствуют всегда, а вот подпись (signature) может отсутствовать.

Пример компактной формы:

eyJhbGciOiJub25lIn0.eyJzdWIiOiJ1c2VyMTIzIiwicHJvZHVjdElkcyI6WzEsMl19.

Она получена из следующих данных:

Служба аутентификации

Затем давайте создадим файл auth.js , который будет нашей службой
аутентификации:

 const express = require('express'); 
 const app = express(); 
 
 app.listen(3000, () => { 
 console.log('Authentication service started on port 3000'); 
 }); 

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

Структура jwt

Давайте поговорим о структуре JWT на примере токена:

<img src="https://rukovodstvo.net/data:image/svg xml,” loading=”lazy” alt=”sample_json_web_token_jwt”>

Как вы можете видеть на изображении, JWT состоит из трех частей, каждая
из которых разделена точкой.

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

Первый раздел JWT – это заголовок, представляющий собой строку в
кодировке Base64. Если вы расшифруете заголовок, он будет выглядеть
примерно так:

 { 
 "alg": "HS256", 
 "typ": "JWT" 
 } 

Раздел заголовка содержит алгоритм хеширования, который использовался
для генерации знака и типа токена.

Второй раздел – это полезная нагрузка, содержащая объект JSON, который
был отправлен обратно пользователю. Поскольку он закодирован только в
Base64, любой может легко его декодировать.

Рекомендуется не включать в JWT какие-либо конфиденциальные данные,
такие как пароли или личную информацию.

Обычно тело JWT выглядит примерно так, хотя это необязательно:

 { 
 "sub": "1234567890", 
 "name": "John Doe", 
 "iat": 1516239022 
 } 

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

Вы также можете увидеть некоторые общие свойства, такие как eat или
exp , который является сроком действия токена.

Последний раздел – это подпись токена. Это создается путем хеширования
строки
base64UrlEncode(header) “.” base64UrlEncode(payload) secret с
использованием алгоритма, упомянутого в разделе заголовка.

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

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

Формат упаковки данных

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

Шифрование

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

Зашифрованный JWT известен как JWE (JSON Web Encryption). В отличие от JWS, его компактная форма имеет 5 сегментов, разделенных точкой. Дополнительно к зашифрованному заголовку и полезной нагрузке он включает в себя зашифрованный ключ, вектор инициализации и тег аутентификации.

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

Схема разделенного секрета аналогична механизму подписки. Все стороны знают секрет и могут шифровать и дешифровать токен.

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

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

Перевод статьи Max NgWizard K: A plain English introduction to JSON web tokens (JWT): what it is and what it isn’t.

📢 but wait !

As a convenience, json-server-auth CLI allows you to define permissions in a more succinct way :

Заключение

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

Как всегда, исходный код доступен на GitHub.

Похожее:  ТЕЛЕ 2 ЛИЧНЫЙ КАБИНЕТ ВХОД ПО НОМЕРУ ТЕЛЕФОНА УЛЬЯНОВСК

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *