0Pricing
R Academy · Урок

Аутентификация и безопасность API

Добавляйте проверку ключей API, заголовки CORS и фильтры ограничения частоты запросов

«Аутентификация и безопасность API» — бесплатный урок R Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения R Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс R Academy содержит 4 уроков всего.

Почему важна безопасность API

API Plumber — это общедоступный HTTP-сервер. Без аутентификации любой, кто может обратиться к порту, способен вызывать ваши конечные точки. Уровни безопасности включают аутентификацию (кто вы?), авторизацию (что вам разрешено делать?), проверку входных данных и безопасность передачи данных.

Фильтры Plumber как промежуточное ПО

Фильтры Plumber выполняются до обработчика маршрута. Используйте pr_filter(name, function(req, res){...}), чтобы добавить промежуточное ПО, проверяющее каждый запрос. Вызывайте plumber::forward(), чтобы передать запрос следующему фильтру или маршруту; чтобы отклонить запрос, завершайте выполнение досрочно.

# library(plumber)
# pr <- plumb('api.R')
# pr |>
#   pr_filter('logger', function(req, res) {
#     cat(req$REQUEST_METHOD, req$PATH_INFO, '
')
#     plumber::forward()  # must call to continue
#   }) |>
#   pr_run(port = 8000)

Фильтр аутентификации по ключу API

Самый распространённый простой способ аутентификации для API «сервер — сервер» — статический ключ API, передаваемый в заголовке. Фильтр проверяет этот заголовок в каждом запросе и возвращает 401, если ключ отсутствует или указан неверно.

# In api.R:
# VALID_KEY <- Sys.getenv('API_SECRET_KEY')
#
# #* @filter auth
# function(req, res) {
#   key <- req$HTTP_X_API_KEY
#   if (is.null(key) || key != VALID_KEY) {
#     res$status <- 401L
#     return(list(error = 'Unauthorized'))
#   }
#   plumber::forward()
# }

Проверка заголовка Authorization

Токены Bearer передаются в заголовке Authorization: Bearer <token>. Получить его можно через req$HTTP_AUTHORIZATION. Разберите значение с помощью strsplit(), чтобы извлечь токен, а затем проверьте его по своему хранилищу.

# #* @filter bearer_auth
# function(req, res) {
#   auth_header <- req$HTTP_AUTHORIZATION
#   if (is.null(auth_header) || !startsWith(auth_header, 'Bearer ')) {
#     res$status <- 401L
#     return(list(error = 'Bearer token required'))
#   }
#   token <- substring(auth_header, 8)  # strip 'Bearer '
#   if (!token_is_valid(token)) {
#     res$status <- 401L
#     return(list(error = 'Invalid token'))
#   }
#   plumber::forward()
# }

Пропуск аутентификации с помощью #* @preempt

Некоторые конечные точки (проверка работоспособности, общедоступная документация) должны пропускать аутентификацию. Пометьте их аннотацией #* @preempt auth, где auth соответствует имени фильтра. Plumber направит запрос непосредственно обработчику, минуя этот фильтр.

# #* Health check — no auth required
# #* @preempt auth
# #* @get /ping
# function() {
#   list(status = 'ok', time = as.character(Sys.time()))
# }
#
# #* Protected endpoint — goes through auth filter
# #* @get /data
# function() {
#   list(secret = 'sensitive data')
# }

CORS с помощью pr_cors()

Если к вашему API обращаются из браузера с другого домена, необходимо включить CORS (совместное использование ресурсов между источниками). Используйте pr_cors(), чтобы настроить разрешённые источники, методы и заголовки, не прописывая необработанные заголовки вручную.

# library(plumber)
# pr <- plumb('api.R')
# pr |>
#   pr_cors(
#     origin            = 'https://myapp.example.com',
#     methods           = c('GET', 'POST'),
#     headers           = c('Content-Type', 'X-API-Key'),
#     credentials       = TRUE
#   ) |>
#   pr_run(port = 8000)

Санитизация входных данных — никогда не доверяйте данным пользователя

Всегда проверяйте и очищайте входные данные перед использованием в запросах или операциях с файлами:

  • Проверяйте тип: is.numeric(), is.character()
  • Проверяйте диапазон: id >= 1 && id <= 1e9
  • Отклоняйте неожиданные символы: grepl('[^a-zA-Z0-9_]', name)
  • Никогда не вставляйте пользовательские строки напрямую в SQL — используйте параметризованные запросы
