Django Rest Framework ลบ csrf


114

ฉันรู้ว่ามีคำตอบเกี่ยวกับ Django Rest Framework แต่ฉันไม่พบวิธีแก้ปัญหาของฉัน

ฉันมีแอปพลิเคชันที่มีการตรวจสอบสิทธิ์และฟังก์ชันบางอย่าง ฉันได้เพิ่มแอพใหม่ลงไปซึ่งใช้ Django Rest Framework ฉันต้องการใช้ไลบรารีในแอพนี้เท่านั้น นอกจากนี้ฉันต้องการส่งคำขอ POST และฉันจะได้รับคำตอบนี้เสมอ:

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

ฉันมีรหัสต่อไปนี้:

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

ฉันต้องการเพิ่ม API โดยไม่กระทบกับแอปพลิเคชันปัจจุบัน ดังนั้นคำถามของฉันคือฉันจะปิดการใช้งาน CSRF เฉพาะสำหรับแอปนี้ได้อย่างไร


คุณใช้โทเค็น @csrf_exempt อยู่แล้ว คุณสามารถใช้สิ่งนี้กับมุมมองทั้งหมด มันไม่ควรทำงาน?
mukesh

ไม่ฉันยังคงได้รับรายละเอียด: "CSRF ล้มเหลว: โทเค็น CSRF ขาดหายไปหรือไม่ถูกต้อง" ข้อความ. ฉันสรุปจากคำตอบว่าฉันควรลบการรับรองความถูกต้องเริ่มต้น
Irene Texas

1
ฉันพบสถานการณ์ที่คล้ายกันมากโดยใช้การตรวจสอบความถูกต้องของโทเค็น สำหรับใครที่อยู่ในเรือลำเดียวกัน: stackoverflow.com/questions/34789301/…
The Brewmaster

คำตอบ:


219

เหตุใดจึงเกิดข้อผิดพลาดนี้

สิ่งนี้เกิดขึ้นเนื่องจากSessionAuthenticationโครงร่างเริ่มต้นที่DRF ใช้ DRF SessionAuthenticationใช้กรอบเซสชันของ Django สำหรับการตรวจสอบสิทธิ์ซึ่งต้องตรวจสอบ CSRF

เมื่อคุณไม่ได้กำหนดสิ่งใด ๆauthentication_classesในมุมมอง / ชุดมุมมองของคุณ DRF จะใช้คลาสการพิสูจน์ตัวตนนี้เป็นค่าเริ่มต้น

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

เนื่องจาก DRF จำเป็นต้องรองรับทั้งการตรวจสอบสิทธิ์แบบเซสชันและแบบไม่ใช้เซสชันสำหรับมุมมองเดียวกันจึงบังคับใช้การตรวจสอบ CSRF สำหรับผู้ใช้ที่ได้รับการพิสูจน์ตัวตนเท่านั้น ซึ่งหมายความว่าเฉพาะคำขอที่ได้รับการรับรองความถูกต้องเท่านั้นที่ต้องใช้โทเค็น CSRF และคำขอที่ไม่ระบุชื่ออาจถูกส่งโดยไม่มีโทเค็น CSRF

หากคุณกำลังใช้ AJAX style API กับ SessionAuthentication คุณจะต้องรวมโทเค็น CSRF ที่ถูกต้องสำหรับการเรียกเมธอด HTTP ที่ "ไม่ปลอดภัย" เช่นPUT, PATCH, POST or DELETEคำขอ

แล้วจะทำอย่างไร?

ตอนนี้หากต้องการปิดใช้งานการตรวจสอบ csrf คุณสามารถสร้างคลาสการพิสูจน์ตัวตนที่กำหนดเองCsrfExemptSessionAuthenticationซึ่งขยายจากSessionAuthenticationคลาสเริ่มต้น ในชั้นการตรวจสอบนี้เราจะแทนที่การตรวจสอบซึ่งเกิดขึ้นภายในที่เกิดขึ้นจริงenforce_csrf()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

