Безопасность мобильного oauth 2.0

Краткое введение в OAuth

Лучший способ представить OAuth — перечислить список событий, которые происходят во время входа:

  • Обработка отправки формы. Пользователь переходит на домашнюю страницу приложения, например http://www.example.com, и нажимает кнопку «Sign In with Facebook», которая ссылается на маршрут приложения, например .
  • Fetch Request Token (внутренний запрос). Сервер получает запрос и отвечает перенаправлением на URL авторизации OAuth Facebook. Все провайдеры OAuth должны документировать URL-адрес для перенаправления пользователя.

    • В запросе передается Consumer key — «логин приложения», а сам запрос подписывается при помощи Consumer secret — «пароля приложения», что защищает его от подделки.
    • В ответ Provider генерирует и возвращает «заполненный мусором» токен, который называется Request Token.
  • Redirect to Authorization (через редирект в браузере). Теперь пользователю предлагается войти в систему Facebook (если уже не вошел в систему). Затем предоставляется запрос об обмене информацией, когда пользователю необходимо предоставить разрешение Facebook для совместного использования запрошенной информации с исходным приложением. Все это делается на веб-сайте Facebook и является частной транзакцией между Facebook и пользователем, приложение не участвует.
  • Fetch Access Token (внутренний запрос). После того, как пользователь принимает запрос на обмен информацией, Facebook перенаправляет обратно в приложение по предварительно настроенному URL-адресу обратного вызова, например . Строка запроса URL-адреса переадресации включает в себя код авторизации, который приложение может использовать для доступа к API Facebook от имени пользователя.
  • Call API (внутренний запрос). Приложение использует API Facebook для получения информации о пользователе. Особый интерес представляет собой уникальный идентификатор пользователя ( Shared Secret ), который может использоваться для регистрации пользователя в базе данных приложения после регистрации пользователя для входа в систему.

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

В настоящее время используются две версии протокола OAuth, как в соответствии с описанным выше общим процессом, так и с некоторыми различиями в реализации. OAuth 1.0a, используемый Twitter, является самым сложным из двух. OAuth 2, используемый Facebook, представляет собой несовместимую пересмотренную версию протокола, которая устраняет большую часть сложности версии 1.0a, полагаясь на защищенный HTTP для шифрования.

TrustScreen

Технология TrustScreen призвана повысить безопасность онлайн операций в дистанционном банковском обслуживании (ДБО) и других ответственных приложениях. Её задача состоит в защите от подмены злоумышленником подписываемого документа или его хеша в процессе подписи, а также от выполнения несанкционированных операций в случае успешного перехвата PIN-кода. Технология делает возможным визуальный контроль подписываемых данных, которые отображаются на экране устройства непосредственно перед подписью. Все значимые операции над отправленными на подпись данными производятся «на борту» устройства:

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

Как получить access_token?

Процесс получения access_token полностью описан на следующей странице https://www.instagram.com /developer/authentication/ и состоит из нескольких шагов.

Получение access_token на стороне сервера

Если вы получаете токен на стороне сервера, убедитесь, что в настройках ранее созданного вами клиента стоит галочка во вкладке Security напротив пункта Disable implicit Oauth.

Шаг первый: отправляем URL авторизации

С помощью ранее полученного нами Client ID нам необходимо перейти по-следующему URL:

  • здесь, CLIENT-ID — это наш полученный ранее Client ID,
  • redirect_uri — заполненный нами ранее Valid redirect URIs.

Шаг второй: получаем редирект от инстаграм

Перейдя по-указанному URL, на появившейся странице нажимаем кнопку Authorize, после чего инстаграм перенаправит нас на наш redirect_uri, отправив дополнительно в GET параметре наш code (данный code копируем в адресной строке браузера и запоминаем).

Шаг третий: запрос на получение access_token

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

  • здесь, client_id — наш полученный ранее Client ID,
  • client_secret — наш полученный ранее Client Secret,
  • redirect_uri — заполненный нами ранее Valid redirect URIs,
  • code — code полученный нами на шаге 2.

