0

I use Django in production.

I see that if an unauthenticated user accesses an exposed API - the server will return HTTP 401 Unauthorized.

If the user will access a non-existing API (with or without authentication) - the server will return 404 Not found.

This seems to me like a bad security practice and allows an attacker to find the server's exposed APIs.

Is there a way to change that so both will return the exact same result (I think 401 is the best practice, no?)

user972014
  • 3,296
  • 6
  • 49
  • 89

1 Answers1

5

I would create a fall-back URL in the Django server to match all non-definitive URLs

from django.contrib import admin
from django.http.response import JsonResponse
from django.urls import path, re_path


def not_found_json(request, any_path=None):
    return JsonResponse({'message': 'not allowed'}, status=403)


urlpatterns = [
    path('admin/', admin.site.urls),
    # app1 urls
    # app2 urls
    # app3 urls
    # at last,
    re_path(r'(?P<any_path>.+)', not_found_json, name='not-found-json'),
]

Notes

  1. this fall-back URL definition must be in your ROOT_URLCONF--(Django doc)
  2. The pattern expression must be on the bottom of the list
  3. Use 403 Forbidden, which is more appropriate than 401 Unauthorized
JPG
  • 82,442
  • 19
  • 127
  • 206
  • I accepted the answer. But I would add: 1. to make it more consistent with the answer that django returns when not having the right permission, should return Unauthorized. This is the way: https://stackoverflow.com/a/14263766/972014 2. Should also disable BrowsableAPIRenderer in django-rest-framework as explained here: https://stackoverflow.com/a/49395080/972014 – user972014 Sep 23 '20 at 20:44
  • I strongly disagree with ***Unauthorized*** (Ref: [401 Unauthorized vs 403 Forbidden](https://stackoverflow.com/questions/50143518/401-unauthorized-vs-403-forbidden-which-is-the-right-status-code-for-when-the-u?rq=1) ). In your case, User may have *logged-in*, but he must not have the right ***permission*** – JPG Sep 24 '20 at 02:27
  • 1
    Also, The [**`JsonResponse`**](https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.JsonResponse) won't render any ***Browsable API*** because, it comes with bare Django, not with DRF. – JPG Sep 24 '20 at 02:28
  • Regarding the Unauthorized - I now understand. You are right. Regarding the Browsable - in default configuration browsable is supported, so navigating in the browser to the API directly will expose information about it. – user972014 Sep 24 '20 at 15:50
  • 1
    You can render the API with or without the browsable option, it has nothing to do with ***fall-back logic***. But, I admit that disabling the browsable API is a good choice and I did that in my project too. Apart from that, my point is that ***the browsable API rendering is only applicable for DRF views/APIs***, and here the `not_found_json(...)` is not a DRF view/API. – JPG Sep 24 '20 at 15:57