Токен безопасности JWT

внешний интерфейс сервер Безопасность koa

JWT

Некоторое время назад я имел дело с лотереей H5.Во время теста я думал, что если пользователь поймал интерфейс лотереи, такой как

https:xxx/lottery/userinfo

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

После этого решение состоит в том, чтобы определить, содержит ли файл cookie интерфейса информацию о параметрах, такую ​​как userInfo.

Но есть и другой способ справиться с этим — JWT.

Что такое JWT (JSON WEB TOKEN)

JWT — это метод безопасной передачи информации между двумя взаимодействующими сторонами в виде объектов JSON.

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

JWT состоит из трех частей

голова

head

  • type тип объявления

  • alg объявляет алгоритм шифрования

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

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

payload

Полезная нагрузка — это место, где хранится достоверная информация.

payload

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

параметр имея в виду
iat Время выпуска jwt
exp Время истечения jwt, это время истечения должно быть больше, чем время выдачи
nbf Определите, до какого времени jwt недоступен

Например, чтобы определить полезную нагрузку

{
  "exp": Math.floor(Date.now() / 1000) + (60 * 60),
  "name": "John Doe"
}

Полезная нагрузка будет закодирована в base64 для формирования второй части JWT.

виза

签证

Как видите, визовая часть состоит из трех частей.

параметр имея в виду
base64UrlEncode Заголовок с шифрованием Base64
base64UrlEncode Полезная нагрузка с шифрованием Base64
your-256-bit-secret Пользовательский зашифрованный секрет

Секрет эквивалентен закрытому ключу и не может быть раскрыт. Если клиент может получить секрет, он может самостоятельно выпустить JWT.

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload)
var signature = HMACSHA256(encodedString, 'secret')

подпись является третьей частью JWT

Объединение трех вышеперечисленных частей вместе является окончательным JWT.

сторонняя библиотека

jsonwebtoken

Если вы создаете jwt самостоятельно, это немного сложно. Для поддержки JWT разработано множество сторонних библиотек. например, jsonwebtoken

jsonwebtoken

  • знак используется для генерации токена

  • Verify используется для проверки токена.

koa-jwt

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

Создайте простой сервер, чтобы увидеть эффект

Git-адрес проекта

app.use(
  jwtKoa({secret: SECRET})
  .unless({
    path: [/\/login/] // 不需要通过jwt验证的请求路径
  })
)
router.get('/login', async (ctx) => {
  let token = jwt.sign({
    name: 'dva'
  }, SECRET)
  console.log(token, 'token')
  ctx.body = {
    token
  }
})

router.get('/try', async (ctx) => {
  let token = ctx.header.authorization
  let result = jwt.verify(token, SECRET)
  ctx.body = {
    result
  }
})

  • /логин, чтобы получить токен
// {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZHZhIiwiaWF0IjoxNTMxMjgwMDg2fQ.Rh_vAKeytjAL2TbOk-MmXQWFesszjRU3Bzldrx5x17s"}%
  • Если токен не добавлен, он будет перехвачен koa-jwt

wrong

  • добавить токен

token

Рабочий механизм

工作机制

Картинка взята из статьи«Аутентификация пользователя JWT с разделением интерфейса и сервера»

  • Войдите, чтобы получить JWT

  • Внешний интерфейс инициирует запрос, и JWT монтируется в заголовке.

Боевой проект

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

Адрес проекта GIT

серверная часть

Построить два интерфейса входа, лотереи

server

  • логин используется для генерации JWT и возврата его во внешний интерфейс
    token = jwt.sign({
        name: 'who',
        exp: Math.floor(Date.now() / 1000) + (60 * 60), // 设置 token 过期时间
      }, SECRET)
  • лотерея используется для проверки JWT, и если проверка пройдена, будет выполнено действие лотереи
    let token = this.headers.authorization
      // 解码
      let decoded = jwt.verify(token, SECRET)
      // console.log(decoded, 'decoded')
      let {name} = decoded

      if (name != 'who') {
        code = 403
        return
      }

внешний интерфейс

  • После нажатия кнопки «Войти» на главной странице информация о возвращаемом токене сохраняется.

login

После того, как я получил токен здесь, я записал его в localStorage

 window.localStorage.setItem('token', token)
  • Войдите на страницу лотереи, чтобы разыграть лотерею, и устанавливайте авторизацию каждый раз, когда вы запрашиваете

Authorization

    axios.get(`/${APP_NAME}/win`, {
      headers: {
        Authorization: token
      }
    })

Если будет передан неверный токен, во время проверки JWT на стороне сервера будет сообщено об ошибке.

error

error

Интернет-адрес проекта

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

Other

Вопрос ответ

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

Вопрос 1: Обработка заголовка base64 и полезной нагрузки

Фактически, когда JWT обрабатывает заголовок и полезную нагрузку, он просто выполняет кодирование Base64. Если вы получаете определенный токен, его легко расшифровать.

const base64url = require('base64url')
let header = {
  'typ': 'JWT',
  'alg': 'HS256'
}

let resultH = base64url(JSON.stringify(header))
console.log(resultH, 'resultH')

let payload = {
  name: 'dva',
  exp: 1531410000
}

let result = base64url(JSON.stringify(payload))
console.log(result, 'result')

// 解码payload
let isP = 'eyJuYW1lIjoiZHZhIiwiZXhwIjoxNTMxNDEwMDAwfQ'
let getP = base64url.decode(isP)
console.log(getP, 'getP')

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

Вопрос 2 Как продлить JWT

После ознакомления с информацией делается вывод, что обновление и обновление jwt на данный момент имеет следующие методы обработки.Основной принцип заключается в том, что в определенный момент времени сервер выдает новый токен

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

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

token

Верните его после получения нового токена. Когда вы запустили лотерею, установите этот последний токен

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

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

Проблема 3 Обработка выхода из системы

Можно сказать, что более важным вопросом токена является его отмена.

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

remove

Но на самом деле токен все еще действителен. Если пользователь сохраняет значение токена, то после нажатия кнопки «Выход» значение токена можно использовать. Это можно понимать как псевдовыход из системы.

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

Но jwt не вмешивается в работу сервера для сохранения состояния пользователя. С этим справиться сложнее. Мы надеемся, что токен больше нельзя будет использовать после выхода пользователя из системы.

  • Установить относительно короткий срок действия токена, каждый раз при поступлении запроса он будет перевыпускаться и токен будет постоянно обновляться.

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

Вопрос 4 JWT single sign-on (принудительно выйти из юзера, например, после смены пароля, надеюсь, что все места, где заходят другие клиенты принудительно выходят из системы)

Это можно понять как немедленно сделать токен недействительным (немного похоже на вопрос 3 выше)

  • jwt + база данных (типа redis) + вайтлист (эту идею предложили коллеги в компании, отдельное спасибо~)

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

myRedis.set(`${uid}:token`, ${tokenA})

Когда пользователь входит в систему на устройстве B, токен в Redis обновляется.

myRedis.set(`${uid}:token`, ${tokenB})

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

Я добавил эту логику единого входа в проект.

попытка входа

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

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

退出

Недостаток: необходимо хранить соответствие {user, token} каждого пользователя

  • jwt + база данных (например, Redis) + черный список

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

Недостаток: объем данных, занесенных в черный список, со временем растет.

Вопрос 5. Как предотвратить повторные атаки

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

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

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

Вопрос 6: Как использовать метод «обновления токена каждый раз при отправке запроса» Если у клиента есть параллельные запросы, как с этим бороться

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

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

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

Это некоторые из ответов для вас. Добро пожаловать, чтобы оставить сообщение для обсуждения.

Дополнительные статьи