CSRF exemption for cookie-less requests

I have this idea for a change to the CSRF protection, but looking for someone to point out the security hole(s) in it I’m failing to find myself:

The tl;dr is that I think requests that don’t send cookies should be automatically exempted from CSRF protection.

In its current incarnation, CsrfMiddleware protects all requests if they’re using one of the unsafe (RFC 9110) methods. This creates some friction when you have a Django project that uses both “browser” type views (cookie-based authentication) and “API” type views (Authorization header based authentication, typically using the Bearer authentication scheme); The API type views have to be explicitly exempted from protection, and it’s impossible for the same view to support both authentication schemes.

But seeing as the absence of cookies would mean there’s nothing to really ‘forge’, couldn’t it make sense to simply accept the request if no cookies are present? That should make supporting things like Bearer tokens for views work out of the box. Is there a security problem to this I’m missing?

I am aware of Implement Modern CSRF Protection · Issue #98 · django/new-features · GitHub, which is a radically different approach; if that ends up being implemented, my suggestion here isn’t relevant, although mine should maintain exactly the same browser support as the current does.

DRF’s APIView handles this for you — disabling CSRF except where SessionAuthentication is used. See the topic discussion there for more details:

I’d suggest just using that.

If Django were to adopt some kind of similar base API view then it would need to apply the same sort of approach. But it doesn’t strike me as a just turn it off for… kind of thing. The CSRF Edge Cases docs already have the suggested approach — either using @csrf_excempt for just your API views, or the more complex example protecting just a single code branch would be the ones to think about. (The latter there would make an interesting case as a project base view, allowing a single endpoint to handle both types of authenticated request.)

True, there are existing ways to work around it.

My suggestion was for a built-in way to deal with it that wouldn’t require additional workarounds or external libraries, but if the official recommendation for “API” type views is to use Django REST framework, then I think that’s fair.