GithubHelp home page GithubHelp logo

Comments (16)

benjamin-lawson avatar benjamin-lawson commented on June 18, 2024 1

To add on, I've figured out a workaround for this issue. It seems like the value contained in the X-CSRFToken header is incorrect when the request is coming from the Swagger docs. I wrote a middleware to take the unencrypted cookie value and replace the X-CSRFToken header with that value instead. That allows the request to go through and pass the CSRF check.

class CsrfInterceptMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        request.META['HTTP_X_CSRFTOKEN'] = request.COOKIES.get('csrftoken', request.META['HTTP_X_CSRFTOKEN'])
        response = self.get_response(request)
        return response

from django-ninja.

benjamin-lawson avatar benjamin-lawson commented on June 18, 2024 1

This is how I solved this problem.

https://django-ninja.dev/reference/csrf/

How to protect against CSRF with Django Ninja

Use an authentication method not automatically embedded in the request CSRF attacks rely on authentication methods that are automatically included in requests started from another site, like cookies or Basic access authentication. Using an authentication method that does not automatically gets embedded, such as the Authorization: Bearer header for exemple, mitigates this attack.

from django.contrib.auth import authenticate, login
from django.views.decorators.csrf import csrf_exempt

from typing import List
from ninja import NinjaAPI, Schema, Form, Redoc
from ninja.security import HttpBearer 

from .schemas import *

class AuthBearer(HttpBearer):
    def authenticate(self, request, token):
        if token == "1kh@g%5#01_8d89*2bheymz#vxt+3c#z9-xbs_a7swg%*1":
            return token

        return None

class MultipleAuth(HttpBearer):

    def authenticate(self, request, token):
        if token == "1kh@g%5#01_8d89*2bheymz#vxt+3c#z9-xbs_a7swg%*1" and request.user.is_authenticated:
            return request.user

        return None

api = NinjaAPI( csrf = False, auth = MultipleAuth() )

@api.post("/login", auth = AuthBearer(), response = { 200: LoginSchema, 401: MessageSchema })
@csrf_exempt
def user_login( request, username: Form[str], password: Form[str] ):
    user = authenticate( request, username = username, password = password )

    if user is None:
        return 401, {"message": "Password is incorrect. If you have forgot, please reset your password."}

    elif not user.is_active:
        return 401, {"message": "Your account status is inactive."}

    login(request, user)

    return user
@api.post("/create-post")
def create_post(request, payload: SinglePostSchema):
    group_post = GroupPost.objects.create(**payload.dict()) 
    return { "id": group_post.id }

Django Ninja CRUD example

This is how we solved the issue as well, switch to JWT or another type of auth that doesn't rely on CSRF checks. But that isn't really a solution to the problem, but a work around.

from django-ninja.

chrisJuresh avatar chrisJuresh commented on June 18, 2024

+1 same issue here

from django-ninja.

yzongyue avatar yzongyue commented on June 18, 2024

when calling the update profile endpoint, csrf_token should be included in request body

from django-ninja.

naveencom avatar naveencom commented on June 18, 2024

when calling the update profile endpoint, csrf_token should be included in request body

I'm a beginner. Could you please elaborate on how to include CSRF in request body ?

from django-ninja.

yzongyue avatar yzongyue commented on June 18, 2024

when calling the update profile endpoint, csrf_token should be included in request body

I'm a beginner. Could you please elaborate on how to include CSRF in request body ?

https://stackoverflow.com/questions/5100539/django-csrf-check-failing-with-an-ajax-post-request this may help.

from django-ninja.

naveencom avatar naveencom commented on June 18, 2024

when calling the update profile endpoint, csrf_token should be included in request body

I'm a beginner. Could you please elaborate on how to include CSRF in request body ?

https://stackoverflow.com/questions/5100539/django-csrf-check-failing-with-an-ajax-post-request this may help.

Okay.. But how do we pass csrf_token value to api consuming client (Eg: Mobile app) ?

from django-ninja.

chrisJuresh avatar chrisJuresh commented on June 18, 2024

when calling the update profile endpoint, csrf_token should be included in request body

I'm a beginner. Could you please elaborate on how to include CSRF in request body ?

https://stackoverflow.com/questions/5100539/django-csrf-check-failing-with-an-ajax-post-request this may help.

Okay.. But how do we pass csrf_token value to api consuming client (Eg: Mobile app) ?

Correct me if I'm wrong but it should be in the HttpResponse (header?) automatically if you are using django authentication.

from django-ninja.

benjamin-lawson avatar benjamin-lawson commented on June 18, 2024

when calling the update profile endpoint, csrf_token should be included in request body

I've verified, via a custom Middleware, that both the CSRF header is being (X-CSRFToken) and the cookie are being passed to the server. When those aren't present, I get a different error.

from django-ninja.

rafrasenberg avatar rafrasenberg commented on June 18, 2024

did anyone get this to work? I tried your solution @benjamin-lawson but no success for me. I get a KeyError:

backend_web-1                 | Traceback (most recent call last):
backend_web-1                 |   File "/usr/local/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
backend_web-1                 |     response = get_response(request)
backend_web-1                 |                ^^^^^^^^^^^^^^^^^^^^^
backend_web-1                 |   File "/usr/src/app/core/middleware.py", line 12, in __call__
backend_web-1                 |     "csrftoken", request.META["HTTP_X_CSRFTOKEN"]
backend_web-1                 |                  ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
backend_web-1                 | KeyError: 'HTTP_X_CSRFTOKEN'