ในมุมมองของคุณคุณสามารถกำหนดauthentication_classesให้เป็น:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

สิ่งนี้ควรจัดการกับข้อผิดพลาด csrf


12
ขออภัยบางทีฉันพลาดประเด็น แต่ความเสี่ยงด้านความปลอดภัยที่จะข้าม / ปิดการใช้งานการป้องกัน csrf ไม่ใช่หรือ
Paolo

2
@Paolo OP จำเป็นต้องปิดใช้งานการรับรองความถูกต้อง CSRF สำหรับ API เฉพาะ แต่ใช่ความเสี่ยงด้านความปลอดภัยที่จะปิดการใช้งานการป้องกัน csrf หากจำเป็นต้องปิดใช้งานการตรวจสอบสิทธิ์เซสชันสำหรับกรณีการใช้งานเฉพาะเขาก็สามารถใช้วิธีนี้ได้
Rahul Gupta

เฮ้ @RahulGupta - ไม่มีวิธีตรวจสอบมัณฑนากร csrf_exempt ในมุมมองจากนั้นปิดใช้งาน enforce_csrf สำหรับมุมมองเหล่านั้นเท่านั้นหรือ
Abhishek

@Abhishek บางทีคุณอาจกำลังมองหาansด้านล่างโดย bixente57 ปิดใช้งาน csrf สำหรับมุมมองที่กำหนดเอง
Rahul Gupta

1
@RahulGupta ถ้าคุณไม่ต้องการบังคับใช้ _csrf แล้วอะไรจะเป็นวิธีที่ดีที่สุด?
gamer

24

วิธีแก้ปัญหาที่ง่ายกว่า:

ใน views.py ใช้วงเล็บปีกกา CsrfExemptMixin และ 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})

1
ขอบคุณนี่เป็นวิธีแก้ปัญหาที่ง่ายที่สุด api ของฉันใช้ oauth2_provider และโทเค็น
Dat TT

1
ผู้ชายอ่า ฉันมี CsrfExemptMixin แต่ไม่มี authentication_classes = [] ขอบคุณ!
MagicLAMP

FYI บรรทัด authentication_classes ดูเหมือนจะเป็นกุญแจสำคัญ ทำงานเหมือนกันกับฉันโดยมีหรือไม่มี CsrfExemptMixin
Dashdrum

14

แก้ไข urls.py

หากคุณจัดการเส้นทางของคุณใน urls.py คุณสามารถรวมเส้นทางที่คุณต้องการด้วย csrf_exempt () เพื่อแยกเส้นทางเหล่านี้ออกจากมิดเดิลแวร์การตรวจสอบ 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())),
    ...
)

อีกทางหนึ่งในฐานะมัณฑนากรบางคนอาจพบว่าการใช้มัณฑนากร @csrf_exempt เหมาะกับความต้องการของตนมากกว่า

ตัวอย่างเช่น

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

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

ควรทำให้งานเสร็จ!


คำอธิบายบางอย่างเกี่ยวกับรหัสจะช่วยให้ได้คำตอบที่ดีขึ้น
chevybow

@chevybow ขอโทษจริงๆฉันยังใหม่กับชุมชน จริงๆแล้วมันเป็นมัณฑนากรของ Django เพื่อปิดการใช้งาน CSRF สำหรับมุมมองบางอย่าง
Syed Faizan

สิ่งนี้ใช้ได้กับฉันกับ python3 และ django 1.11 และดูเหมือนง่ายที่สุด!
madannes

13

สำหรับทุกคนที่ไม่พบคำตอบที่เป็นประโยชน์ ใช่ DRF จะลบการป้องกัน CSRF โดยอัตโนมัติหากคุณไม่ได้ใช้SessionAuthenticationAUTHENTICATION CLASS ตัวอย่างเช่นนักพัฒนาจำนวนมากใช้ JWT เท่านั้น:

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

