Django Rest Framework loại bỏ csrf


111

Tôi biết rằng có các câu trả lời liên quan đến Django Rest Framework, nhưng tôi không thể tìm thấy giải pháp cho vấn đề của mình.

Tôi có một ứng dụng có xác thực và một số chức năng. Tôi đã thêm một ứng dụng mới vào nó, sử dụng Django Rest Framework. Tôi chỉ muốn sử dụng thư viện trong ứng dụng này. Ngoài ra, tôi muốn thực hiện yêu cầu ĐĂNG và tôi luôn nhận được phản hồi sau:

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

Tôi có mã sau:

# 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})

Tôi muốn thêm API mà không ảnh hưởng đến ứng dụng hiện tại. Vì vậy, câu hỏi của tôi là làm cách nào để tắt CSRF chỉ cho ứng dụng này?


Bạn đang sử dụng mã thông báo @csrf_exempt. Bạn có thể sử dụng điều này trên toàn cảnh. Điều đó không nên làm việc?
mukesh

Không, tôi vẫn nhận được thông tin chi tiết: "CSRF Không thành công: Mã thông báo CSRF bị thiếu hoặc không chính xác." thông điệp. Tôi đã kết luận từ các câu trả lời rằng tôi nên xóa xác thực mặc định.
Irene Texas

1
Tôi đã gặp phải một tình huống RẤT tương tự khi sử dụng xác thực Mã thông báo. Đối với bất cứ ai khác trong cùng một thuyền: stackoverflow.com/questions/34789301/...
Các Brewmaster

Câu trả lời:


218

Tại sao lỗi này lại xảy ra?

Điều này đang xảy ra do SessionAuthenticationlược đồ mặc định được DRF sử dụng. DRF's SessionAuthenticationsử dụng khung phiên của Django để xác thực yêu cầu kiểm tra CSRF.

Khi bạn không xác định bất kỳ cái nào authentication_classestrong khung nhìn / tập hợp của mình, DRF sử dụng các lớp xác thực này làm mặc định.

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

Vì DRF cần hỗ trợ cả xác thực dựa trên phiên và không dựa trên phiên cho các chế độ xem giống nhau, nó thực thi kiểm tra CSRF cho chỉ những người dùng đã xác thực. Điều này có nghĩa là chỉ các yêu cầu được xác thực mới yêu cầu mã thông báo CSRF và các yêu cầu ẩn danh có thể được gửi mà không cần mã thông báo CSRF.

Nếu bạn đang sử dụng API kiểu AJAX với SessionAuthentication, bạn sẽ cần bao gồm mã thông báo CSRF hợp lệ cho bất kỳ lệnh gọi phương thức HTTP "không an toàn" nào, chẳng hạn như PUT, PATCH, POST or DELETEyêu cầu.

Làm gì sau đó?

Bây giờ để vô hiệu hóa kiểm tra csrf, bạn có thể tạo một lớp xác thực tùy chỉnh CsrfExemptSessionAuthenticationmở rộng từ SessionAuthenticationlớp mặc định . Trong lớp xác thực này, chúng tôi sẽ ghi đè enforce_csrf()kiểm tra đang diễn ra bên trong thực tế 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

Theo quan điểm của bạn, thì bạn có thể xác định authentication_classeslà:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

Điều này sẽ xử lý lỗi csrf.


10
Xin lỗi, có thể tôi đã bỏ sót điểm, nhưng không phải là rủi ro bảo mật khi bỏ qua / tắt tính năng bảo vệ csrf?
Paolo

1
@Paolo OP cần để tắt xác thực CSRF cho một API cụ thể. Nhưng có, đó là một rủi ro bảo mật nếu vô hiệu hóa tính năng bảo vệ csrf. Nếu một người cần tắt xác thực phiên cho một trường hợp sử dụng cụ thể, thì anh ta có thể sử dụng giải pháp này.
Rahul Gupta