Thoughts?

from django-ninja.

yzongyue avatar yzongyue commented on June 18, 2024

This is example code that works; django-ninja use https://github.com/django/django/blob/main/django/middleware/csrf.py#L349 check csrf



import requests


"""
python manage.py migrate
python manage.py shell -c "from django.contrib.auth import get_user_model; get_user_model().objects.create_superuser('[email protected]', 'adminadmin', 'adminadmin')"
"""


def main():
    base_url = 'http://127.0.0.1:8000'
    
    session = requests.Session()
    resp = session.post(base_url + '/login/', json={'email': '[email protected]', 'password': 'adminadmin'})
    print(resp.status_code)
    print(resp.text)
    print(resp.cookies)
    csrf_token = resp.cookies['csrftoken']
    print(csrf_token)

    resp = session.post(base_url + '/profile/', json={'first_name': 'John', 'last_name': 'Doe'}, headers={"X-CSRFTOKEN": csrf_token})
    print(resp.status_code)
    print(resp.text)


if __name__ == '__main__':
    main()

from django-ninja.

rafrasenberg avatar rafrasenberg commented on June 18, 2024

Yeah for me the CSRF works fine when making requests from my frontend. I'm specifically talking about the Swagger docs, that isn't working with the following setup:

api = NinjaAPI(....stuff, csrf=True)

api.add_router(
    "/v1",
    base_router,
    auth=django_auth,
)

# Some route
@router.post(
    "/",
    summary="Ping pong!",
    auth=None,
)
@ensure_csrf_cookie
def ping(request):
    return {"ping": "pong!"}

That will fail when making a request from Swagger. I want to protect my route with CSRF but not don't require a user to be logged in. @yzongyue

from django-ninja.

yzongyue avatar yzongyue commented on June 18, 2024

ensure_csrf_cookie may not required or just put it before @router.get, my test code:

@ensure_csrf_cookie
@api.get(
    "/pingpong",
    summary="Ping pong!",
    auth=None,
)
def ping(request):
    return {"ping": "pong!"}


@api.post(
    "/postpingpong",
    summary="Ping pong!",
    auth=None,
    # auth=django_auth
)
def postping(request, code: int):
    return {"ping": "pong!"}

and ensure django.middleware.csrf.CsrfViewMiddleware exist in settings.MIDDLEWARE @rafrasenberg

from django-ninja.

rafrasenberg avatar rafrasenberg commented on June 18, 2024

Thanks @yzongyue

The .get works indeed. However .post gets me CSRF verification failed. Request aborted. from Swagger.

from django-ninja.

naveencom avatar naveencom commented on June 18, 2024

This is how I solved this problem.

https://django-ninja.dev/reference/csrf/

How to protect against CSRF with Django Ninja

Use an authentication method not automatically embedded in the request CSRF attacks rely on authentication methods that are automatically included in requests started from another site, like cookies or Basic access authentication. Using an authentication method that does not automatically gets embedded, such as the Authorization: Bearer header for exemple, mitigates this attack.

from django.contrib.auth import authenticate, login
from django.views.decorators.csrf import csrf_exempt

from typing import List
from ninja import NinjaAPI, Schema, Form, Redoc
from ninja.security import HttpBearer 

from .schemas import *

class AuthBearer(HttpBearer):
    def authenticate(self, request, token):
        if token == "1kh@g%5#01_8d89*2bheymz#vxt+3c#z9-xbs_a7swg%*1":
            return token

        return None

class MultipleAuth(HttpBearer):

    def authenticate(self, request, token):
        if token == "1kh@g%5#01_8d89*2bheymz#vxt+3c#z9-xbs_a7swg%*1" and request.user.is_authenticated:
            return request.user

        return None

api = NinjaAPI( csrf = False, auth = MultipleAuth() )

@api.post("/login", auth = AuthBearer(), response = { 200: LoginSchema, 401: MessageSchema })
@csrf_exempt
def user_login( request, username: Form[str], password: Form[str] ):
    user = authenticate( request, username = username, password = password )

    if user is None:
        return 401, {"message": "Password is incorrect. If you have forgot, please reset your password."}

    elif not user.is_active:
        return 401, {"message": "Your account status is inactive."}

    login(request, user)

    return user
@api.post("/create-post")
def create_post(request, payload: SinglePostSchema):
    group_post = GroupPost.objects.create(**payload.dict()) 
    return { "id": group_post.id }

Django Ninja CRUD example

from django-ninja.

pizzapanther avatar pizzapanther commented on June 18, 2024

This issue is because APIKeyCookie which SessionAuth inherits from for django_auth defaults to csrf = True even if the NinjaAPI has CSRF off.

I fixed it with a custom django_auth

class CustomSessionAuth(SessionAuth):
  def __init__(self, csrf=True):
    super().__init__(csrf)
    self.csrf = False

django_auth = CustomSessionAuth()

I guess when using APIKeyCookie, CSRF On is best for security? However, for session auth that is not always the case because session auth has several mechanisms. Maybe there should be a setting to tweak CSRF on or off for SessionAuth?

from django-ninja.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.