Django Rest Framework menghapus csrf

112

Saya tahu bahwa ada jawaban mengenai Django Rest Framework, tetapi saya tidak dapat menemukan solusi untuk masalah saya.

Saya memiliki aplikasi yang memiliki otentikasi dan beberapa fungsi. Saya menambahkan aplikasi baru ke dalamnya, yang menggunakan Django Rest Framework. Saya ingin menggunakan perpustakaan hanya di aplikasi ini. Saya juga ingin membuat permintaan POST, dan saya selalu menerima tanggapan ini:

{
    "detail": "CSRF Failed: CSRF token missing or incorrect."
}

Saya memiliki kode berikut:

# urls.py
from django.conf.urls import patterns, url


urlpatterns = patterns(
    'api.views',
    url(r'^object/$', views.Object.as_view()),
)

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.views.decorators.csrf import csrf_exempt


class Object(APIView):

    @csrf_exempt
    def post(self, request, format=None):
        return Response({'received data': request.data})

Saya ingin menambahkan API tanpa mempengaruhi aplikasi saat ini. Jadi pertanyaan saya adalah bagaimana saya bisa menonaktifkan CSRF hanya untuk aplikasi ini?

Irene Texas
sumber
Anda sudah menggunakan token @csrf_exempt. Anda dapat menggunakan ini di seluruh tampilan. Bukankah seharusnya itu berhasil?
mukesh
Tidak, saya masih mendapat detail: "CSRF Gagal: Token CSRF hilang atau salah." pesan. Saya menyimpulkan dari jawaban bahwa saya harus menghapus otentikasi default.
Irene Texas
1
Saya mengalami situasi yang SANGAT mirip menggunakan otentikasi Token. Untuk orang lain di perahu yang sama: stackoverflow.com/questions/34789301/…
The Brewmaster

Jawaban:

218

Mengapa kesalahan ini terjadi?

Ini terjadi karena SessionAuthenticationskema default yang digunakan oleh DRF. DRF SessionAuthenticationmenggunakan kerangka sesi Django untuk otentikasi yang membutuhkan CSRF untuk diperiksa.

Saat Anda tidak menentukan apa pun authentication_classesdi view / viewset Anda, DRF menggunakan kelas otentikasi ini sebagai default.

'DEFAULT_AUTHENTICATION_CLASSES'= (
    'rest_framework.authentication.SessionAuthentication',
    'rest_framework.authentication.BasicAuthentication'
),

Karena DRF perlu mendukung otentikasi berbasis sesi dan non-sesi ke tampilan yang sama, DRF memberlakukan pemeriksaan CSRF hanya untuk pengguna yang diautentikasi. Ini berarti bahwa hanya permintaan terautentikasi yang memerlukan token CSRF dan permintaan anonim dapat dikirim tanpa token CSRF.

Jika Anda menggunakan API gaya AJAX dengan SessionAuthentication, Anda harus menyertakan token CSRF yang valid untuk setiap panggilan metode HTTP "tidak aman", seperti PUT, PATCH, POST or DELETEpermintaan.

Lalu apa yang harus dilakukan?

Sekarang untuk menonaktifkan pemeriksaan csrf, Anda dapat membuat kelas otentikasi khusus CsrfExemptSessionAuthenticationyang diturunkan dari SessionAuthenticationkelas default . Di kelas otentikasi ini, kami akan mengganti enforce_csrf()pemeriksaan yang terjadi di dalam file SessionAuthentication.

from rest_framework.authentication import SessionAuthentication, BasicAuthentication 

class CsrfExemptSessionAuthentication(SessionAuthentication):

    def enforce_csrf(self, request):
        return  # To not perform the csrf check previously happening

Dalam pandangan Anda, maka Anda dapat menentukan authentication_classesmenjadi:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

Ini harus menangani kesalahan csrf.

Rahul Gupta
sumber
10
Maaf mungkin saya melewatkan intinya, tetapi bukankah risiko keamanan untuk melewati / menonaktifkan perlindungan csrf?
Paolo
1
@Paolo OP diperlukan untuk menonaktifkan otentikasi CSRF untuk API tertentu. Tapi ya, itu risiko keamanan untuk menonaktifkan perlindungan csrf. Jika seseorang perlu menonaktifkan otentikasi sesi untuk kasus penggunaan tertentu, maka dia dapat menggunakan solusi ini.
Rahul Gupta
Halo @RahulGupta - Apakah tidak ada cara untuk memeriksa dekorator csrf_exempt pada tampilan, dan kemudian hanya menonaktifkan penegakan_csrf untuk tampilan tersebut?
Abhishek
@Abhishek Mungkin Anda sedang mencari di bawah ans oleh bixente57. Menonaktifkan csrf untuk tampilan kustom.
Rahul Gupta
1
@RahulGupta jika Anda tidak ingin menegakkan_csrf, lalu apa cara terbaik?
pemain
21