Xin chào @RahulGupta - Không có cách nào để kiểm tra trình trang trí csrf_exempt trên chế độ xem và sau đó chỉ vô hiệu hóa chế độ thực thi cho các chế độ xem đó?
Abhishek

@Abhishek Có thể bạn đang tìm kiếm các ans dưới đây của bixente57. Nó vô hiệu hóa csrf cho các chế độ xem tùy chỉnh.
Rahul Gupta

1
@RahulGupta nếu bạn không muốn cưỡng chế_csrf, thì cách tốt nhất sẽ là gì?
game thủ

21

Giải pháp dễ dàng hơn:

Trong views.py, sử dụng dấu ngoặc nhọn CsrfExemptMixin và các lớp xác thực:

# 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})

1
Cảm ơn, đây là giải pháp dễ dàng nhất cho vấn đề. Api của tôi bằng cách sử dụng oauth2_provider và mã thông báo.
Dat TT

1
ahhhh anh bạn. Tôi đã có CsrfExemptMixin, nhưng không có xác thực_classes = []. Cảm ơn bạn!
MagicLAMP

FYI, dòng xác thực lớp có vẻ là một chìa khóa. Hoạt động giống nhau đối với tôi khi có hoặc không có CsrfExemptMixin.
Dashdrum

14

Sửa đổi urls.py

Nếu bạn quản lý các tuyến đường của mình trong urls.py, bạn có thể bọc các tuyến đường mong muốn của mình bằng csrf_exempt () để loại trừ chúng khỏi phần mềm trung gian xác minh 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())),
    ...
)

Ngoài ra, với tư cách là Người trang trí Một số có thể thấy việc sử dụng trình trang trí @csrf_exempt phù hợp hơn với nhu cầu của họ

ví dụ,

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

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

sẽ hoàn thành công việc!


Một số giải thích cho mã sẽ tạo ra một câu trả lời tốt hơn.
chevybow

@chevybow Thực sự xin lỗi, tôi thực sự là người mới tham gia cộng đồng. Trên thực tế, nó là một Decorator của Django để vô hiệu hóa CSRF cho một Chế độ xem nhất định
Syed Faizan

điều này đã làm việc cho tôi với python3 và django 1.11 và có vẻ dễ dàng nhất!
madannes

12

Đối với tất cả những người không tìm thấy câu trả lời hữu ích. Có DRF tự động loại bỏ bảo vệ CSRF nếu bạn không sử dụng SessionAuthenticationLỚP XÁC NHẬN, ví dụ: nhiều nhà phát triển chỉ sử dụng JWT:

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

Nhưng sự cố CSRF not setcó thể xảy ra từ một số lý do khác, do bạn đã thêm không chính xác đường dẫn vào chế độ xem của mình:

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

thay vì

url(r'^api/signup/', CreateUserView.as_view()),

8

Tôi đã thử một số câu trả lời ở trên và cảm thấy việc tạo một lớp riêng biệt hơi quá sức.

Để tham khảo, tôi đã gặp sự cố này khi cố gắng cập nhật một phương thức xem dựa trên chức năng thành một phương thức xem dựa trên lớp để đăng ký người dùng.

Khi sử dụng chế độ xem dựa trên lớp (CBV) và Django Rest Framework (DRF), Kế thừa từ lớp ApiView và đặt các lớp allow_class và các lớp xác thực thành một bộ trống. Tìm một ví dụ dưới đây.

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

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

        # rest of your code here

7

Nếu bạn không muốn sử dụng xác thực dựa trên phiên, bạn có thể xóa Session Authenticationkhỏi REST_AUTHENTICATION_CLASSES và điều đó sẽ tự động xóa tất cả các vấn đề dựa trên csrf. Nhưng trong trường hợp đó, apis có thể duyệt có thể không hoạt động.

Bên cạnh đó, lỗi này không xảy ra ngay cả với xác thực phiên. Bạn nên sử dụng xác thực tùy chỉnh như TokenAuthentication cho apis của mình và đảm bảo gửi Accept:application/jsonContent-Type:application/json(miễn là bạn đang sử dụng json) trong các yêu cầu của mình cùng với mã xác thực.


