Masalah dan Konteks Berkenaan
Produk anda perlu membaca kalendar pihak ketiga bagi pihak pengguna tanpa mengendalikan kata laluan kalendar pengguna tersebut. Terangkan aliran kod keizinan OAuth 2.0 yang lengkap dengan PKCE untuk klien awam, termasuk apa yang mesti disahkan oleh bahagian belakang aplikasi dan pelayan keizinan.
Andaikan klien ialah aplikasi satu halaman (SPA) pelayar atau aplikasi natif. Ia tidak boleh menyimpan satu kelayakan statik secara sulit merentasi semua tika (instance) yang digunakan dengan andal, jadi ia adalah klien awam. Pelayan keizinan mengesahkan pengguna, mengumpulkan persetujuan, dan mengeluarkan token. Pelayan sumber mendedahkan API kalendar. Skop awal ialah akses kalendar baca sahaja; pengeluaran token segar semula (refresh token) bergantung pada dasar pelayan keizinan.
Panduan temu duga backend awam yang diterbitkan pada Mac 2026 meminta calon menerangkan geran kod keizinan, PKCE, klien awam dan sulit, serta sempadan OAuth berbanding OpenID Connect. Garis dasar keselamatan semasa ialah Amalan Terbaik Semasa Keselamatan OAuth 2.0 (OAuth 2.0 Security Best Current Practice): klien awam mesti menggunakan PKCE, klien sulit juga disyorkan untuk menggunakannya, pelayan keizinan mesti menghalang penurunan taraf (downgrade) PKCE, dan URI pengalihan berdaftar memerlukan pemadanan tepat.
Ini ialah soalan backend kerana kemahiran utamanya ialah mereka bentuk dan mengesahkan protokol keizinan rentas perkhidmatan, sempadan token, dan kawalan keselamatan. Pengalihan pelayar hanyalah pengangkutannya (transport).
Perkara yang Dinilai oleh Penemu Duga
Pertama, bolehkah calon memisahkan empat peranan tersebut? Pemilik sumber ialah pengguna, klien ialah produk pengagregatan kalendar, pelayan keizinan mengendalikan pengesahan, persetujuan, dan pengeluaran token, manakala pelayan sumber memiliki data kalendar. Mengelirukan klien dengan pengguna menyebabkan setiap kenyataan seterusnya tentang kebenaran dan audiens token menyimpang.
Kedua, bolehkah calon menerangkan mengapa aliran kod keizinan mempunyai dua fasa (legs)? Pelayar hanya menerima kod keizinan jangka pendek dan guna sekali. Klien menukarnya di titik akhir token, jadi token akses tidak melalui URL pengalihan pelayar. PKCE kemudian mengikat rahsia rawak yang dicipta untuk permintaan keizinan kepada pertukaran kod tersebut. Pihak yang memintas kod tersebut tetapi tidak mempunyai code_verifier masih tidak boleh menebusnya.
Ketiga, bolehkah calon memastikan tanggungjawab setiap kawalan adalah tepat?
| Kawalan | Ikatan utama | Masalah utama yang ditangani |
|---|---|---|
state | Memulakan sesi klien ke panggilan balik | Korelasi permintaan dan CSRF log masuk |
| PKCE | Permintaan keizinan ke pertukaran token | Pemintasan kod dan suntikan kod |
| Pengesahan klien | Klien sulit ke pelayan keizinan | Identiti klien yang menebus kod |
| URI pengalihan tepat | Klien berdaftar ke panggilan balik yang dibenarkan | Penghantaran kod ke titik akhir yang dikawal penyerang |
OIDC nonce | Permintaan log masuk ke ID Token | Main semula (replay) ID Token dan korelasi sesi log masuk |
Akhir sekali, jawapan yang kukuh menyatakan sempadan PKCE. Ia tidak menghalang XSS asal yang sama (same-origin) daripada membaca pengesah (verifier) atau token. Ia tidak menggantikan TLS, pengesahan pengalihan, keistimewaan paling sedikit (least privilege), penyimpanan token yang selamat, atau protokol pengesahan.
Soalan Penjelasan Sebelum Menjawab
- Adakah klien awam atau sulit? SPA atau aplikasi natif tidak boleh membuktikan identiti dengan
rahsia klien statik. Aplikasi web dengan backend terkawal boleh menjadi sulit dan mesti mengesahkan identiti di titik akhir token selain menggunakan PKCE.
- Adakah matlamatnya keizinan API atau log masuk produk? Akses API Kalendar menggunakan OAuth. Jika
produk memerlukan identiti pengguna, gunakan OIDC dan sahkan ID Token dan bukannya menyimpulkan identiti daripada token akses.
- Adakah klien menggunakan satu pelayan keizinan atau penyedia identiti yang dikonfigurasikan oleh penyewa?
Pelbagai pengeluar memerlukan pengesahan pengeluar atau URI pengalihan yang berbeza untuk mengelakkan serangan kekeliruan (mix-up attacks).
- Adakah akses luar talian diperlukan? Jangan minta token segar semula jika ia tidak diperlukan. Jika ia
diperlukan, takrifkan penggiliran (rotation), pengesanan main semula, pembatalan (revocation), dan tamat tempoh.
- Di manakah keadaan transaksi panggilan balik disimpan? Kekalkan ikatan
state → code_verifier.
Satu pengesah global merosakkan tab serentak; meletakkannya dalam URL atau log memusnahkan kerahsiaan.
- Berapa banyak akses kalendar yang diperlukan? Mulakan dengan skop baca sahaja yang diperlukan oleh ciri tersebut.
Kebenaran menulis atau seluruh akaun memerlukan justifikasi konkrit.
Rangka Kerja Jawapan 30 Saat
“Bagi setiap percubaan keizinan, saya menjana state yang tidak dapat diramal dan code_verifier berentropi tinggi, kemudian memperoleh code_challenge S256. Pelayar pergi ke titik akhir keizinan dengan response_type=code, ID klien, URI pengalihan yang didaftarkan secara tepat, skop minimum, state, dan cabaran (challenge). Pengguna hanya mengesahkan diri di pelayan keizinan. Panggilan balik mengembalikan kod jangka pendek guna sekali dan state yang sama. Klien mengesahkan state, memuatkan pengesah transaksi tersebut, dan menghantar kod, URI pengalihan, serta pengesah ke titik akhir token. Pelayan mengeluarkan token akses hanya selepas mengesahkan kod, klien, URI pengalihan, dan ikatan cabaran. Klien awam tidak boleh menganggap rahsia statik sebagai kelayakan; klien sulit juga mengesahkan identiti. PKCE melindungi pertukaran kod, state menghubungkaitkan permintaan, dan ID Token OIDC adalah perkara yang menyokong identiti log masuk.”
Penyelaman Mendalam Langkah demi Langkah
Langkah satu: cipta transaksi keizinan guna sekali.
Bagi setiap tindakan "sambung kalendar", klien menjana dua nilai rawak bebas:
stateialah ID transaksi yang tidak dapat diteka yang terikat pada sesi pengguna semasa, pengeluar yang dijangka,
URI panggilan balik, dan destinasi pascakeizinan.
code_verifierdijana dengan sumber rawak selamat secara kriptografi. RFC 7636 mentakrifkannya
sebagai 43 hingga 128 aksara tidak dikhaskan dan mengesyorkan sekurang-kurangnya 256 bit entropi.
Klien memperoleh cabaran seperti berikut:
code_challenge = base64url_without_padding(
SHA256(ASCII(code_verifier))
)
code_challenge_method = S256Simpan rekod state → code_verifier yang berasingan untuk setiap transaksi. Dua tab pelayar boleh memulakan dua sambungan kalendar secara serentak, jadi "pengesah terkini" bukanlah model data yang sah. Berikan transaksi tempoh luput yang singkat dan padamkannya selepas berjaya atau kegagalan muktamad. Jangan letakkan pengesah dalam URL pengalihan, peristiwa analitik, atau log aplikasi.
Langkah dua: bina permintaan keizinan.
Klien menghantar ejen pengguna ke titik akhir keizinan:
GET /authorize
?response_type=code
&client_id=calendar-client
&redirect_uri=registered-callback
&scope=calendar.read
&state=random-transaction-id
&code_challenge=derived-challenge
&code_challenge_method=S256Pelayan keizinan mengesahkan ID klien, jenis respons, skop, dan URI pengalihan. URI pengalihan mesti sepadan tepat dengan nilai yang dipra-daftar dan bukannya domain yang luas, laluan kecil (subpath) sewenang-wenangnya, atau parameter pemajuan yang dikawal pengguna. Kelayakan pengguna diserahkan hanya kepada pelayan keizinan. Klien menerima hasil keizinan, bukan kata laluan pengguna.
Langkah tiga: proses panggilan balik tanpa memanggil titik akhir token lagi.
Selepas persetujuan diberikan, pelayan keizinan mengalihkan ejen pengguna ke panggilan balik yang didaftarkan dengan code dan state yang asal. Klien terlebih dahulu mengesahkan transaksi tempatannya:
- State wujud, belum tamat tempoh, dan belum digunakan.
- Ia terikat pada sesi pelayar semasa dan pelayan keizinan yang dijangka.
- Panggilan balik tiba di titik akhir yang didaftarkan untuk transaksi ini.
- Respons ralat masih boleh menggunakan state untuk mencari dan menamatkan transaksi yang betul dengan selamat.
Batalkan jika berlaku ketidakpadanan state dan bukannya "mencuba permintaan token untuk melihat sama ada ia berfungsi." Kod keizinan mestilah berjangka pendek dan guna sekali. Penebusan berulang mesti gagal, dan pelayan keizinan harus membatalkan token yang telah dikeluarkan daripada kod tersebut jika boleh.
Langkah empat: tukar kod dengan pengesah.
Klien menghantar permintaan token ini:
POST /token
grant_type=authorization_code
code=returned-authorization-code
redirect_uri=registered-callback
client_id=calendar-client
code_verifier=stored-verifierPelayan keizinan membina semula transaksi asal: klien dan URI pengalihan mana kod tersebut dimiliki, sama ada ia telah tamat tempoh atau telah digunakan, sama ada permintaan keizinan mengandungi cabaran, dan sama ada penggunaan S256 pada pengesah ini sama dengan cabaran yang disimpan. Jika permintaan keizinan tidak mempunyai cabaran tetapi permintaan token mengemukakan pengesah, pelayan tidak boleh secara senyap menurunkan taraf transaksi kepada aliran bukan PKCE.
Klien awam menghantar ID klien untuk pengenalpastian, tetapi rahsia statik yang dihantar dalam kod awam tidak boleh mengesahkannya. Klien sulit juga menggunakan kaedah pengesahan klien berdaftarnya. PKCE melengkapkan dan bukannya menggantikan pengesahan tersebut.
Langkah lima: terangkan setiap kawalan melalui laluan serangan.
Katakan klien yang sah mencipta pengesah V1 dan cabaran C1. Penyerang memintas kod panggilan balik tetapi tidak mengetahui V1. Pengesah yang dipilih oleh penyerang tidak menghasilkan C1, jadi pertukaran token gagal. Itulah perlindungan teras PKCE terhadap pemintasan kod keizinan.
Sekarang andaikan penyerang memulakan transaksi keizinan yang berasingan dan menyuntik kod tersebut ke dalam panggilan balik mangsa. Transaksi mangsa mempunyai state dan pengesah yang berbeza. Korelasi state atau ikatan PKCE gagal, dan klien mesti membatalkannya. Pelayan keizinan juga mesti mengingati sama ada cabaran hadir untuk sesuatu kod, bagi menghalang penyerang daripada memadamkan cabaran dan mencetuskan penurunan taraf.
Jika penyerang menukar URI pengalihan ke titik akhir yang mereka kawal, pendaftaran tepat dan pemadanan rentetan tepat akan menolaknya semasa keizinan. Jika klien menyokong beberapa pelayan keizinan, ia juga mesti membandingkan pengeluar respons dengan pengeluar yang disimpan dalam transaksi atau menggunakan URI pengalihan yang berbeza untuk setiap pengeluar. Mengingati hanya URL titik akhir keizinan adalah tidak mencukupi untuk mengelakkan kekeliruan (mix-up).
Langkah enam: hadkan token kepada tujuan sebenar mereka.
Token akses mewakili keizinan dan bukan pernyataan identiti pengguna untuk klien. Pelayan sumber mengesahkan bahawa token tersebut ditujukan untuknya dan mengehadkannya kepada sumber dan tindakan yang diperlukan. Klien hanya meminta calendar.read, menggunakan token akses jangka pendek, dan menjauhkan token daripada URL, log, dan storan berterusan yang boleh diakses oleh skrip yang tidak berkaitan.
PKCE telah menyelesaikan tugasnya selepas kod menjadi token. Jika XSS boleh membaca memori SPA, storan transaksi, atau token akses, PKCE tidak dapat memulihkan rahsia tersebut. Aplikasi pelayar berisiko lebih tinggi boleh menggunakan BFF: token kekal dalam backend terkawal dan pelayar memegang kuki sesi HttpOnly. Ini mengurangkan pendedahan token kepada JavaScript tetapi menambah kos sesi kuki, CSRF, penskalaan backend, dan proksi API.
Jika klien awam menerima token segar semula, pelayan keizinan mesti mengesan main semula melalui kekangan penghantar (sender constraint) atau penggiliran token segar semula. Dengan penggiliran, setiap penyegaran mengeluarkan token segar semula baharu dan membatalkan yang lama. Penggunaan semula nilai lama menunjukkan kemungkinan kompromi, jadi keluarga geran (grant family) dibatalkan dan pengguna memberi keizinan semula.
Langkah tujuh: asingkan OAuth, OIDC, dan geran lain.
OAuth menjawab sama ada klien boleh mengakses sumber bagi pihak pengguna. OIDC menambah lapisan identiti di atas OAuth supaya klien boleh mengesahkan log masuk melalui ID Token. Panggilan balik OIDC juga memerlukan pengesahan tandatangan, pengeluar, audiens, tamat tempoh, dan nonce. Token akses ditujukan untuk pelayan sumber; ID Token ditujukan untuk klien. Kedua-duanya tidak boleh ditukar ganti.
Gunakan kelayakan klien (client credentials) apabila tiada pengguna mengambil bahagian dan perkhidmatan mengakses sumber di bawah kuasanya sendiri. Peranti input terhad boleh menggunakan aliran keizinan peranti (device authorization flow). Aliran tersirat (implicit flow) meletakkan token akses dalam respons keizinan, meningkatkan kebocoran URL dan pendedahan main semula; amalan terbaik semasa mengutamakan aliran pengembalian kod. Geran kelayakan kata laluan pemilik sumber mendedahkan kata laluan pengguna kepada klien dan dilarang oleh garis dasar keselamatan semasa.
Langkah lapan: sahkan kegagalan, bukan hanya laluan gembira (happy path).
| Ujian | Hasil yang dijangka |
|---|---|
| Tukar satu aksara pengesah | Pertukaran token gagal |
| Tebus kod yang sama dua kali | Pertukaran kedua gagal dan mencetuskan pengendalian keselamatan |
| State panggilan balik tiada, tamat tempoh, atau milik sesi lain | Klien menolak sebelum pertukaran token |
| Keizinan meniadakan cabaran tetapi permintaan token menghantar pengesah | Pelayan keizinan menolak penurunan taraf |
| Pengalihan berbeza hanya mengikut huruf besar/kecil, laluan, atau garis miring di hujung | Pemadanan tepat gagal |
| Dua tab memulakan aliran dan panggilan balik kembali tidak mengikut urutan | Setiap state mendapatkan pengesahnya sendiri |
| Tukar pengeluar selepas menyambungkan dua pelayan keizinan | Klien menolak kekeliruan tersebut |
| Imbas log dan analitik | Tiada kod, pengesah, token akses, atau token segar semula kelihatan |
| XSS membaca storan yang boleh diakses oleh pelayar | PKCE dinilai secara jelas tidak mencukupi; nilaikan BFF |
Pemantauan pengeluaran harus membezakan penafian keizinan, ketidakpadanan state, kegagalan PKCE, penggunaan semula kod, ketidakpadanan pengalihan, dan main semula token segar semula. Satu pembilang "OAuth gagal" menyembunyikan kedua-dua isyarat serangan dan kecacatan pelaksanaan klien.
Contoh Jawapan Berkualiti Tinggi
“Saya akan mengkhususkan skop ini sebagai klien awam yang mengakses API kalendar pihak ketiga. Ia tidak boleh melindungi rahsia kongsi, jadi rahsia yang dibenamkan dalam berkas SPA bukanlah pengesahan klien.
Pada permulaan setiap keizinan, saya menjana state yang tidak dapat diramal dan pengesah kod dengan sekurang-kurangnya 256 bit entropi, memperoleh cabaran S256, dan menyimpan pengesah, sesi pelayar, pengeluar, URI pengalihan, dan tamat tempoh di bawah state tersebut. Permintaan keizinan membawa jenis respons kod, ID klien, URI pengalihan yang didaftarkan secara tepat, skop baca sahaja, state, dan cabaran. Pengguna mengesahkan identiti dan memberikan persetujuan hanya di pelayan keizinan.
Apabila panggilan balik mengembalikan kod dan state, saya terlebih dahulu membuktikan bahawa state adalah milik sesi semasa dan belum digunakan, kemudian memuatkan pengesahnya. Permintaan token menghantar kod, URI pengalihan yang sama, ID klien, dan pengesah. Pelayan keizinan mengesahkan bahawa kod tersebut berjangka pendek, belum digunakan, dikeluarkan kepada klien dan URI pengalihan ini, dan bahawa nilai S256 pengesah adalah sama dengan cabaran sebelum mengeluarkan token akses. Kod yang dipintas tidak berguna tanpa pengesah. Pelayan juga menolak permintaan yang cuba mengalih keluar PKCE dan menurunkan taraf pertukaran.
State menghubungkaitkan permintaan pelayar, manakala PKCE mengikat pertukaran kod; saya tidak akan menggabungkan kedua-duanya menjadi satu parameter CSRF yang kabur. Aplikasi web sulit juga mengesahkan identiti di titik akhir token dan harus mengekalkan PKCE. Token akses hanya untuk pelayan sumber kalendar. Jika produk menggunakan aliran ini untuk log masuk, ia memerlukan OIDC dan mesti mengesahkan tandatangan, pengeluar, audiens, tamat tempoh, dan nonce ID Token.
Saya akan menguji pengesah yang salah, penggunaan semula kod, state rentas sesi, ketidakpadanan tepat pengalihan, tab tidak mengikut urutan, penurunan taraf PKCE, dan kekeliruan pengeluar, serta mengesahkan bahawa log tidak mengandungi sebarang kod atau token. PKCE tidak boleh menghalang XSS asal yang sama daripada mencuri pengesah atau token. Untuk klien pelayar berisiko tinggi, saya akan menilai BFF yang menyimpan token di bahagian pelayan dan hanya memberikan kuki sesi HttpOnly kepada pelayar.”
Kesilapan Biasa
- Menganggap ID klien sebagai rahsia → ID klien muncul dalam permintaan keizinan dan
hanya mengenal pasti klien → **gunakan PKCE untuk klien awam dan pengesahan klien sebenar untuk klien sulit.**
- Hanya mengatakan bahawa "kod lebih selamat daripada token" → ini tidak menerangkan sebab kod yang
dipintas tidak boleh ditebus → tunjukkan ikatan cabaran-pengesah dan perbandingan titik akhir token.
- Menggunakan
plainatau membenarkan PKCE ditinggalkan → pengesah boleh didedahkan atau penyerang
boleh mencetuskan penurunan taraf → gunakan S256 dan jadikan pelayan keizinan merekod serta menguatkuasakan PKCE.
- Menjana state tanpa mengikatnya pada sesi → nilai yang sah boleh dipindahkan dan
aliran serentak menimpa antara satu sama lain → **simpan pemetaan state guna sekali, sesi, pengeluar, dan pengesah.**
- Membenarkan pemadanan pengalihan kabur (fuzzy) → kod boleh sampai ke laluan kecil atau
pengalih yang dikawal penyerang → pra-daftar dan bandingkan URI pengalihan secara tepat.
- Menyahkod token akses dan menganggapnya sebagai identiti log masuk → audiens dan semantiknya mungkin
milik pelayan sumber → gunakan log masuk OIDC dan sahkan ID Token.
- Mendakwa bahawa PKCE menghalang XSS → XSS boleh membaca pengesah, kod, atau token yang dikeluarkan secara langsung →
kurangkan pendedahan token dalam pelayar, perbaiki XSS, dan nilaikan BFF mengikut risiko.
- Mencatat log kod, pengesah, atau token → diagnostik meluaskan pendedahan rahsia yang boleh ditebus
→ catat log ID transaksi, kelas ralat, dan nilai korelasi yang tidak boleh ditebus.
Soalan Susulan dan Maklum Balas
Susulan 1: Adakah aplikasi web tradisional dengan backend masih memerlukan PKCE?
Ya. Pengesahan klien di titik akhir token membuktikan bahawa klien sulit memegang kelayakan yang dilindunginya. PKCE mengikat permintaan keizinan ini kepada pertukaran kod ini dan seterusnya melindungi daripada suntikan dan penyalahgunaan kod. Kawalan-kawalan tersebut mengikat hubungan yang berbeza. Sesi bahagian pelayan boleh menyimpan pengesah dan state sementara pelayar hanya membawa pengecam sesi yang tidak dapat diramal.
Susulan 2: Apakah yang berubah jika "sambung kalendar" juga bermaksud "log masuk dengan kalendar"?
Gunakan OIDC dan bukannya menganggap token akses atau respons UserInfo sewenang-wenangnya sebagai bukti log masuk. Minta skop openid dan nonce. Sahkan tandatangan, pengeluar, audiens, tamat tempoh, dan nonce ID Token, kemudian gunakan pasangan pengeluar-subjek sebagai kunci identiti luaran. State terus mengikat transaksi pelayar; nonce mengikat permintaan pengesahan kepada ID Token.
Susulan 3: Apakah risiko baharu yang muncul apabila setiap penyewa SaaS mengkonfigurasi pengeluar yang berbeza?
Klien boleh menghantar respons daripada pelayan keizinan A ke titik akhir token pelayan keizinan B, menyebabkan kekeliruan (mix-up). Simpan pengeluar yang dijangka dalam transaksi dan sahkan pengeluar respons. Satu lagi pertahanan menggunakan URI pengalihan yang berbeza bagi setiap pengeluar dan mengesahkan bahawa respons tiba di titik akhir yang betul. Titik akhir keizinan dan token mesti datang daripada metadata pengeluar dipercayai yang sama.
Susulan 4: Bagaimanakah anda melindungi token segar semula untuk penyegerakan kalendar jangka panjang?
Berikan hanya skop yang diperlukan dan gunakan penggiliran token segar semula atau kekangan penghantar. Dengan penggiliran, jejaki keluarga token: setiap penyegaran membatalkan nilai lama, dan penggunaan semula token segar semula yang lama membatalkan keluarga aktif tersebut dan memerlukan keizinan baharu. Klien juga memerlukan storan selamat, laluan pembatalan, jangka hayat maksimum, dan tamat tempoh ketidakaktifan. PKCE melindungi pertukaran kod awal, bukan token segar semula yang dicuri kemudian.
Susulan 5: Mengapakah dua tab yang menyambungkan akaun kalendar berbeza gagal secara berselang-seli?
Punca biasa ialah satu state atau pengesah global: tab kedua menimpa tab pertama. Panggilan balik mesti mencari transaksi bebas mengikut state. Rekod tersebut mengandungi pengesah, akaun atau pengeluar yang dijangka, URI pengalihan, dan masa penciptaan. Gunakannya secara atomik. Tolak state yang tidak diketahui, pendua, dan tamat tempoh dan bukannya menggunakan pengesah terkini sebagai sandaran.