Solusi yang lebih mudah:

Di views.py, gunakan tanda kurung CsrfExemptMixin dan authentication_classes:

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.views.decorators.csrf import csrf_exempt
from braces.views import CsrfExemptMixin


class Object(CsrfExemptMixin, APIView):
    authentication_classes = []

    def post(self, request, format=None):
        return Response({'received data': request.data})
bixente57
sumber
1
Terima kasih, ini adalah solusi termudah untuk masalah tersebut. Api saya menggunakan oauth2_provider dan token.
Dat TT
1
ahhhh man. Saya memiliki CsrfExemptMixin, tetapi tidak memiliki authentication_classes = []. Terima kasih!
MagicLAMP
FYI, baris authentication_classes tampaknya menjadi kuncinya. Bekerja sama untuk saya dengan atau tanpa CsrfExemptMixin.
Dashdrum
14

Ubah urls.py

Jika Anda mengelola rute Anda di urls.py, Anda bisa menggabungkan rute yang Anda inginkan dengan csrf_exempt () untuk mengecualikannya dari middleware verifikasi CSRF.

from django.conf.urls import patterns, url
    from django.views.decorators.csrf import csrf_exempt
    import views

urlpatterns = patterns('',
    url(r'^object/$', csrf_exempt(views.ObjectView.as_view())),
    ...
)

Atau, sebagai Dekorator Beberapa orang mungkin menganggap penggunaan dekorator @csrf_exempt lebih sesuai untuk kebutuhan mereka

misalnya,

from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse

@csrf_exempt
def my_view(request):
    return HttpResponse('Hello world')

harus menyelesaikan Pekerjaan!

Syed Faizan
sumber
Beberapa penjelasan tentang kode akan membuat jawaban yang lebih baik.
chevybow
@chevybow Benar-benar Maaf, Saya sebenarnya Baru di Komunitas. Sebenarnya ini adalah Penghias Django untuk menonaktifkan CSRF untuk Pandangan Tertentu
Syed Faizan
ini bekerja untuk saya dengan python3 dan django 1.11 dan tampaknya paling mudah!
madannes
12

Untuk semua yang tidak menemukan jawaban yang membantu. Ya, DRF secara otomatis menghapus perlindungan CSRF jika Anda tidak menggunakan SessionAuthenticationKELAS OTENTIKASI, misalnya, banyak pengembang hanya menggunakan JWT:

'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),

Tetapi masalah CSRF not setdapat terjadi karena beberapa alasan lain, misalnya Anda tidak menambahkan jalur dengan benar ke tampilan Anda:

url(r'^api/signup/', CreateUserView),  # <= error! DRF cant remove CSRF because it is not as_view that does it!

dari pada

url(r'^api/signup/', CreateUserView.as_view()),
Ivan Borshchov
sumber
8

Saya mencoba beberapa jawaban di atas dan merasa membuat kelas terpisah agak berlebihan.

Sebagai referensi, saya mengalami masalah ini saat mencoba memperbarui metode tampilan berbasis fungsi ke metode tampilan berbasis kelas untuk pendaftaran pengguna.

Saat menggunakan tampilan berbasis kelas (CBVs) dan Django Rest Framework (DRF), Mewarisi dari kelas ApiView dan setel permission_classes dan authentication_classes ke tupel kosong. Temukan contoh di bawah ini.

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

    def post(self, request, *args, **kwargs):

        # rest of your code here
Mike Hawes
sumber
7

Jika Anda tidak ingin menggunakan otentikasi berbasis sesi, Anda dapat menghapus Session Authenticationdari REST_AUTHENTICATION_CLASSES dan itu akan secara otomatis menghapus semua masalah berbasis csrf. Namun dalam kasus ini, API yang dapat dijelajahi mungkin tidak berfungsi.