4

Bạn cần thêm điều này để ngăn xác thực phiên mặc định: (settings.py)

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

Sau đó: (views.py)

from rest_framework.permissions import AllowAny

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

    def ...():

3

Tôi bị ấn tượng với cùng một vấn đề. Tôi đã theo dõi tài liệu tham khảo này và nó đã hoạt động. Giải pháp là tạo một phần mềm trung gian

Thêm tệp disable.py vào một trong các ứng dụng của bạn (trong trường hợp của tôi, đó là 'myapp')

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

Và thêm phần mềm trung gian vào MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)

4
Điều này sẽ làm cho toàn bộ trang web của bạn dễ bị CSRF tấn công. vi.wikipedia.org/wiki/Cross-site_request_forgery
Jeanno

1

Nếu bạn đang sử dụng môi trường ảo độc quyền cho ứng dụng của mình, bạn có thể sử dụng cách tiếp cận sau mà không có hiệu quả với bất kỳ ứng dụng nào khác.

Những gì bạn quan sát xảy ra vì rest_framework/authentication.pycó mã này trong authenticatephương thức của SessionAuthenticationlớp:

self.enforce_csrf(request)

Bạn có thể sửa đổi Requestlớp để có một thuộc tính được gọi csrf_exemptvà khởi tạo nó bên trong lớp View tương ứng của bạn Truenếu bạn không muốn kiểm tra CSRF. Ví dụ:

Tiếp theo, sửa đổi mã trên như sau:

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

Có một số thay đổi liên quan bạn phải thực hiện trong Requestlớp. Bản triển khai hoàn chỉnh có sẵn tại đây (với mô tả đầy đủ): https://github.com/piaxis/django-rest-framework/commit/1bdb872bac5345202e2f58728d0e7fad70dfd7ed


1

Giải pháp của tôi được cho thấy đòn. Chỉ trang trí lớp của tôi.

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

1
Mặc dù mã này có thể trả lời câu hỏi, nhưng việc cung cấp thêm ngữ cảnh liên quan đến lý do và / hoặc cách mã này trả lời câu hỏi sẽ cải thiện giá trị lâu dài của nó.
Alex Riabov,

0

Khi sử dụng REST API POST, việc không có tiêu đề yêu cầu X-CSRFToken có thể gây ra lỗi đó. Tài liệu Django cung cấp mã mẫu về cách lấy và đặt giá trị mã thông báo CSRF từ JS.

Như đã chỉ ra trong các câu trả lời ở trên, kiểm tra CSRF xảy ra khi sử dụng Xác thực phiên. Một cách tiếp cận khác là sử dụng TokenAuthentication, nhưng hãy nhớ rằng nó phải được đặt đầu tiên trong danh sách DEFAULT_AUTHENTICATION_CLASSES của cài đặt REST_FRAMEWORK.


-1

Đây cũng có thể là sự cố trong cuộc tấn công DNS Rebinding .

Giữa các lần thay đổi DNS, đây cũng có thể là một yếu tố. Chờ cho đến khi DNS được xóa hoàn toàn sẽ giải quyết vấn đề này nếu nó hoạt động trước khi có sự cố / thay đổi DNS.


Điều này có liên quan gì đến câu hỏi trên?
boatcoder

Có nghĩa là sự cố này có thể xảy ra khi bạn đang chuyển đổi DNS và nó chưa được phổ biến hoàn toàn. Nếu ứng dụng có định tuyến khác với phiên Django bình thường, thì đây là lý do. Chỉ cần thông báo về một trường hợp nguy hiểm mà tôi đã gặp phải. Đây có vẻ là một tài nguyên có phần kinh điển, vì vậy tôi nghĩ rằng tôi sẽ thêm một nguồn bổ sung.
chris Frisina
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.