แต่ปัญหาCSRF not setอาจเกิดจากสาเหตุอื่นเช่นคุณไม่ได้เพิ่มเส้นทางให้คุณดูอย่างถูกต้อง:

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

แทน

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

8

ฉันลองทำตามคำตอบสองสามข้อข้างต้นแล้วและรู้สึกว่าการสร้างชั้นเรียนแยกต่างหากนั้นค่อนข้างยาก

สำหรับการอ้างอิงฉันพบปัญหานี้เมื่อพยายามอัปเดตวิธีการดูตามฟังก์ชันเป็นวิธีการดูตามคลาสสำหรับการลงทะเบียนผู้ใช้

เมื่อใช้ class-based-views (CBVs) และ Django Rest Framework (DRF) ให้สืบทอดจากคลาส ApiView และตั้งค่า permission_classes และ authentication_classes เป็นทูเปิลว่าง ดูตัวอย่างด้านล่าง

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

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

        # rest of your code here

7

หากคุณไม่ต้องการใช้การพิสูจน์ตัวตนตามเซสชันคุณสามารถลบออกSession Authenticationจาก REST_AUTHENTICATION_CLASSES และนั่นจะเป็นการลบปัญหาที่อิง csrf ทั้งหมดโดยอัตโนมัติ แต่ในกรณีนี้เรียกดู apis อาจไม่ทำงาน

นอกจากนี้ข้อผิดพลาดนี้ไม่ควรมาพร้อมกับการตรวจสอบสิทธิ์เซสชัน คุณควรใช้การตรวจสอบสิทธิ์แบบกำหนดเองเช่น TokenAuthentication สำหรับ apis ของคุณและตรวจสอบให้แน่ใจว่าได้ส่งAccept:application/jsonและContent-Type:application/json(หากคุณใช้ json) ในคำขอของคุณพร้อมกับโทเค็นการตรวจสอบสิทธิ์


4

คุณต้องเพิ่มสิ่งนี้เพื่อป้องกันการตรวจสอบเซสชันเริ่มต้น: (settings.py)

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

จากนั้น: (views.py)

from rest_framework.permissions import AllowAny

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

    def ...():

3

ฉันหลงกับปัญหาเดียวกัน ฉันติดตามข้อมูลอ้างอิงนี้และได้ผล วิธีแก้ไขคือสร้างมิดเดิลแวร์

เพิ่มไฟล์ disable.py ในแอพของคุณ (ในกรณีของฉันคือ 'myapp')

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

และเพิ่ม middileware ลงใน MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)

4
วิธีนี้จะทำให้ทั้งเว็บไซต์ของคุณมีแนวโน้มที่จะถูกโจมตี CSRF en.wikipedia.org/wiki/Cross-site_request_forgery
Jeanno

1

โซลูชันของฉันปรากฏขึ้น เพียงแค่ตกแต่งชั้นเรียนของฉัน

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
แม้ว่ารหัสนี้อาจตอบคำถาม แต่การให้บริบทเพิ่มเติมเกี่ยวกับสาเหตุและ / หรือวิธีที่รหัสนี้ตอบคำถามช่วยเพิ่มคุณค่าในระยะยาว
Alex Riabov

1

เมื่อใช้ REST API POST การไม่มีส่วนหัวของคำขอ X-CSRFToken อาจทำให้เกิดข้อผิดพลาดนั้น เอกสาร Djangoมีโค้ดตัวอย่างในการรับและตั้งค่าโทเค็น CSRF จาก JS

ตามที่ระบุไว้ในคำตอบข้างต้นการตรวจสอบ CSRF จะเกิดขึ้นเมื่อใช้ SessionAuthentication อีกวิธีหนึ่งคือการใช้ TokenAuthentication แต่โปรดทราบว่าควรวางไว้ก่อนในรายการ DEFAULT_AUTHENTICATION_CLASSES ของการตั้งค่า REST_FRAMEWORK