# #* @post /search
# function(req, res) {
#   body <- jsonlite::fromJSON(req$postBody)
#   query <- body$query
#   if (!is.character(query) || nchar(query) > 200) {
#     res$status <- 400L
#     return(list(error = 'query must be a string <= 200 chars'))
#   }
#   if (grepl('[;\'"]', query)) {
#     res$status <- 400L
#     return(list(error = 'Invalid characters in query'))
#   }
#   list(results = search_db(query))
# }

Основы ограничения частоты запросов

В Plumber нет встроенного ограничителя частоты запросов, но его можно реализовать в фильтре с помощью общего окружения, отслеживающего количество запросов для каждого IP:

  • Записывайте время каждого запроса по IP в окружение R
  • Возвращайте 429, если количество запросов превышает лимит в заданном временном окне
  • В рабочей среде используйте для ограничения частоты обратный прокси, например nginx
# request_log <- new.env()
#
# #* @filter rate_limit
# function(req, res) {
#   ip <- req$REMOTE_ADDR
#   now <- as.numeric(Sys.time())
#   if (!exists(ip, envir = request_log)) assign(ip, c(), envir = request_log)
#   times <- get(ip, envir = request_log)
#   times <- times[times > now - 60]   # last 60 seconds
#   if (length(times) >= 60) { res$status <- 429L; return(list(error='Too Many Requests')) }
#   assign(ip, c(times, now), envir = request_log)
#   plumber::forward()
# }

Безопасное хранение ключей API

Никогда не прописывайте секреты непосредственно в исходных файлах. Храните их в переменных окружения и считывайте при запуске с помощью Sys.getenv(). Локально используйте файл .env (исключённый из git), а в рабочей среде передавайте секреты через окружение развёртывания.

# In .env (never commit this file):
# API_SECRET_KEY=my_super_secret_key_here
#
# In api.R:
# VALID_KEY <- Sys.getenv('API_SECRET_KEY', unset = '')
# if (nchar(VALID_KEY) == 0) stop('API_SECRET_KEY not set')
#
# Load .env locally (devtools::load_dot_env or Sys.setenv):
# readRenviron('.env')
cat('Sys.getenv reads API keys without exposing them in source
')

Добавление контекста пользователя в запрос

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

# #* @filter auth
# function(req, res) {
#   token <- req$HTTP_AUTHORIZATION
#   user <- validate_token(token)  # returns list(id=1, role='admin')
#   if (is.null(user)) { res$status <- 401L; return(list(error='Unauthorized')) }
#   req$user <- user   # attach to request
#   plumber::forward()
# }
#
# #* @get /profile
# function(req) {
#   list(user_id = req$user$id, role = req$user$role)
# }

Обработка ошибок с помощью tryCatch

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

# #* @get /risky/<id:int>
# function(id, res) {
#   tryCatch({
#     result <- risky_db_call(id)
#     list(data = result)
#   }, error = function(e) {
#     message('Error in /risky: ', conditionMessage(e))
#     res$status <- 500L
#     list(error = 'Internal server error')
#   })
# }

Быстрая проверка: аннотация @preempt

Что делает аннотация #* @preempt auth с конечной точкой Plumber?

Итоги по безопасности API

Защита API Plumber включает несколько уровней:

  • pr_filter('auth', ...) — проверяет каждый запрос в промежуточном ПО
  • req$HTTP_AUTHORIZATION / req$HTTP_X_API_KEY — считывают заголовки аутентификации
  • #* @preempt auth — пропускает аутентификацию для общедоступных конечных точек
  • pr_cors() — настраивает междоменный доступ из браузера
  • Проверка входных данных до любой операции с базой данных или файлами
  • Sys.getenv() для секретов — никогда не прописывайте ключи непосредственно в коде

Часто задаваемые вопросы

Урок «Аутентификация и безопасность API» бесплатный?

Да — полный текст урока «Аутентификация и безопасность API» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс R Academy, подпишись на CoddyKit PRO. Курс R Academy содержит 4 уроков всего.

Чему я научусь в уроке «Аутентификация и безопасность API»?

Добавляйте проверку ключей API, заголовки CORS и фильтры ограничения частоты запросов Ты практикуешь R Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать R Academy?

Предыдущий опыт не требуется. R Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Аутентификация и безопасность API»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке R Academy?

Да. Каждый урок R Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Введение в Plumber и REST
  2. Создание конечных точек GET и POST
  3. Аутентификация и безопасность API
  4. Развёртывание API Plumber в рабочей среде
← Назад к R Academy