ฉันพยายามแก้ไขปัญหาที่คล้ายกัน ผู้ใช้ของฉันต้องได้รับการตรวจสอบสิทธิ์สำหรับทุกคำขอที่พวกเขาทำ ฉันได้มุ่งเน้นไปที่การรับรองความถูกต้องของผู้ใช้อย่างน้อยหนึ่งครั้งโดยแอพพลิเคชั่นแบ็กเอนด์ (การตรวจสอบโทเค็น JWT) แต่หลังจากนั้นฉันตัดสินใจว่าฉันไม่ควรต้องการแบ็กเอนด์อีกต่อไป
ฉันเลือกที่จะหลีกเลี่ยงการต้องการปลั๊กอิน Nginx ที่ไม่ได้รวมอยู่ในค่าเริ่มต้น มิฉะนั้นคุณสามารถตรวจสอบการเขียนสคริปต์ nginx-jwt หรือ Lua และสิ่งเหล่านี้อาจเป็นวิธีแก้ปัญหาที่ยอดเยี่ยม
ที่อยู่รับรองความถูกต้อง
จนถึงตอนนี้ฉันได้ทำสิ่งต่อไปนี้แล้ว:
auth_request
มอบหมายรับรองความถูกต้องในการใช้ Nginx สิ่งนี้เรียกinternal
ตำแหน่งที่ส่งการร้องขอไปยังจุดตรวจสอบความถูกต้องของโทเค็นแบ็กเอนด์ของฉัน เพียงอย่างเดียวไม่ได้แก้ไขปัญหาของการจัดการการตรวจสอบจำนวนมากเลย
ผลลัพธ์ของการตรวจสอบความถูกต้องของโทเค็นถูกแคชโดยใช้proxy_cache_key "$cookie_token";
คำสั่ง เมื่อตรวจสอบโทเค็นได้สำเร็จแบ็กเอนด์จะเพิ่มCache-Control
คำสั่งที่บอกให้ Nginx แคชเฉพาะโทเค็นนานสูงสุด 5 นาที ณ จุดนี้โทเค็นการตรวจสอบความถูกต้องใด ๆ ที่อยู่ในแคชคำขอต่อมาจากผู้ใช้ / โทเค็นเดียวกันจะไม่ได้รับแบ็กเอนด์รับรองความถูกต้องอีกต่อไป!
เพื่อป้องกันแอปพลิเคชันแบ็กเอนด์ของฉันจากน้ำท่วมที่อาจเกิดขึ้นจากโทเค็นที่ไม่ถูกต้องฉันยังแคชปฏิเสธการตรวจสอบเมื่อปลายทางแบ็กเอนด์ของฉันส่งกลับ 401 รายการเหล่านี้จะถูกแคชในระยะเวลาสั้น ๆ เท่านั้น
ฉันได้เพิ่มการปรับปรุงเพิ่มเติมสองสามอย่างเช่นจุดสิ้นสุดการล็อกเอาต์ที่ทำให้โทเค็นใช้ไม่ได้โดยส่งคืน 401 (ซึ่งยังถูกแคชโดย Nginx) ดังนั้นหากผู้ใช้คลิกการล็อกเอาต์โทเค็นจะไม่สามารถใช้ได้อีกต่อไป
นอกจากนี้แคช Nginx ของฉันยังมีสำหรับทุกโทเค็นผู้ใช้ที่เกี่ยวข้องเป็นวัตถุ JSON ซึ่งช่วยฉันในการดึงข้อมูลจากฐานข้อมูลถ้าฉันต้องการข้อมูลนี้ และยังช่วยฉันในการถอดรหัสโทเค็น
เกี่ยวกับอายุการใช้โทเค็นและการรีเฟรชโทเค็น
หลังจาก 5 นาทีโทเค็นจะหมดอายุในแคชดังนั้นแบ็คเอนด์จะถูกสอบถามอีกครั้ง นี่คือเพื่อให้แน่ใจว่าคุณสามารถทำให้โทเค็นเป็นโมฆะได้เนื่องจากผู้ใช้ล็อกเอาต์เพราะมันถูกบุกรุกและอื่น ๆ การตรวจสอบความถูกต้องเป็นระยะดังกล่าวมีการใช้งานที่เหมาะสมในแบ็กเอนด์หลีกเลี่ยงฉันต้องใช้โทเค็นรีเฟรช
การรีเฟรชโทเค็นตามธรรมเนียมจะใช้เพื่อขอโทเค็นการเข้าถึงใหม่ พวกเขาจะถูกเก็บไว้ในแบ็กเอนด์ของคุณและคุณจะตรวจสอบว่ามีการร้องขอโทเค็นการเข้าถึงด้วยโทเค็นการรีเฟรชที่ตรงกับที่คุณมีในฐานข้อมูลสำหรับผู้ใช้เฉพาะนี้ หากผู้ใช้ออกจากระบบหรือโทเค็นถูกบุกรุกคุณจะลบ / ทำให้โทเค็นการรีเฟรชในฐานข้อมูลของคุณไม่ได้ดังนั้นการร้องขอโทเค็นใหม่โดยใช้โทเค็นการรีเฟรชที่ไม่ถูกต้องจะล้มเหลว
กล่าวโดยย่อโทเค็นการรีเฟรชมักจะมีอายุการใช้งานนานและจะถูกตรวจสอบกับแบ็กเอนด์เสมอ พวกเขาจะใช้ในการสร้างโทเค็นการเข้าถึงที่มีความถูกต้องสั้นมาก (ไม่กี่นาที) โดยปกติโทเค็นการเข้าถึงเหล่านี้จะเข้าถึงแบ็กเอนด์ของคุณ แต่คุณจะตรวจสอบลายเซ็นและวันที่หมดอายุเท่านั้น
ที่นี่ในการตั้งค่าของเราเราใช้โทเค็นที่มีความถูกต้องนานขึ้น (อาจเป็นชั่วโมงหรือต่อวัน) ที่มีบทบาทและคุณสมบัติเหมือนกันทั้งโทเค็นการเข้าถึงและโทเค็นการรีเฟรช เนื่องจากเรามีการตรวจสอบและการตรวจสอบความถูกต้องของพวกเขาโดย Nginx พวกเขาจะได้รับการตรวจสอบอย่างสมบูรณ์โดยแบ็กเอนด์ทุกๆ 5 นาที ดังนั้นเราจึงได้รับประโยชน์จากการใช้โทเค็นการรีเฟรช (สามารถทำให้โทเค็นเป็นโมฆะได้อย่างรวดเร็ว) โดยไม่มีความซับซ้อนเพิ่มขึ้น และการตรวจสอบอย่างง่ายจะไม่ถึงแบ็กเอนด์ของคุณที่มีขนาดอย่างน้อย 1 ออร์เดอร์ที่ช้ากว่าแคช Nginx แม้ว่าจะใช้สำหรับการตรวจสอบลายเซ็นและวันหมดอายุเท่านั้น
ด้วยการตั้งค่านี้ฉันสามารถปิดใช้งานการรับรองความถูกต้องในแบ็กเอนด์ของฉันเนื่องจากคำขอที่เข้ามาทั้งหมดมาถึงauth_request
คำสั่ง Nginx ก่อนที่จะสัมผัส
สิ่งนี้ไม่ได้แก้ปัญหาอย่างสมบูรณ์หากคุณต้องการดำเนินการอนุญาตตามประเภททรัพยากร แต่อย่างน้อยคุณได้บันทึกส่วนการอนุญาตพื้นฐานแล้ว และคุณสามารถหลีกเลี่ยงการถอดรหัสโทเค็นหรือทำการค้นหาฐานข้อมูลเพื่อเข้าถึงข้อมูลโทเค็นได้เนื่องจาก Nginx แคชการตอบสนองการรับรองความถูกต้องสามารถมีข้อมูลและส่งกลับไปที่แบ็กเอนด์
ตอนนี้ความกังวลที่ใหญ่ที่สุดของฉันคือฉันอาจจะทำลายบางสิ่งบางอย่างที่เกี่ยวข้องกับการรักษาความปลอดภัยโดยไม่ต้องตระหนักถึงมัน โทเค็นที่ได้รับใด ๆ ยังคงถูกตรวจสอบอย่างน้อยหนึ่งครั้งก่อนที่ Nginx จะถูกแคช โทเค็น tempered ใด ๆ จะแตกต่างกันดังนั้นจะไม่ตีแคชเนื่องจากคีย์แคชจะแตกต่างกัน
นอกจากนี้อาจเป็นมูลค่าการกล่าวขวัญว่าการพิสูจน์ตัวตนในโลกแห่งความเป็นจริงจะต่อสู้กับโทเค็นขโมยโดยการสร้าง (และตรวจสอบ) Nonce เพิ่มเติมหรือบางสิ่งบางอย่าง
นี่เป็นสารสกัดแบบเรียบง่ายของการกำหนดค่า Nginx ของฉันสำหรับแอปของฉัน:
# Cache for internal auth checks
proxy_cache_path /usr/local/var/nginx/cache/auth levels=1:2 keys_zone=auth_cache:10m max_size=128m inactive=10m use_temp_path=off;
# Cache for content
proxy_cache_path /usr/local/var/nginx/cache/resx levels=1:2 keys_zone=content_cache:16m max_size=128m inactive=5m use_temp_path=off;
server {
listen 443 ssl http2;
server_name ........;
include /usr/local/etc/nginx/include-auth-internal.conf;
location /api/v1 {
# Auth magic happens here
auth_request /auth;
auth_request_set $user $upstream_http_X_User_Id;
auth_request_set $customer $upstream_http_X_Customer_Id;
auth_request_set $permissions $upstream_http_X_Permissions;
# The backend app, once Nginx has performed internal auth.
proxy_pass http://127.0.0.1:5000;
proxy_set_header X-User-Id $user;
proxy_set_header X-Customer-Id $customer;
proxy_set_header X-Permissions $permissions;
# Cache content
proxy_cache content_cache;
proxy_cache_key "$request_method-$request_uri";
}
location /api/v1/Logout {
auth_request /auth/logout;
}
}
ตอนนี้นี่คือการแยกการกำหนดค่าสำหรับ/auth
จุดสิ้นสุดภายในซึ่งรวมอยู่ด้านบนเป็น/usr/local/etc/nginx/include-auth-internal.conf
:
# Called before every request to backend
location = /auth {
internal;
proxy_cache auth_cache;
proxy_cache_methods GET HEAD POST;
proxy_cache_key "$cookie_token";
# Valid tokens cache duration is set by backend returning a properly set Cache-Control header
# Invalid tokens are shortly cached to protect backend but not flood Nginx cache
proxy_cache_valid 401 30s;
# Valid tokens are cached for 5 minutes so we can get the backend to re-validate them from time to time
proxy_cache_valid 200 5m;
proxy_pass http://127.0.0.1:1234/auth/_Internal;
proxy_set_header Host ........;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Accept application/json;
}
# To invalidate a not expired token, use a specific backend endpoint.
# Then we cache the token invalid/401 response itself.
location = /auth/logout {
internal;
proxy_cache auth_cache;
proxy_cache_key "$cookie_token";
# Proper caching duration (> token expire date) set by backend, which will override below default duration
proxy_cache_valid 401 30m;
# A Logout requests forces a cache refresh in order to store a 401 where there was previously a valid authorization
proxy_cache_bypass 1;
# This backend endpoint always returns 401, with a cache header set to the expire date of the token
proxy_pass http://127.0.0.1:1234/auth/_Internal/Logout;
proxy_set_header Host ........;
proxy_pass_request_body off;
}
.
การระบุการให้บริการเนื้อหา
ตอนนี้การตรวจสอบจะถูกแยกออกจากข้อมูล เนื่องจากคุณบอกว่ามันเหมือนกันสำหรับผู้ใช้ทุกคนเนื้อหาของตัวเองสามารถถูกแคชโดย Nginx (ในตัวอย่างของฉันในcontent_cache
โซน)
scalability
สถานการณ์สมมตินี้ใช้งานได้ดีมากนอกกรอบโดยสมมติว่าคุณมีเซิร์ฟเวอร์ Nginx หนึ่งตัว ในสถานการณ์โลกแห่งความเป็นจริงคุณอาจมีความพร้อมใช้งานสูงหมายถึงอินสแตนซ์ Nginx หลายรายการซึ่งอาจโฮสต์แอปพลิเคชันแบ็กเอนด์ (Laravel) ของคุณ ในกรณีดังกล่าวคำขอใด ๆ ที่ผู้ใช้ของคุณสามารถส่งไปยังเซิร์ฟเวอร์ Nginx ใด ๆ ของคุณและจนกว่าพวกเขาทั้งหมดจะแคชโทเค็นในเครื่องพวกเขาจะไปถึงแบ็กเอนด์ของคุณเพื่อตรวจสอบ สำหรับเซิร์ฟเวอร์จำนวนน้อยการใช้โซลูชันนี้จะยังคงก่อให้เกิดประโยชน์มากมาย
อย่างไรก็ตามสิ่งสำคัญคือต้องทราบว่าด้วยเซิร์ฟเวอร์ Nginx หลายแห่ง (และแคช) คุณสูญเสียความสามารถในการออกจากระบบทางฝั่งเซิร์ฟเวอร์เพราะคุณไม่สามารถล้าง (โดยการบังคับให้รีเฟรช) แคชโทเค็นในทุกรายการเช่น/auth/logout
ในตัวอย่างของฉัน คุณเหลือระยะเวลาแคชโทเค็นเพียง 5 ล้านเท่านั้นซึ่งจะบังคับให้แบ็กเอนด์ของคุณถูกสอบถามในไม่ช้าและจะบอก Nginx ว่าคำขอนั้นถูกปฏิเสธ วิธีแก้ปัญหาบางส่วนคือการลบส่วนหัวโทเค็นหรือคุกกี้บนไคลเอนต์เมื่อออกจากระบบ
ความคิดเห็นใด ๆ จะได้รับการต้อนรับและชื่นชมมาก!