Selain itu kesalahan ini seharusnya tidak datang bahkan dengan otentikasi sesi. Anda harus menggunakan otentikasi khusus seperti TokenAuthentication untuk apis Anda dan pastikan untuk mengirim Accept:application/jsondan Content-Type:application/json(asalkan Anda menggunakan json) dalam permintaan Anda bersama dengan token otentikasi.

hspandher
sumber
4

Anda perlu menambahkan ini untuk mencegah otentikasi sesi default: (settings.py)

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

Kemudian: (views.py)

from rest_framework.permissions import AllowAny

class Abc(APIView):
    permission_classes = (AllowAny,)

    def ...():
Nouvellie
sumber
3

Saya terpukul dengan masalah yang sama. Saya mengikuti referensi ini dan berhasil. Solusinya adalah dengan membuat middleware

Tambahkan file disable.py di salah satu aplikasi Anda (dalam kasus saya ini adalah 'myapp')

class DisableCSRF(object):
    def process_request(self, request):
        setattr(request, '_dont_enforce_csrf_checks', True)

Dan tambahkan middileware ke MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)
Venkatesh Mondi
sumber
4
Ini akan membuat seluruh situs web Anda rentan terhadap serangan CSRF. en.wikipedia.org/wiki/Cross-site_request_forgery
Jeanno
1

Jika Anda menggunakan lingkungan virtual eksklusif untuk aplikasi Anda, Anda dapat menggunakan pendekatan berikut tanpa aplikasi lain yang efektif.

Apa yang Anda amati terjadi karena rest_framework/authentication.pymemiliki kode ini dalam authenticatemetode SessionAuthenticationkelas:

self.enforce_csrf(request)

Anda dapat mengubah Requestkelas agar properti dipanggil csrf_exemptdan menginisialisasinya di dalam kelas View masing-masing Truejika Anda tidak ingin pemeriksaan CSRF. Sebagai contoh:

Selanjutnya, ubah kode di atas sebagai berikut:

if not request.csrf_exempt:
    self.enforce_csrf(request)

Ada beberapa perubahan terkait yang harus Anda lakukan di Requestkelas. Implementasi lengkap tersedia di sini (dengan deskripsi lengkap): https://github.com/piaxis/django-rest-framework/commit/1bdb872bac5345202e2f58728d0e7fad70dfd7ed

Reetesh Ranjan
sumber
1

Solusi saya ditampilkan pukulan. Hiasi saja kelasku.

from django.views.decorators.csrf import csrf_exempt
@method_decorator(csrf_exempt, name='dispatch')
@method_decorator(basic_auth_required(
    target_test=lambda request: not request.user.is_authenticated
), name='dispatch')
class GenPedigreeView(View):
    pass
Jak Liao
sumber
1
Meskipun kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang mengapa dan / atau bagaimana kode ini menjawab pertanyaan tersebut meningkatkan nilai jangka panjangnya.
Alex Riabov
1

Saat menggunakan REST API POST, tidak adanya header permintaan X-CSRFoken dapat menyebabkan kesalahan tersebut. Dokumentasi Django menyediakan kode contoh untuk mendapatkan dan menyetel nilai token CSRF dari JS.

Seperti yang ditunjukkan dalam jawaban di atas, pemeriksaan CSRF terjadi saat SessionAuthentication digunakan. Pendekatan lain adalah dengan menggunakan TokenAuthentication, tetapi perlu diingat bahwa ini harus ditempatkan pertama kali dalam daftar DEFAULT_AUTHENTICATION_CLASSES pengaturan REST_FRAMEWORK.

Alexander Kaluzhny
sumber
-1

Ini juga bisa menjadi masalah selama serangan DNS Rebinding .

Di antara perubahan DNS, ini juga bisa menjadi faktor. Menunggu sampai DNS benar-benar habis akan menyelesaikan ini jika berfungsi sebelum masalah / perubahan DNS.

chris Frisina
sumber
Apa hubungannya ini dengan pertanyaan di atas?
boatcoder
Artinya, masalah ini dapat terjadi saat Anda mengalihkan DNS dan belum sepenuhnya disebarkan. Jika aplikasi mempunyai perutean berbeda dari sesi normal Django, inilah alasannya. Hanya menginformasikan kasus edge yang saya hadapi. Ini tampaknya merupakan sumber daya yang agak kanonik, jadi saya pikir saya akan menambahkan sumber daya tambahan.
chris Frisina