1

หากคุณกำลังใช้สภาพแวดล้อมเสมือนพิเศษเฉพาะสำหรับแอปพลิเคชันของคุณคุณสามารถใช้แนวทางต่อไปนี้ได้โดยไม่ต้องใช้แอปพลิเคชันอื่น ๆ

สิ่งที่คุณสังเกตเห็นเกิดขึ้นเนื่องจากrest_framework/authentication.pyมีรหัสนี้ในauthenticateวิธีการของSessionAuthenticationคลาส:

self.enforce_csrf(request)

คุณสามารถปรับเปลี่ยนRequestคลาสเพื่อให้มีการเรียกคุณสมบัติcsrf_exemptและเริ่มต้นภายในคลาส View ของคุณตามลำดับTrueหากคุณไม่ต้องการตรวจสอบ CSRF ตัวอย่างเช่น:

จากนั้นแก้ไขโค้ดด้านบนดังนี้:

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

มีการเปลี่ยนแปลงที่เกี่ยวข้องบางอย่างที่คุณต้องทำในRequestชั้นเรียน


0

การลบการตรวจสอบ CSRF ไม่ใช่วิธีแก้ปัญหาเดียว (หรือดีที่สุด) เสมอไป SessionAuthenticationที่จริงมันเป็นกลไกการรักษาความปลอดภัยที่สำคัญสำหรับ

ฉันประสบปัญหาเดียวกันเมื่อพยายามรับรองความถูกต้องกับ JWT และส่งคำขอ POST

การตั้งค่าเริ่มต้นของฉันมีลักษณะดังนี้:

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework.authentication.SessionAuthentication",
        "django_cognito_jwt.JSONWebTokenAuthentication",
    ),
    ...
}

ตามที่SessionAuthenticationตรวจสอบก่อนในรายการข้อผิดพลาด CSRF จึงเพิ่มขึ้น วิธีแก้ปัญหาของฉันทำได้ง่ายเพียงแค่เปลี่ยนคำสั่งเพื่อตรวจสอบการตรวจสอบสิทธิ์ JWT ก่อนเสมอ แบบนี้:

    "DEFAULT_AUTHENTICATION_CLASSES": (
        "django_cognito_jwt.JSONWebTokenAuthentication",
        "rest_framework.authentication.SessionAuthentication",
    ),

ในตอนท้ายSessionAuthenticationสำหรับฉันใช้เฉพาะในแผงผู้ดูแลระบบ django และ 99% ของคำขอจะไปที่ API ที่ใช้การตรวจสอบสิทธิ์ JWT


-1

นอกจากนี้ยังอาจจะมีปัญหาในช่วงที่การโจมตี DNS rebinding

ในระหว่างการเปลี่ยนแปลง DNS สิ่งนี้อาจเป็นปัจจัยได้ การรอจนกว่า DNS จะถูกล้างอย่างสมบูรณ์จะแก้ไขปัญหานี้ได้หากทำงานก่อนที่ DNS จะเกิดปัญหา / การเปลี่ยนแปลง


สิ่งนี้เกี่ยวข้องกับคำถามข้างต้นอย่างไร
boatcoder

หมายความว่าปัญหานี้อาจเกิดขึ้นได้เมื่อคุณเปลี่ยน DNS และยังไม่แพร่กระจายอย่างสมบูรณ์ หากแอปมีการกำหนดเส้นทางที่แตกต่างจากเซสชันปกติของ Django นี่คือเหตุผลว่าทำไม เพียงแค่แจ้งกรณีขอบที่ฉันพบ ดูเหมือนว่าจะเป็นแหล่งข้อมูลที่ยอมรับได้ดังนั้นฉันจึงคิดว่าจะเพิ่มทรัพยากรเพิ่มเติม
chris Frisina
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.