การยืนยันตัวตนและความปลอดภัยของ API
เพิ่มการตรวจสอบคีย์ API ส่วนหัว CORS และตัวกรองจำกัดอัตรา
การยืนยันตัวตนและความปลอดภัยของ API เป็นบทเรียน R Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 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()— กำหนดค่าการเข้าถึงข้ามต้นทางจากเบราว์เซอร์- ตรวจสอบความถูกต้องของข้อมูลนำเข้าก่อนการดำเนินการกับ DB หรือไฟล์ใด ๆ
Sys.getenv()สำหรับค่า секрет — อย่าฝังคีย์ไว้ในโค้ดโดยตรง
คำถามที่พบบ่อย
บทเรียน “การยืนยันตัวตนและความปลอดภัยของ API” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การยืนยันตัวตนและความปลอดภัยของ API” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส R Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส R Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การยืนยันตัวตนและความปลอดภัยของ API”
เพิ่มการตรวจสอบคีย์ API ส่วนหัว CORS และตัวกรองจำกัดอัตรา คุณปฏิบัติ R Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน R Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน R Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “การยืนยันตัวตนและความปลอดภัยของ API” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน R Academy นี้ได้ไหม
ได้ บทเรียน R Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- แนะนำ Plumber และ REST
- การสร้างจุดปลายทาง GET และ POST
- การยืนยันตัวตนและความปลอดภัยของ API
- การนำ Plumber API ไปใช้งานจริง