Если мы все выполнили верно, то в ответ на наш запрос мы получим json с нашим заветным access_token, а также информацию об аккаунте.

Получение access_token на стороне клиента

Чтобы получить access_token на стороне клиента, в настройках нашего созданного клиента (имеется в виду клиент, который создается на странице Manage Clients), во вкладке Security необходимо установить галочку напротив пункта Enforce signed requests. Данный метод получения токена отличается от получения токена на стороне сервера тем, что при каждом запросе на получение данных от инстаграм, потребуется передавать параметр sig, который необходимо предварительно получить, как написано на странице https://www.instagram.com /developer/secure-api-requests/. То есть, если генерировать токен на стороне сервера, запрос на получение данных будет примерно следующим:

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

при условии, что в настройках стоит галочка напротив Enforce signed requests.

Шаг первый: отправляем URL авторизации

С помощью ранее полученного нами Client ID нам необходимо перейти по-следующему URL:

Шаг второй: получаем редирект от инстаграм

Перейдя по-указанному URL, на появившейся странице нажимаем кнопку Authorize, после чего инстаграм перенаправит нас на наш redirect_uri, отправив дополнительно в адресной строке браузера наш access_token.

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

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

Работа с веб-приложениями посредством плагина

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

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

плагин для работы с порталом государственных услуг, поддерживающий наиболее распространённые средства электронной подписи. Плагин предназначен для использования в инфраструктуре электронного правительства, поддерживает исключительно квалифицированные средства электронной подписи. По состоянию на начало 2016 г. не работает в последних версиях Mac OS X и в браузере Google Chrome.

Структура JWT

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

Шаг 1. Создаем HEADER

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

Поле не говорит нам ничего нового, только то, что это JSON Web Token. Интереснее здесь будет поле , которое определяет алгоритм хеширования. Он будет использоваться при создании подписи. — не что иное, как , для его вычисления нужен лишь один секретный ключ (более подробно об этом в шаге 3). Еще может использоваться другой алгоритм — в отличие от предыдущего, он является ассиметричным и создает два ключа: публичный и приватный. С помощью приватного ключа создается подпись, а с помощью публичного только лишь проверяется подлинность подписи, поэтому нам не нужно беспокоиться о его безопасности.

Шаг 2. Создаем PAYLOAD

Payload — это полезные данные, которые хранятся внутри JWT. Эти данные также называют JWT-claims (заявки). В примере, который рассматриваем мы, сервер аутентификации создает JWT с информацией об id пользователя — userId.

Мы положили только одну заявку (claim) в payload. Вы можете положить столько заявок, сколько захотите. Существует список стандартных заявок для JWT payload — вот некоторые из них:

  • iss (issuer) — определяет приложение, из которого отправляется токен.
  • sub (subject) — определяет тему токена.
  • exp (expiration time) — время жизни токена.

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

Шаг 3. Создаем SIGNATURE

Подпись вычисляется с использование следующего псевдо-кода:

Алгоритм base64url кодирует хедер и payload, созданные на 1 и 2 шаге. Алгоритм соединяет закодированные строки через точку. Затем полученная строка хешируется алгоритмом, заданным в хедере на основе нашего секретного ключа.

Шаг 4. Теперь объединим все три JWT компонента вместе

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

Вы можете попробовать создать свой собственный JWT на сайте jwt.io.
Вернемся к нашему примеру. Теперь сервер аутентификации может слать пользователю JWT.

Как JWT защищает наши данные?

Очень важно понимать, что использование JWT НЕ скрывает и не маскирует данные автоматически. Причина, почему JWT используются — это проверка, что отправленные данные были действительно отправлены авторизованным источником

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

Подписанные данные позволяют получателю данных проверить аутентификацию источника данных. Таким образом закодирование и подпись данных не защищает их. С другой стороны, главная цель шифрования — это защита данных от неавторизованного доступа. Для более детального объяснения различия между кодированием и шифрованием, а также о том, как работает хеширование, смотрите . Поскольку JWT только лишь закодирована и подписана, и поскольку JWT не зашифрована, JWT не гарантирует никакой безопасности для чувствительных (sensitive) данных.

