Saya sedang mengerjakan aplikasi web yang harus menangani impuls sangat besar dari pengguna simultan, yang perlu diotorisasi, untuk meminta konten yang identik. Dalam kondisi saat ini, itu benar-benar melumpuhkan bahkan untuk contoh AWS 32-core.
(Perhatikan bahwa kami menggunakan Nginx sebagai proxy terbalik)
Respons tidak dapat dengan mudah di-cache karena, dalam kasus terburuk, kita harus memeriksa apakah pengguna diautentikasi dengan mendekode JWT mereka. Ini mengharuskan kami menjalankan Laravel 4, yang sebagian besar akan setuju, lambat , bahkan dengan PHP-FPM dan OpCache diaktifkan. Ini sebagian besar disebabkan oleh fase bootstrap yang lumayan.
Orang mungkin bertanya, "Mengapa Anda menggunakan PHP dan Laravel sejak awal jika Anda tahu ini akan menjadi masalah?" - tapi sekarang sudah terlambat untuk kembali pada keputusan itu!
Kemungkinan Solusi
Salah satu solusi yang telah diajukan adalah mengekstraksi modul Auth dari Laravel ke modul eksternal ringan (ditulis dalam sesuatu yang cepat seperti C) yang tanggung jawabnya adalah untuk memecahkan kode JWT dan memutuskan apakah pengguna diautentikasi.
Alur permintaan adalah:
- Periksa apakah cache hit (jika tidak lulus ke PHP seperti biasa)
- Pecahkan kode token
- Periksa apakah itu valid
- Jika valid , sajikan dari cache
- Jika tidak valid , beri tahu Nginx, dan kemudian Nginx akan meneruskan permintaan ke PHP untuk menangani seperti biasa.
Ini akan memungkinkan kami untuk tidak menekan PHP begitu kami telah melayani permintaan ini untuk satu pengguna dan sebaliknya menjangkau modul ringan untuk dipusingkan dengan decoding JWT dan peringatan lain yang datang dengan jenis auth.
Saya bahkan berpikir untuk menulis kode ini secara langsung sebagai modul ekstensi HTTP Nginx.
Kekhawatiran
Kekhawatiran saya adalah bahwa saya belum pernah melihat ini dilakukan sebelumnya dan bertanya-tanya apakah ada cara yang lebih baik.
Juga, saat Anda menambahkan konten spesifik pengguna ke halaman, itu benar-benar membunuh metode ini.
Apakah ada solusi sederhana lain yang tersedia langsung di Nginx? Atau apakah kita harus menggunakan sesuatu yang lebih khusus seperti Varnish?
Pertanyaan saya:
Apakah solusi di atas masuk akal?
Bagaimana ini biasanya didekati?
Apakah ada cara yang lebih baik untuk mencapai perolehan kinerja yang serupa atau lebih baik?
sumber
Jawaban:
Saya sudah mencoba untuk mengatasi masalah serupa. Pengguna saya perlu diautentikasi untuk setiap permintaan yang mereka buat. Saya telah fokus untuk mendapatkan pengguna diautentikasi setidaknya sekali oleh aplikasi backend (validasi token JWT), tetapi setelah itu, saya memutuskan saya tidak perlu memerlukan backend lagi.
Saya memilih untuk menghindari memerlukan plugin Nginx yang tidak disertakan secara default. Kalau tidak, Anda dapat memeriksa skrip nginx-jwt atau Lua dan ini mungkin akan menjadi solusi yang bagus.
Mengatasi otentikasi
Sejauh ini saya sudah melakukan yang berikut:
Mendelegasikan otentikasi ke Nginx menggunakan
auth_request
. Ini panggilaninternal
lokasi yang melewati permintaan ke titik akhir validasi token backend saya. Ini saja tidak membahas masalah penanganan sejumlah besar validasi sama sekali.Hasil validasi token di-cache menggunakan
proxy_cache_key "$cookie_token";
direktif. Setelah validasi token berhasil, backend menambahkanCache-Control
arahan yang memberitahu Nginx untuk hanya men-cache token hingga 5 menit. Pada titik ini, setiap token autentikasi yang divalidasi ada di cache, permintaan selanjutnya dari pengguna / token yang sama tidak menyentuh backend auth lagi!Untuk melindungi aplikasi backend saya dari potensi banjir oleh token yang tidak valid, saya juga cache validasi ditolak, ketika endpoint backend saya mengembalikan 401. Yang ini hanya di-cache untuk jangka waktu singkat untuk menghindari kemungkinan mengisi cache Nginx dengan permintaan seperti itu.
Saya telah menambahkan beberapa peningkatan tambahan seperti titik akhir logout yang membatalkan token dengan mengembalikan 401 (yang juga di-cache oleh Nginx) sehingga jika pengguna mengklik logout, token tidak dapat digunakan lagi bahkan jika itu tidak kedaluwarsa.
Juga, cache Nginx saya berisi untuk setiap token, pengguna terkait sebagai objek JSON, yang menyelamatkan saya dari mengambilnya dari DB jika saya memerlukan informasi ini; dan juga menyelamatkan saya dari mendekripsi token.
Tentang token seumur hidup dan menyegarkan token
Setelah 5 menit, token akan kedaluwarsa dalam cache, jadi backend akan ditanyai lagi. Ini untuk memastikan bahwa Anda dapat membatalkan token, karena pengguna logout, karena telah dikompromikan, dan sebagainya. Validasi ulang berkala tersebut, dengan implementasi yang tepat di backend, menghindari saya harus menggunakan token penyegaran.
Token penyegaran secara tradisional akan digunakan untuk meminta token akses baru; mereka akan disimpan di backend Anda dan Anda akan memverifikasi bahwa permintaan untuk token akses dibuat dengan token penyegaran yang cocok dengan yang Anda miliki di database untuk pengguna khusus ini. Jika pengguna keluar, atau token dikompromikan, Anda akan menghapus / membatalkan token penyegaran dalam DB Anda sehingga permintaan berikutnya untuk token baru menggunakan token penyegaran tidak valid akan gagal.
Singkatnya, token penyegaran biasanya memiliki validitas panjang dan selalu diperiksa terhadap backend. Mereka digunakan untuk menghasilkan token akses yang memiliki validitas yang sangat singkat (beberapa menit). Token akses ini biasanya mencapai backend Anda, tetapi Anda hanya memeriksa tanda tangan dan tanggal kedaluwarsanya.
Di sini, di pengaturan saya, kami menggunakan token dengan validitas yang lebih lama (bisa berjam-jam atau sehari), yang memiliki peran dan fitur yang sama seperti token akses dan token refresh. Karena kami memiliki cache validasi dan pembatalannya oleh Nginx, mereka hanya sepenuhnya diverifikasi oleh backend setiap 5 menit sekali. Jadi kami tetap memanfaatkan penggunaan token penyegaran (dapat dengan cepat membatalkan token) tanpa kerumitan tambahan. Dan validasi sederhana tidak pernah mencapai backend Anda yang setidaknya 1 urutan besarnya lebih lambat dari cache Nginx, bahkan jika digunakan hanya untuk memeriksa tanda tangan dan tanggal kadaluwarsa.
Dengan pengaturan ini, saya dapat menonaktifkan otentikasi di backend saya, karena semua permintaan masuk mencapai
auth_request
arahan Nginx sebelum menyentuhnya.Itu tidak sepenuhnya menyelesaikan masalah jika Anda perlu melakukan segala jenis otorisasi per sumber daya, tetapi setidaknya Anda telah menyimpan bagian otorisasi dasar. Dan Anda bahkan dapat menghindari mendekripsi token atau melakukan pencarian DB untuk mengakses data token karena respons autentik yang di-cache Nginx dapat berisi data dan meneruskannya kembali ke backend.
Sekarang, kekhawatiran terbesar saya adalah bahwa saya mungkin melanggar sesuatu yang jelas terkait dengan keamanan tanpa menyadarinya. Yang sedang berkata, setiap token yang diterima masih divalidasi setidaknya sekali sebelum di-cache oleh Nginx. Setiap token yang di-tempered akan berbeda sehingga tidak menekan cache karena kunci cache juga akan berbeda.
Juga, mungkin perlu disebutkan bahwa otentikasi dunia nyata akan berjuang melawan pencurian token dengan menghasilkan (dan memverifikasi) Nonce tambahan atau sesuatu.
Berikut adalah ekstrak disederhanakan dari konfigurasi Nginx saya untuk aplikasi saya:
Sekarang, inilah ekstrak konfigurasi untuk
/auth
titik akhir internal , termasuk di atas sebagai/usr/local/etc/nginx/include-auth-internal.conf
:.
Mengatasi penyajian konten
Sekarang otentikasi dipisahkan dari data. Karena Anda memberi tahu itu identik untuk setiap pengguna, konten itu sendiri juga bisa di-cache oleh Nginx (dalam contoh saya, di
content_cache
zona).Skalabilitas
Skenario ini berfungsi dengan baik dengan asumsi Anda memiliki satu server Nginx. Dalam skenario dunia nyata Anda mungkin memiliki ketersediaan tinggi, yang berarti beberapa instance Nginx, berpotensi juga menjadi hosting aplikasi backend (Laravel) Anda. Dalam hal itu, permintaan apa pun yang dibuat pengguna Anda dapat dikirim ke server Nginx Anda, dan sampai mereka semua memiliki cache token lokal, mereka akan terus menjangkau backend Anda untuk memverifikasinya. Untuk sejumlah kecil server, menggunakan solusi ini masih akan membawa manfaat besar.
Namun, penting untuk dicatat bahwa dengan beberapa server Nginx (dan karenanya cache) Anda kehilangan kemampuan untuk logout di sisi server karena Anda tidak dapat membersihkan (dengan memaksa penyegaran) cache token pada semua server tersebut, seperti
/auth/logout
lakukan dalam contoh saya. Anda hanya tersisa dengan durasi cache token 5 juta yang akan memaksa backend Anda segera ditanyakan, dan akan memberi tahu Nginx bahwa permintaan ditolak. Solusi parsial adalah dengan menghapus token header atau cookie pada klien saat logout.Setiap komentar akan sangat disambut dan dihargai!
sumber