Django-rest-framework: DRF harus mengotorisasi semua permintaan OPSI secara default

Dibuat pada 21 Nov 2017  ·  22Komentar  ·  Sumber: encode/django-rest-framework

Mengikuti diskusi di #908

Izin untuk permintaan OPTIONS (metadata) tidak ditangani dengan benar di DRF.

Sesuai dengan spesifikasi W3C , semua permintaan OPTIONS preflight TIDAK diautentikasi, artinya pengguna akan selalu mendapatkan kesalahan 401 saat melakukan preflighting permintaan untuk titik akhir yang diautentikasi, karena browser modern tidak pernah mengirim ini sesuai spesifikasi:

Jika tidak, buat permintaan pra-penerbangan. Ambil URL permintaan dari Asal sumber asal menggunakan sumber perujuk sebagai ganti sumber perujuk dengan tanda pengalihan manual dan kumpulan tanda kuki blok, menggunakan metode OPTIONS , dan dengan batasan tambahan berikut:

  • Sertakan header Access-Control-Request-Method dengan nilai kolom sebagai header metode permintaan (bahkan jika itu adalah metode sederhana).
  • Jika tajuk permintaan penulis tidak kosong, sertakan tajuk Access-Control-Request-Headers dengan nilai bidang tajuk sebagai daftar nama bidang tajuk yang dipisahkan koma dari tajuk permintaan penulis dalam urutan leksikografis, masing-masing dikonversi ke huruf kecil ASCII (bahkan ketika satu atau lebih banyak adalah tajuk sederhana).
  • Kecualikan header permintaan penulis.
  • Kecualikan kredensial pengguna .
  • Kecualikan badan entitas permintaan.

Saya pikir ini harus menjadi perilaku default di sini.
DRF harus mengotorisasi semua permintaan OPTIONS secara default untuk kelas izin standar (IsAuthenticated, IsAdminUser, dll) dan pengguna dapat menimpa ini ketika mereka secara eksplisit perlu melindungi info metadata (melanggar kompatibilitas standar CORS)

Langkah-langkah untuk mereproduksi:

views.py

class MyView(APIView):

    permission_classes = (IsAuthenticated,)

urls.py

urlpatterns = [
    url(r'^myview/', MyView.as_view()),
]

menghibur

http GET http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

http OPTIONS http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

Perilaku yang diharapkan

http GET http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

http OPTIONS http://127.0.0.1:8000/myview/
HTTP/1.0 200 OK

Solusi sementara

class IsAuthenticated(permissions.IsAuthenticated):

    def has_permission(self, request, view):
        if request.method == 'OPTIONS':
            return True
        return super(IsAuthenticated, self).has_permission(request, view)

Komentar yang paling membantu

Versi DRF: 3.7.3
Python 3.6.3

Solusi sementara yang disebutkan di atas tidak berhasil untuk saya. Semua permintaan diautentikasi terlepas dari apakah itu PUT, POST, GET, OPTIONS, dll.

Ini berhasil mengubahnya menjadi:

# myapp/permissions.py
from rest_framework.permissions import IsAuthenticated

class AllowOptionsAuthentication(IsAuthenticated):
    def has_permission(self, request, view):
        if request.method == 'OPTIONS':
            return True
        return request.user and request.user.is_authenticated

Dan di settings.py:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.TokenAuthentication',),
    'DEFAULT_PERMISSION_CLASSES': (
        'myapp.permissions.AllowOptionsAuthentication',
    )
}

Semua 22 komentar

Versi DRF: 3.7.3
Python 3.6.3

Solusi sementara yang disebutkan di atas tidak berhasil untuk saya. Semua permintaan diautentikasi terlepas dari apakah itu PUT, POST, GET, OPTIONS, dll.

Ini berhasil mengubahnya menjadi:

# myapp/permissions.py
from rest_framework.permissions import IsAuthenticated

class AllowOptionsAuthentication(IsAuthenticated):
    def has_permission(self, request, view):
        if request.method == 'OPTIONS':
            return True
        return request.user and request.user.is_authenticated

Dan di settings.py:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.TokenAuthentication',),
    'DEFAULT_PERMISSION_CLASSES': (
        'myapp.permissions.AllowOptionsAuthentication',
    )
}

Saya juga mengalami masalah yang sama ini. Saya setuju dengan solusi yang diusulkan dan terima kasih untuk solusi sementara!

Tidak 100% yakin bahwa "harus mengesahkan semua permintaan OPSI secara default" persis seperti yang kami inginkan karena ada banyak pengembang yang menggunakan kerangka kerja REST yang menggunakan permintaan OPTIONS untuk kasus selain permintaan preflight CORS.