Шаг 5. Проверка JWT

В нашем простом примере из 3 участников мы используем JWT, который подписан с помощью алгоритма и только сервер аутентификации и сервер приложения знают секретный ключ. Сервер приложения получает секретный ключ от сервера аутентификации во время установки аутентификационных процессов. Поскольку приложение знает секретный ключ, когда пользователь делает API-запрос с приложенным к нему токеном, приложение может выполнить тот же алгоритм подписывания к JWT, что в шаге 3. Приложение может потом проверить эту подпись, сравнивая ее со своей собственной, вычисленной хешированием. Если подписи совпадают, значит JWT валидный, т.е. пришел от проверенного источника. Если подписи не совпадают, значит что-то пошло не так — возможно, это является признаком потенциальной атаки. Таким образом, проверяя JWT, приложение добавляет доверительный слой (a layer of trust) между собой и пользователем.

Шаг 4. Получение информации о пользователе

Теперь, когда у нас есть параметры access_token, мы можем сделать запрос к Yandex API и получить информацию о пользователе:

if (isset($tokenInfo)) {
    $params = array(
        'format'       => 'json',
        'oauth_token'  => $tokenInfo
    );
}

В параметр format передаём значение json — формат возвращаемых данных; в oauth_token записываем access_token — токен доступа, который мы достали по POST запросу в предыдущем шаге.

Для получения информации о пользователе сформированные параметры нам нужно отправить GET запросом по адресу https://login.yandex.ru/info:

$userInfo = json_decode(file_get_contents('https://login.yandex.ru/info' . '?' . urldecode(http_build_query($params))), true);

В результате, если всё было сделано успешно, то получим JSON ответ. Преобразуем его сразу же в массив:

Array
(
     => stanislav.protasevich
     => Протасевич Станислав
     => male
     => 1988-07-03
     => 203179654
     => stanislav.protasevich@yandex.com
     => Array
        (
             => stanislav.protasevich@yandex.com
        )
)

Если в массиве есть ключ id, то записываем пришедший массив в переменную $userInfo:

if (isset($userInfo)) {
    $userInfo = $userInfo;
    $result = true;
}

Полный код:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <title>Аутентификация через Yandex</title>
</head>
<body>

<?php

$client_id = '22d7dfc5f4358b47b41f6e1f8a80efa0'; // Id приложения
$client_secret = '721a338df24447efe9080cfd36a2da7a'; // Пароль приложения
$redirect_uri = 'http://localhost/yandex-auth'; // Callback URI

$url = 'https://oauth.yandex.ru/authorize';

$params = array(
    'response_type' => 'code',
    'client_id'     => $client_id,
    'display'       => 'popup'
);

echo $link = '<p><a href="' . $url . '?' . urldecode(http_build_query($params)) . '">Аутентификация через Yandex</a></p>';

if (isset($_GET)) {
    $result = false;

    $params = array(
        'grant_type'    => 'authorization_code',
        'code'          => $_GET,
        'client_id'     => $client_id,
        'client_secret' => $client_secret
    );

    $url = 'https://oauth.yandex.ru/token';

    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, urldecode(http_build_query($params)));
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    $result = curl_exec($curl);
    curl_close($curl);

    $tokenInfo = json_decode($result, true);

    if (isset($tokenInfo)) {
        $params = array(
            'format'       => 'json',
            'oauth_token'  => $tokenInfo
        );

        $userInfo = json_decode(file_get_contents('https://login.yandex.ru/info' . '?' . urldecode(http_build_query($params))), true);
        if (isset($userInfo)) {
            $userInfo = $userInfo;
            $result = true;
        }
    }
}

?>

</body>
</html>

Фундамент OAuth

    Есть замечательный цикл статей про OAuth: Beginner’s Guide to OAuth (на английском; от автора с говорящим прозвищем hueniverse). Его изучение отнимет у вас приблизительно 4 часа, если вы до этого момента совершенно не знакомы с темой.

Приложение = Consumer + доступ к API

