Uwierzytelnianie i bezpieczeństwo API
Dodawaj walidację kluczy API, nagłówki CORS i filtry ograniczające częstotliwość żądań.
Uwierzytelnianie i bezpieczeństwo API to bezpłatna lekcja R Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej R Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs R Academy zawiera 4 lekcji w sumie.
Dlaczego bezpieczeństwo API ma znaczenie
API Plumbera jest publicznym serwerem HTTP. Bez uwierzytelniania każdy, kto może uzyskać dostęp do portu, może wywoływać endpointy. Warstwy bezpieczeństwa obejmują uwierzytelnianie (kim jesteś?), autoryzację (co możesz zrobić?), walidację danych wejściowych oraz bezpieczeństwo transportu.
Filtry Plumbera jako middleware
Filtry w Plumberze są uruchamiane przed handlerem trasy. Użyj pr_filter(name, function(req, res){...}), aby dodać middleware sprawdzający każde żądanie. Wywołaj plumber::forward(), aby przekazać żądanie do następnego filtra lub trasy; aby je odrzucić, zakończ działanie funkcji wcześniej.
# 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)Filtr uwierzytelniania za pomocą klucza API
Najczęściej stosowanym prostym mechanizmem uwierzytelniania w interfejsach API komunikujących się między serwerami jest statyczny klucz API przekazywany w nagłówku. Filtr sprawdza ten nagłówek przy każdym żądaniu i zwraca kod 401, jeśli klucza brakuje lub jest nieprawidłowy.
# 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()
# }Sprawdzanie nagłówka Authorization
Tokeny Bearer są przekazywane w nagłówku Authorization: Bearer <token>. Dostęp do niego można uzyskać za pomocą req$HTTP_AUTHORIZATION. Użyj strsplit(), aby wyodrębnić część zawierającą token, a następnie zweryfikuj ją względem magazynu tokenów.
# #* @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()
# }Pomijanie uwierzytelniania za pomocą #* @preempt
Niektóre endpointy, takie jak sprawdzanie stanu usługi czy publiczna dokumentacja, powinny pomijać uwierzytelnianie. Oznacz je za pomocą #* @preempt auth, gdzie auth odpowiada nazwie filtra. Plumber kieruje żądanie bezpośrednio do handlera, omijając ten filtr.
# #* 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 za pomocą pr_cors()
Jeśli API jest wywoływane z przeglądarki znajdującej się w innej domenie, należy włączyć CORS (Cross-Origin Resource Sharing). Użyj pr_cors(), aby skonfigurować dozwolone źródła, metody i nagłówki bez ręcznego zapisywania surowych nagłówków.
# 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)Sanityzacja danych wejściowych — nigdy nie ufaj danym od użytkownika
Zawsze waliduj i sanityzuj dane wejściowe przed użyciem ich w zapytaniach lub operacjach na plikach:
- Sprawdzaj typ:
is.numeric(),is.character() - Sprawdzaj zakres:
id >= 1 && id <= 1e9 - Odrzucaj nieoczekiwane znaki:
grepl('[^a-zA-Z0-9_]', name) - Nigdy nie wstawiaj bezpośrednio ciągów znaków od użytkownika do SQL — używaj zapytań parametryzowanych
# #* @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))
# }Koncepcje ograniczania częstotliwości żądań
Plumber nie ma wbudowanego mechanizmu ograniczania częstotliwości żądań, ale można go zaimplementować w filtrze za pomocą współdzielonego środowiska, które śledzi liczbę żądań z poszczególnych adresów IP:
- Zapisuj znacznik czasu każdego żądania według adresu IP w środowisku R
- Zwracaj kod 429, jeśli liczba żądań w danym oknie przekroczy limit
- W środowisku produkcyjnym używaj odwrotnego proxy, takiego jak nginx, do ograniczania częstotliwości żądań
# 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()
# }Bezpieczne przechowywanie kluczy API
Nigdy nie umieszczaj sekretów na stałe w plikach źródłowych. Przechowuj je w zmiennych środowiskowych i odczytuj podczas uruchamiania za pomocą Sys.getenv(). Lokalnie używaj pliku .env (wykluczonego z gita), a w środowisku produkcyjnym wstrzykuj sekrety za pośrednictwem środowiska wdrożeniowego.
# 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
')Dołączanie kontekstu użytkownika do żądania
Po zweryfikowaniu tokenu w filtrze uwierzytelniania dołącz zdekodowane informacje o użytkowniku do obiektu req, aby kolejne handlery mogły uzyskać do nich dostęp bez ponownej weryfikacji. Niestandardowe pola obiektu req zachowują się w całym łańcuchu filtrów.
# #* @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)
# }Obsługa błędów za pomocą tryCatch
Umieść logikę endpointu w tryCatch(), aby przechwytywać nieoczekiwane błędy i zwracać poprawną odpowiedź 500 zamiast powodować awarię workera lub ujawniać wywołującemu ślad stosu.
# #* @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')
# })
# }Szybkie sprawdzenie: adnotacja @preempt
Co adnotacja #* @preempt auth robi z endpointem Plumbera?
Podsumowanie bezpieczeństwa API
Zabezpieczanie API Plumbera obejmuje wielowarstwową ochronę:
pr_filter('auth', ...)— sprawdzanie każdego żądania w middlewarereq$HTTP_AUTHORIZATION/req$HTTP_X_API_KEY— odczytywanie nagłówków uwierzytelniania#* @preempt auth— pomijanie uwierzytelniania dla publicznych endpointówpr_cors()— konfigurowanie dostępu między domenami z przeglądarki- Walidacja danych wejściowych przed każdą operacją na bazie danych lub pliku
Sys.getenv()do obsługi sekretów — nigdy nie umieszczaj kluczy na stałe w kodzie
Często zadawane pytania
Czy lekcja „Uwierzytelnianie i bezpieczeństwo API” jest bezpłatna?
Tak — pełny tekst „Uwierzytelnianie i bezpieczeństwo API” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu R Academy, przejdź na CoddyKit PRO. Kurs R Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Uwierzytelnianie i bezpieczeństwo API”?
Dodawaj walidację kluczy API, nagłówki CORS i filtry ograniczające częstotliwość żądań. Ćwiczysz R Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć R Academy?
Nie wymagamy żadnego doświadczenia. R Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Uwierzytelnianie i bezpieczeństwo API”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji R Academy?
Tak. Każda lekcja R Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Wprowadzenie do Plumber i REST
- Tworzenie endpointów GET i POST
- Uwierzytelnianie i bezpieczeństwo API
- Wdrażanie API Plumber na produkcji