0Pricing
R Academy · レッスン

認証と API セキュリティ

API キーの検証、CORS ヘッダー、レート制限フィルターを追加します。

「認証と API セキュリティ」はCoddyKit上の無料R Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはR Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 R Academyコースには全4レッスンが含まれています。

APIセキュリティが重要な理由

Plumber APIは公開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による認証のスキップ

ヘルスチェックや公開ドキュメントなど、一部のエンドポイントでは認証をスキップする必要があります。authがフィルター名と一致する場所で、#* @preempt 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')
# }

pr_cors()によるCORS

APIを別のドメインにあるブラウザーから呼び出す場合は、CORS(Cross-Origin Resource Sharing、オリジン間リソース共有)を有効にする必要があります。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ごとのリクエスト数を追跡するフィルターを実装できます。

  • R環境に、IPごとの各リクエストのタイムスタンプを記録する
  • ウィンドウ内の回数が上限を超えたら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セキュリティのまとめ

Plumber APIの保護には、多層的な防御が必要です。

  • pr_filter('auth', ...) — ミドルウェアですべてのリクエストを調べる
  • req$HTTP_AUTHORIZATION / req$HTTP_X_API_KEY — 認証ヘッダーを読み取る
  • #* @preempt auth — 公開エンドポイントでは認証をスキップする
  • pr_cors() — ブラウザーからのオリジン間アクセスを設定する
  • データベースやファイルを操作する前に入力値を検証する
  • シークレットにはSys.getenv()を使用し、キーを決してハードコードしない

よくある質問

「認証と API セキュリティ」レッスンは無料ですか?

はい。「認証と API セキュリティ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、R Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 R Academyコースには全4レッスンが含まれています。

「認証と API セキュリティ」で何を学びますか?

API キーの検証、CORS ヘッダー、レート制限フィルターを追加します。 ブラウザで直接実行するハンズオンコードでR Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

R Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのR Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「認証と API セキュリティ」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このR Academyレッスンでコードを書いて実行できますか?

はい。すべてのR Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Plumber と REST の基礎
  2. GET エンドポイントと POST エンドポイントの作成
  3. 認証と API セキュリティ
  4. Plumber API を本番環境にデプロイする
← R Academyに戻る