Время прочтения: 10 мин.

Что такое API

API – это технология или набор функций, которая позволяет каким-либо образом контактировать двум программным компонентам. Например, система ПО метеослужбы содержит ежедневные данные о погоде. Приложение погоды на телефоне «общается» с этой системой через API и показывает ежедневные обновления погоды на телефоне.

Что такое access_token

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

Примеры функционала API

Для примера возьмем API vk.com. Он позволяет разработчику создать свое собственное приложение для автоматизации работы пользователя. Например, написать сообщение группе пользователей, собрать информацию о группах, аудиозаписях, видеозаписях и др. Для доступа к таким возможностям разработчик через запрос к пользователю получает access_token. После этого токен передается через функции API vk.com. Также, API vk.com позволяет разрабатывать игры для площадки, привязывать оплату, взаимодействовать с Марусей (аналог Алисы от Яндекса) и т.д. Те, кто активно сталкивался с этой социальной сетью, знают, что напрямую музыку или видеозаписи скачивать нельзя, поэтому прибегали к помощи сторонних сервисов. Одним из них является VKMusic – приложение, которое устанавливается на ПК и запрашивает логин/пароль от vk.com, после чего предоставляет возможность в своем интерфейсе выбрать музыку/видеозаписи для скачки с вашего профиля. Это как раз пример работы API.

Авторизация в стороннем приложении с помощью API vk.com

Права 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 или его разработке и поможет иначе взглянуть на эту технологию.