Comments (16)
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.
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 }
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.
+1 same issue here
from django-ninja.
when calling the update profile
endpoint, csrf_token should be included in request body
from django-ninja.
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.
when calling the
update profile
endpoint, csrf_token should be included in request bodyI'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.
when calling the
update profile
endpoint, csrf_token should be included in request bodyI'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.
when calling the
update profile
endpoint, csrf_token should be included in request bodyI'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.
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.
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.
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.
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.
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.
Thanks @yzongyue
The .get
works indeed. However .post
gets me CSRF verification failed. Request aborted.
from Swagger.
from django-ninja.
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 }
from django-ninja.
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)
- [BUG] Async Error Handlers Return Unawaited Coroutines Instead of HttpResponse in Django Ninja
- Dependency Injection support with `anydi`
- [BUG] `models.URLField` serialized & validated as `str` HOT 1
- Exceptions log level HOT 1
- [BUG] CustomParser request header issue when sending json and file
- [BUG] `ModelSchema` produces `id > (integer | null)` openapi HOT 1
- How to set query param for test.
- When are we getting a new release? HOT 3
- How to detect client disconnects on StreamingHttpResponse (Server Side Events)? HOT 4
- Support "Accept" & "Content-Type" Headers
- [BUG] no Response object generated for ASGI api route HOT 1
- Change settings access to support FastAPI-style single file apps
- How to test api.exception_handler
- Not required fields in FilterSchema
- Any way to run auth before route decorators?
- 中文文档上线了
- Some way to deal with auth
- [BUG] Pydantic doesn't allow null=True on BooleanFields when using ModelSchema. HOT 2
- Static Method Not Reflecting Instance-Specific Argument in Dynamically Created ModelSchema Class
- [BUG] `fields_optional="__all__"` causes TypeError on models with a field using the `default` parameter
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from django-ninja.