приложение

  • Скрипт формы импорта контактов из GMail (см. пример выше).
  • Приложение для iPhone, позволяющее писать сообщения в Twitter.
  • Прямоугольный виджет на вашем сайте, в котором отображаются последние сообщения чата и есть возможность написать новое.

Token = Key + Secret

Сообщение = Документ + Цифровая подпись

Цифровая подпись не шифрует документ, она лишь гарантирует его подлинность!

  1. Consumer добавляет цифровую подпись к сообщению, в общем виде —
    $transfer = $message . "-" . md5($message . $sharedSecret);
    // $transfer = "Мой телефон 1234567" . "-" . md5("Мой телефон 1234567" . "529AeGWg")
  2. Service Provider принимает данные, разбивает их обратно на 2 части — $message и $signature — и проделывает точно такую же операцию:
    $signatureToMatch = md5($message . $sharedSecret);
    // $signatureToMatch = md5("Мой телефон 1234567" . "529AeGWg");

    Дальше остается только сравнить получившееся значение $signatureToMatch с тем, что было в полученных данных $signature и рапортовать о подделке, если значения не совпали.

    Итак, чтобы сформировать MD5-подпись, обязательно знать Shared Secret. (Кстати, кроме MD5 есть и другие алгоритмы необратимого хэширования.) Злоумышленник не знает Shared Secret, поэтому и подпись он подделать не может.

Тип разрешения на авторизацию: Код авторизации

Код авторизации является одним из наиболее распространённых типов разрешения на авторизацию, поскольку он хорошо подходит для серверных приложений, где исходный код приложения и секрет клиента не доступны посторонним. Процесс в данном случае строится на перенаправлении (redirection), что означает, что приложение должно быть в состоянии взаимодействовать с пользовательским агентом (user-agent), например, веб-браузером, и получать коды авторизации API, перенаправляемые через пользовательский агент.

Опишем процесс на диаграмме:

Шаг 1: Ссылка с кодом авторизации

Сначала пользователю предоставляется ссылка следующего вида:

Рассмотрим компоненты ссылки:

  • https://cloud.digitalocean.com/v1/oauth/authorize: входная точка API авторизации (API authorization endpoint).
  • client_id=CLIENT_ID: идентификатор клиента приложения (с помощью этого идентификатора API понимает, какое приложение запрашивает доступ).
  • redirect_uri=CALLBACK_URL: URL, на который сервис перенаправит пользовательского агент (браузер) после выдачи авторизационного кода.
  • response_type=code: указывает на то, что приложение запрашивает доступ с помощью кода авторизации.
  • scope=read: задаёт уровень доступа приложения (в данном случае — доступ на чтение).

Шаг 2: Пользователь авторизует приложение

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

На этом скриншоте экрана авторизации DigitalOcean мы можем видеть, что приложение “Thedropletbook App” запрашивает доступ на чтение к аккаунту “manicas@digitalocean.com”.

Шаг 3: Приложение получает код авторизации

Если пользователь выбирает “Авторизовать приложение”, сервис перенаправляет пользовательский агент (браузер) по URL перенаправления (redirect URL), который был задан на этапе регистрации клиента (вместе с кодом авторизации). Ссылка будет выглядеть похожим образом (в данном примере приложение называется “dropletbook.com”):

Шаг 4: Приложение запрашивает токен доступа

Приложение запрашивает токен доступа у API путём отправки авторизационного кода и аутентификационной информации (включая секрет клиента) сервису. Ниже представлен пример POST-запроса для получения токена DigitalOcean:

Шаг 5: Приложение получает токен доступа

Если авторизация прошла успешно, API возвращает токен доступа (а также, опционально, токен для обновления токена доступа — refresh token). Весь ответ сервера может выглядеть следующим образом:

Теперь приложение авторизовано! Оно может использовать токен для доступа к пользовательскому аккаунту через API сервиса с заданными ограничениями доступа до тех пор, пока не истечёт срок действия токена или токен не будет отозван. Если был создан токен для обновления токена доступа, он может быть использован для получения новых токенов доступа, когда истечёт срок действия старого токена.

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

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

Adblock
detector