Время прочтения: 10 мин.
Что такое API
API – это технология или набор функций, которая позволяет каким-либо образом контактировать двум программным компонентам. Например, система ПО метеослужбы содержит ежедневные данные о погоде. Приложение погоды на телефоне «общается» с этой системой через API и показывает ежедневные обновления погоды на телефоне.
Что такое access_token
Access_token – это зашифрованный или специально-сгенерированный ключ доступа от вашего профиля. Он нужен для того, чтобы стороннее приложение могло работать от вашего имени и получать те данные, которые предусмотрены в его функционале.
Примеры функционала API
Для примера возьмем API vk.com. Он позволяет разработчику создать свое собственное приложение для автоматизации работы пользователя. Например, написать сообщение группе пользователей, собрать информацию о группах, аудиозаписях, видеозаписях и др. Для доступа к таким возможностям разработчик через запрос к пользователю получает access_token. После этого токен передается через функции API vk.com. Также, API vk.com позволяет разрабатывать игры для площадки, привязывать оплату, взаимодействовать с Марусей (аналог Алисы от Яндекса) и т.д. Те, кто активно сталкивался с этой социальной сетью, знают, что напрямую музыку или видеозаписи скачивать нельзя, поэтому прибегали к помощи сторонних сервисов. Одним из них является VKMusic – приложение, которое устанавливается на ПК и запрашивает логин/пароль от vk.com, после чего предоставляет возможность в своем интерфейсе выбрать музыку/видеозаписи для скачки с вашего профиля. Это как раз пример работы API.
Права access_token
В случаях, когда API приложения имеет большой функционал, например, как vk.com, безопасность access_token равна безопасности логина и пароля, т.к. утечка токена приведет к не менее плачевным последствиям. Для того, чтобы определить какие права доступа есть у токена, достаточно посмотреть на документацию API
Общие сведения из документации
Теперь, исходя из общих сведений, перейдем к методам API и видим несколько интересных строчек.
Первый метод получает идентификатор беседы, второй метод выводит сообщения беседы в виде текста, третий метод выводит видеозаписи, аудиозаписи, фото и т.д. Иначе говоря, любое стороннее приложение, которое получает токен пользователя, автоматически получает доступ к сообщениям владельца этого токена. Для подтверждения моих слов, авторизуюсь в стороннем приложении с помощью токена. Вы можете написать свое собственное приложение, однако умельцы уже давно написали аналогичный сервис vk.com, который построен исключительно на методах API vk.
В поле ввожу свой токен и захожу на свою страницу:
На что стоит обращать внимание, чтобы предположить безопасен ли токен?
- Обратите внимание на функционал приложения — если он большой, то один токен может использоваться для разных методов.
- Прочитайте документацию к API приложения. Посмотрите можно ли с помощью токена получить доступ к критичным данным.
- Обратите внимание, можно ли использовать один токен для выполнения различных методов.
- Обратите внимание на то, каким образом стороннее приложение получает токен. Видит ли пользователь к каким данным получает доступ стороннее приложение и видит ли пользователь подключение этого приложения к своей странице.
После этого попробуйте написать свое приложение, основываясь на методах API и посмотрите правдивость описанной документации.
Защита
К сожалению, инструментов для обозначения небезопасного API, с точки зрения повышенных прав токена, пока нет. Если вы обычный пользователь и вам важна ваша безопасность, то можете почитать документацию к API приложения, которым вы пользуетесь. Для этого достаточно ввести в поисковой строке API и название приложения. Если же вы технически не подкованы, то:
- Вам не нужно использовать ваш профиль для регистрации или авторизации на каких-либо сервисах, т.к. это допускает утечку вашего токена. Хорошим примером в данном случае является авторизация в различных ботах в телеграмме. Некоторые боты используют ваш аккаунт для создания учетной записи в своей базе данных. Это удобно, но использовать такие боты нужно на свой страх и риск.
- Периодически сбрасывайте ваш токен. Для этого достаточно будет поменять ваш пароль. Если ваш токеен утек, заменить это будет невозможно, однако сброс токена не позволит длительное время пользоваться вашей учетной записью.
- Не храните критичные данные в своем профиле.
Если же вы, как разработчик, хотите создать API для своего приложения или обезопасить уже готовое, то хорошей практикой будет:
- Деление функций и создание отдельного токена для каждой из них. Например, вы хотите добавить функцию для сбора аудиозаписей со страницы пользователя, тогда создайте токен для этой функции. В этом случае стороннее приложение не сможет получить доступ к другим функциям вашего API, если на это не дал согласие пользователь. Это позволяет самому пользователю распоряжаться правами, которые он дает стороннему приложению. Например, я хочу сгенерировать токен. Для этого я могу воспользоваться JWT токеном, который состоит из:
1. id пользователя;
2. время жизни токена;
3. сигнатура, которая обозначает принадлежность к категории прав.
Права в свою очередь можно разделить на следующие категории:
1. Доступ на изменение или получение критических данных – данные, завладев которыми, аккаунт пользователя будет скомпрометирован и/или утрачен. Например, смена почты, пароля, номер телефона и т.д.
2. Доступ на изменение или получение данных, которые используются для правки в учетную запись. На примере vk.com, к данным можно отнести информацию об имени и фамилии, администрируемые группы, скрытые файлы, например, видео или аудиофайлы т.д.
3. Доступ на изменение или получение аудиофайлов, видеофайлов, фотографий и т.д. В этом случае, сторонний сервис, который будет запрашивать токен у пользователя, должно четко прописывать, к какой категории прав ему нужен доступ.
Пример реализации на псевдокоде
Создадим два объекта, которые включают в себя алгоритм шифрования и тип технологии, userID и время жизни сессии.
const header = '{"alg":"HS256","typ":"JWT"}' // string
const payload = '{"user_id":1,"exp":1581357039}' // string
Переведем объекты в формат base64 и склеим их в одну строку для дальнейшей обработки .
// переводим в формат base64 заголовок и данные
const headerBase64 = base64urlEncode(header)
const payloadBase64 = base64urlEncode(payload)
// склеиваем точкой полученные строки
const data = headerBase64 + '.' + payloadBase64
Кодируем строку ключом шифрования. В данном случае ключом шифрования является категория прав. На выходе получаем jwt токен для определенной категории.
// кодируем алгоритмом шифрования нашим ключом шифрования
const secret = «Категория ПРАВ и ее секретный ключ»
const sig = HMAC-SHA256(data, secret)
// формируем токен
const jwt = data + '.' + sig
Таким образом, вы, как разработчик, даете возможность пользователю распоряжаться правами токена, который он выдает для использования стороннего приложения.
Также, можно воспользоваться HMAC-токеном (hash-based message authentication code) с секретным ключом, который хранится на сервере и выдается только после входа в профиль клиента. Таким образом, нельзя скомпрометировать токен без участия пользователя. Этот токен должен идентифицировать категорию прав доступа, как в случае с JWT-токеном, и иметь свой срок жизни, т.е. по истечению какого-то времени он теряет свою валидность и получить данные по нему уже будет невозможно. HMAC используется для аутентификации сообщения по хэш-функциям. Токен HMAC помещается в файл куки, известный только серверу. Данный метод похож на зашифрованный файл куки, однако данный способ защиты менее ресурсозатратный, т.к. не надо зашифровывать и расшифровывать значения из файла.
Реализацию генерации HMAC – токена привожу ниже:
// определим коды ошибок, которые мы будем возвращать.
define("E_UNSUPPORTED_HASH_ALGO",-1);
class HMAC_Generator{
private $key, $algo;
private $sign_param_name = "hmac";
function __construct($key, $algo = "sha256"){
$this->key = $key;
$this->algo = $algo;
}
function make_data_hmac($data, $key = NULL){
// если не задан ключ в параметре - используем из свойств
if(empty($key)) $key = $this->key;
// если параметр с подписью есть в массиве - уберем.
if(isset($data[$this->sign_param_name])) unset($data[$this->sign_param_name]);
// отсортируем по ключам в алфавитном порядке -
// на случай, если последовательность полей изменилась
// например, если данные передавались GET- или POST-запросом.
HMAC_Generator::ksort_recursive($data);
// сформируем JSON (или другую сериализацию - можно переопределить метод encode_string)
$data_enc = $this->serialize_array($data);
// формируем и возвращаем подпись
return $this->make_signature($data_enc, $key);
}
function check_data_hmac($data, $key = NULL, $sign_param_name = NULL){
// если не задан ключ в аргументах - используем из свойств
if(empty($key)) $key = $this->key;
// если не задано имя параметра с подписью аргументах в параметре - используем из свойств
if(empty($sign_param_name)) $sign_param_name = $this->sign_param_name;
// если в данных нет подписи - сразу вернем false
if(empty($data[$sign_param_name])) return false;
// исходный HMAC нам приходит в том же массиве, что и данные,
// заберем его значение для сверки и выкинем из массива
$hmac = $data[$sign_param_name];
unset($data[$sign_param_name]);
// сформируем контрольный HMAC
$orig_hmap = $this->make_data_hmac($data, $key);
// проверку осуществляем регистронезависимо
if(strtolower($orig_hmap) != strtolower($hmac)) return false;
else return true;
}
// Установка алгоритма хеширования
function set_hash_algo($algo){
// приведем к нижнему регистру
$algo = strtolower($algo);
// проверим, поддерживается ли системой выбранный алгоритм
if(in_array($algo, hash_algos()))
$this->algo = $algo;
else return
E_UNSUPPORTED_HASH_ALGO;
}
//
// сериализацию и хеширование - выносим в отдельные методы, просто перепишите или переопределите их
//
private function serialize_array($data){
// кодируем все в json, в случае если мы будем собирать подпись не только в PHP,
// такой тип сериализации - оптимальный
$data_enc = json_encode($data, JSON_UNESCAPED_UNICODE);
return $data_enc;
}
// переопределите, если будет другой алго формирования подписи, не HASH HMAC
private function make_signature($data_enc, $key){
// сформируем подпись HMAC при помощи выбранного аглоритма
$hmac = hash_hmac($this->algo, $data_enc, $key);
return $hmac;
}
// статический метод для рекурсивной сортировки массива по именам ключей
public static function ksort_recursive(&$array, $sort_flags = SORT_REGULAR) {
// если это не массив - сразу вернем false
if (!is_array($array)) return false;
ksort($array, $sort_flags);
foreach ($array as &$arr) {
HMAC_Generator::ksort_recursive($arr, $sort_flags);
}
return true;
}
}
- Используйте такой токен и его время жизни, который нельзя будет подобрать за этот срок жизни. Например, зашифрованный токен с условной длинной в 16 символов можно подобрать за 2-ое суток, тогда жизнь токена должна быть меньше 2-ух суток. Например, 6 букв латинского алфавита, скажем, в 2 регистрах — (26*2)⁶ комбинаций. С повышением длины число комбинаций растёт экспоненциально. Далее делим число комбинаций на скорость, которая проходит одна комбинация. В случае с хэшами смотрим на производительность видеокарты, в случае с короткими несоленными токенами нужно учитывать радужные таблицы. 26^6 = 308915776 вариаций / hash/sec = time – наглядная формула.
- Отключите создание архива данных учетной записи. Многие популярные API используют функцию создания архива данных. В них входят переписки, истории паролей и прочие персональные данные. Если же нуждаетесь в этой функции, переложите это на службу поддержки.
Итог
При проведении аудита некоторые исследователи не обращают внимания на такие вещи как access_token, а именно на те ситуации, когда разработчики дают много прав токену пользователя, из-за чего происходят взломы аккаунтов пользователей, поднимается ажиотаж на взлом у злоумышленников и т.д.
В данной публикации разобрали некоторые важные аспекты API, которые многие эксперты почему-то не затрагивают. Возможно, для кого-то этот материал окажется полезным при проведении аудита API или его разработке и поможет иначе взглянуть на эту технологию.