0Pricing
R Academy · Lección

Autenticación y seguridad de API

Añada validación de claves de API, encabezados CORS y filtros de limitación de solicitudes.

Autenticación y seguridad de API es una lección gratuita de R Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de R Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de R Academy incluye 4 lecciones en total.

Por qué es importante la seguridad de las API

Una API de Plumber es un servidor HTTP público. Sin autenticación, cualquiera que pueda acceder al puerto puede llamar a sus endpoints. Las capas de seguridad incluyen autenticación (¿quién es usted?), autorización (¿qué puede hacer?), validación de entradas y seguridad del transporte.

Filtros de Plumber como middleware

Los filtros de Plumber se ejecutan antes del controlador de la ruta. Use pr_filter(name, function(req, res){...}) para añadir middleware que inspeccione cada solicitud. Llame a plumber::forward() para pasar al siguiente filtro o a la ruta; devuelva el control anticipadamente para rechazar la solicitud.

# 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)

Filtro de autenticación mediante clave de API

El patrón de autenticación simple más habitual para las API de servidor a servidor consiste en pasar una clave de API estática en una cabecera. El filtro comprueba la cabecera en cada solicitud y devuelve 401 si falta o es incorrecta.

# 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()
# }

Comprobación de la cabecera Authorization

Los tokens Bearer se envían en la cabecera Authorization: Bearer <token>. Acceda a ella mediante req$HTTP_AUTHORIZATION. Analícela con strsplit() para extraer la parte correspondiente al token y, después, valide este contra su almacén.

# #* @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()
# }

Omitir la autenticación con #* @preempt

Algunos endpoints, como las comprobaciones de estado y la documentación pública, deben omitir la autenticación. Anótelos con #* @preempt auth, donde auth coincide con el nombre del filtro. Plumber dirige la solicitud directamente al controlador, omitiendo ese filtro.

# #* 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 con pr_cors()

Si se llama a su API desde un navegador ubicado en otro dominio, debe habilitar CORS (intercambio de recursos de origen cruzado). Use pr_cors() para configurar los orígenes, métodos y cabeceras permitidos sin escribir manualmente cabeceras sin procesar.

# 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)

Saneamiento de entradas: nunca confíe en los datos del usuario

Valide y sanee siempre las entradas antes de utilizarlas en consultas u operaciones con archivos:

  • Compruebe el tipo: is.numeric(), is.character()
  • Compruebe el rango: id >= 1 && id <= 1e9
  • Rechace los caracteres inesperados: grepl('[^a-zA-Z0-9_]', name)
  • Nunca interpole directamente cadenas proporcionadas por el usuario en SQL; use consultas parametrizadas
# #* @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))
# }

Conceptos de limitación de frecuencia

Plumber no incluye un limitador de frecuencia integrado, pero puede implementarlo en un filtro mediante un entorno compartido que registre el número de solicitudes por IP:

  • Registre la marca de tiempo de cada solicitud por IP en un entorno de R
  • Rechace la solicitud con 429 si el recuento supera el límite durante la ventana establecida
  • En producción, use un proxy inverso como nginx para limitar la frecuencia
# 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()
# }

Almacenamiento seguro de las claves de API

Nunca incluya secretos directamente en los archivos de código fuente. Almacénelos en variables de entorno y léalos al iniciar la aplicación con Sys.getenv(). Use localmente un archivo .env (excluido de git) e inyecte los secretos mediante el entorno de despliegue en producción.

# 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
')

Asociar el contexto del usuario a la solicitud

Después de validar un token en el filtro de autenticación, asocie la información descodificada del usuario al objeto req para que los controladores posteriores puedan acceder a ella sin volver a validarla. Los campos personalizados de req persisten durante toda la cadena de filtros.

# #* @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)
# }

Gestión de errores con tryCatch

Envuelva la lógica de su endpoint en tryCatch() para capturar errores inesperados y devolver una respuesta 500 adecuada, en lugar de bloquear el proceso de trabajo o revelar un seguimiento de pila al llamador.

# #* @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')
#   })
# }

Comprobación rápida: anotación @preempt

¿Qué efecto tiene la anotación #* @preempt auth en un endpoint de Plumber?

Repaso de la seguridad de las API

La protección de una API de Plumber implica defensas en varias capas:

  • pr_filter('auth', ...): inspecciona cada solicitud mediante middleware
  • req$HTTP_AUTHORIZATION / req$HTTP_X_API_KEY: lee las cabeceras de autenticación
  • #* @preempt auth: omite la autenticación en los endpoints públicos
  • pr_cors(): configura el acceso de origen cruzado desde navegadores
  • Validación de entradas antes de cualquier operación con la base de datos o archivos
  • Sys.getenv() para los secretos: nunca incluya las claves directamente en el código

Preguntas frecuentes

¿La lección «Autenticación y seguridad de API» es gratis?

Sí — el texto completo de «Autenticación y seguridad de API» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de R Academy, actualiza a CoddyKit PRO. El curso de R Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Autenticación y seguridad de API»?

Añada validación de claves de API, encabezados CORS y filtros de limitación de solicitudes. Practicas R Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar R Academy?

No se requiere experiencia previa. R Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Autenticación y seguridad de API»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de R Academy?

Sí. Cada lección de R Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Introducción a Plumber y REST
  2. Creación de endpoints GET y POST
  3. Autenticación y seguridad de API
  4. Implementación de API de Plumber en producción
← Volver a R Academy