Namun saya pikir kami mungkin ingin memiliki opsi CORS yang diberkati . Saya tidak tahu apakah itu berarti kita harus memasukkan https://github.com/ottoyiu/django-cors-headers/ secara langsung, atau jika kita harus merujuknya lebih tinggi.

Apakah masalah ini teratasi setelah menggunakan paket itu?

Paket ini sebenarnya bukan solusi, itu hanya menambahkan tajuk CORS tetapi tidak memperbaiki fakta OPSI mengembalikan HTTP401 jika izin DRF tidak secara khusus diberikan untuk semua permintaan yang tidak diautentikasi.

IMO itu harus diperkenalkan sebaliknya, yaitu diizinkan secara default dengan pemberitahuan perubahan yang melanggar.
Ini adalah perilaku yang diharapkan sesuai spesifikasi W3C, dan menyebutkan pihak ke-3 dalam dokumen untuk menambal implementasi yang salah bukanlah solusi yang benar-benar bersih ...

Jadi, meskipun ada orang yang menggunakan OPTIONS untuk hal-hal yang tidak terkait dengan CORS (saya salah satunya), ada kemungkinan lebih banyak secara implisit mengharapkan hal-hal bekerja di luar kotak, karena mekanisme CORS sedang ditegakkan oleh semakin banyak browser. Faktanya, mekanisme ini berpotensi menghasilkan 99%+ permintaan OPTIONS melalui web.

Konflik yang menarik. Saya benar-benar memahami masalah dengan memiliki izin implisit secara default pada metode OPSI. Saya secara alami lebih condong ke solusi yang lebih aman dan saya kira itu tergantung pada tujuan dev untuk DRF. Jadi penolakan default tampaknya lebih tepat bagi saya.

Mungkin juga spesifikasi W3C untuk mengizinkan OPTIONS hanya berlaku dalam konteks CORS. Jelas, DRF tidak memiliki cara untuk mengetahui konteksnya dan harus menjadi pengaturan yang disediakan untuk pengembang.

Jika CORS dimaksudkan, maka izinkan semua permintaan OPSI. CORS tidak diinginkan secara default.

Sebenarnya saya pikir kita dapat mempertimbangkan perubahan yang melanggar untuk versi utama, dengan perilaku saat ini tetap tersedia. Perilaku OPTIONS kami yang ada telah ada sejak JSONP adalah apa yang akan digunakan orang daripada CORS, jadi kami mungkin karena penyegaran yang menghapus kumpulan informasi default yang cukup ad-hoc yang kami ekspos dan alih-alih hanya berkonsentrasi pada OPTIONS untuk CORS secara default .

+1

@tomchristie apakah Anda tahu kapan pembaruan/perbaikan versi utama akan terjadi? Saya juga memukul masalah ini.

Solusi @medakk sempurna, sementara itu!

Tonggak ini untuk memastikan mendapat pertimbangan yang tepat untuk 3.9

Solusi alternatif adalah menangani ini secara eksplisit dalam tampilan. Dalam kasus di mana Anda memiliki beberapa permission_classes ini lebih KERING, karena masing-masing dari mereka akan membutuhkan penanganan ini.

def check_permissions(self, request):
        if request.method == 'OPTIONS':
            return
        super(MyApiView, self).check_permissions(request)

Kembali ke ini setelah melihat lebih jauh - paket https://github.com/OttoYiu/Django-cors-headers menangani permintaan OPTIONS di- preflight

Tidak jelas bagi saya bahwa kami memiliki masalah di sini.

Terima kasih. Kami tidak menggunakan Django-cors-header, tapi mungkin memang seharusnya begitu.
Saya masih setuju dengan komentar sebelumnya bahwa DRF seharusnya tidak memerlukan paket eksternal untuk menangani permintaan CORS yang sesuai dengan W3C.

Ya ada banyak cara/paket pihak ketiga untuk menghindari masalah ini.
Tapi sekali lagi, tiket ini dibuat karena DRF tidak sesuai dengan spesifikasi W3C CORS, dan harus diperbaiki.

Saat ini, sebagian besar permintaan OPTIONS dilakukan ke titik akhir adalah untuk tujuan pra-penerbangan, dan mungkin menipu pengguna DRF baru jika harus menangani masalah ini secara manual karena pilihan berdasarkan pendapat DRF.

Saat ini, sebagian besar permintaan OPTIONS dilakukan ke titik akhir adalah untuk tujuan pra-penerbangan, dan mungkin menipu pengguna DRF baru jika harus menangani masalah ini secara manual karena pilihan berdasarkan pendapat DRF.

Hanya karena CORS membajak metode OPTIONS tidak berarti keamanannya harus diubah secara default untuk mengakomodasi. Terutama jika dukungan CORS diterapkan dengan benar, itu akan berfungsi sebagaimana mestinya. Maaf, tapi saya pikir pengembang yang ingin ini diubah perlu memeriksa pendapat mereka sendiri dan bukan pengelola DRF.

Mungkin peningkatan dokumentasi untuk menyarankan bahwa jika CORS diperlukan, bahwa modul untuk membantu dalam implementasi yang tepat digunakan alih-alih menciptakan kembali roda.

Mari kita turunkan levelnya.

Middleware adalah tempat yang masuk akal untuk menangani header CORS. Kita dapat membangunnya ke dalam kerangka REST, tetapi paket yang ada sudah mencakupnya dengan sangat baik.

Tubuh yang kebetulan dikembalikan oleh kerangka kerja REST untuk permintaan OPTIONS tidak relevan di sini, karena permintaan CORS yang telah diterbangkan sebelumnya harus dicegat oleh middleware, dan permintaan CORS standar dapat berupa metode HTTP apa pun.

Saya akan sangat senang jika kerangka REST pindah ke tidak melayani badan respons tersebut secara default, tetapi itu sedikit berbeda dengan masalah dukungan CORS, dan kerangka REST tidak terlalu memiliki perilaku yang berpendirian di sana, melainkan memiliki perilaku yang mendahului CORS menjadi diterapkan secara luas.

Bagi saya, solusi middleware saja tidak cukup atau setidaknya memerlukan kerja sama dari DRF. Jika Anda melihat kode yang direferensikan, Anda dapat melihat bahwa default global digunakan untuk memasok header CORS. Tetapi bagaimana saya tahu secara global header apa yang didukung oleh setiap tampilan?

Karena itu, saya ingin melihat cara standar bagi middleware untuk menghubungkan ke opsi konfigurasi ini dari tampilan/tampilan: metode allowed_cors_headers misalnya.

Saya setuju bahwa ini tidak perlu ditangani oleh pandangan, tetapi itu benar-benar perlu menghormati pengetahuan pandangan tentang apa yang dapat dilayaninya.

Mungkin juga bermanfaat untuk memiliki penggantian tingkat router untuk diterapkan ke semua tampilan di router.

Pertimbangan tambahan:

  • bervariasi berdasarkan asal
  • bervariasi berdasarkan tipe konten

Ini bukan pilihan favorit saya tetapi trennya menggunakan pihak ketiga.

Saya pikir kita dapat menutup tiket ini setelah dokumentasi diperbarui dengan informasi yang tepat untuk pengguna CORS (seperti: "Hati-hati, DRF tidak memenuhi spesifikasi W3C CORS pada permintaan OPTIONS. Jika Anda menggunakan OPTIONS untuk CORS jangan lupa untuk menambahkan middleware yang tepat jika tidak, permintaan preflight Anda mungkin gagal karena kurangnya otorisasi" dll)

Dokumentasi yang ada https://www.django-rest-framework.org/topics/ajax-csrf-cors/#cors sangat masuk akal, dan berurusan dengan CORS di middleware adalah pendekatan yang benar dalam hal apa pun.

Tetapi bagaimana saya tahu secara global header apa yang didukung oleh setiap tampilan?

Anda tidak perlu - Anda perlu tahu apa kebijakan CORS dari situs tersebut.

Senang menerima permintaan tarik yang membuat dokumen CORS lebih menonjol, atau menyertakannya di tempat lain yang sesuai juga. Selain itu, tidak ada masalah yang dapat ditindaklanjuti di sini.

Kesalahpahaman saya tentang spesifikasi, oops.

Dalam hal ini, dukungan akan lebih baik dimasukkan dalam kontribusi situs Django
paket, bukan drf.

Le mar. 19 fevr. 2019 10:22, Tom Christie [email protected] a
écrit:

Dokumentasi yang ada
https://www.django-rest-framework.org/topics/ajax-csrf-cors/#cors adalah
sangat masuk akal, dan berurusan dengan CORS di middleware adalah benar
pendekatan dalam hal apapun.

Tetapi bagaimana saya tahu secara global header apa yang didukung oleh setiap tampilan?

Anda tidak perlu - Anda perlu tahu apa kebijakan CORS dari situs tersebut.


Anda menerima ini karena Anda berkomentar.
Balas email ini secara langsung, lihat di GitHub
https://github.com/encode/Django-rest-framework/issues/5616#issuecomment-465146969 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AFhtlM6bG-Bs2CvoO972pIfwvxLHbzAxks5vPAjAgaJpZM4Qlvkn
.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

jpocentek picture jpocentek  ·  3Komentar

synic picture synic  ·  3Komentar

manjitkumar picture manjitkumar  ·  3Komentar

tomchristie picture tomchristie  ·  3Komentar

ryankask picture ryankask  ·  4Komentar