2

I am using Django REST framework to have data available in JSON and using them to refresh the page via AJAX. How can I secure the URL that page is being updated with data and no one can access the API url. The url is visible in AJAX in html so it can be accessed but I would like to prevent it by token or any other proper authentication that only access to it has website.

The URL is '/api/item/' (see in the AJAX code)

serializers.py

from rest_framework import serializers

from .models import Item

class ItemModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = [
            'title',
            'value',
        ]

views.py (in API)

from rest_framework import generics
from .serializers import ItemModelSerializer
from .models import Item

class ItemListAPIView(generics.ListAPIView):
    serializer_class = ItemModelSerializer

    def get_queryset(self):
        return Item.objects.all().order_by('sort')

urls.py

urlpatterns = [
    #...urls...
    url(r'^api/item/', include('apps.item.api.urls', namespace='api-item')),
    url(r'^admin/', admin.site.urls),
]

template - ajax

setInterval(function() {
    $.ajax({
        method: "GET",
        url: "/api/item/",
        success: function(data) {
            $("#items tbody").empty();
            $.each(data, function (key, value) {                                      
                var itemKey = key;
                var itemTitle = value.title;  
                var itemValue = value.value;
                $("#items tbody").append(
                   "<tr><td class='left'>" + itemTitle + "</td><td>" + itemValue</td></tr>"
                )
            })
        },
        error: function(data) {
            console.log("error")
            console.log(data)
        }
    })
}, 3000)
Radek
  • 1,149
  • 2
  • 19
  • 40

2 Answers2

0

I'm not sure if I understand your question correctly, I guess you are asking how do you set a token in AJAX for your API in Django. Usually, we use a concept of shared token. There will be a login API which will authenticate a user with a username/password. If the credentials are correct, auth API will return the token which you can use and set in AJAX requests. Use that token to make further calls to your API for authentication.

If you directly want to call your API without auth API, you will have to use a hard coded token shared between Django and AJAX however it is unsafe to follow such practices.

Look here for DRF authentication http://www.django-rest-framework.org/api-guide/authentication/#sessionauthentication

pass-by-ref
  • 1,288
  • 2
  • 12
  • 20
  • 1
    "If you directly want to call your API without auth API, you will have to use a hard coded token shared between Django and AJAX however it is unsafe to follow such practices." - that is my precisely the point - how to secure GET requests in Django, AJAX, JS only that website can use it nobody else? – Radek May 12 '17 at 01:00
0

I have done something similar to this:

@Shwetabh Sharan you will have to use a hardcoded token shared between Django and AJAX

But

@Shwetabh Sharan says, however, it is unsafe to follow such practices

So far no one has addressed this issue how to secure GET or POST requests in Django, AJAX, JS only that website can use it nobody else

they always recommend "JWT, token authentication and session authentication", etc. But all those solutions require a user registered, and what happens if I want to protect a search autocomplete field or anything on the frontend that is public

I have created a rest framework custom permission and I using django csrfmiddlewaretoken

it behaves the same like Django post form, csrf token middleware send an input type hidden with {% csrf_token %}(input hidden with the token) but and this case only needs {{ csrf_token }}(the token), Django compares the request token with cookie token which are encrypted

permission.py
    from django.middleware.csrf import _compare_salted_tokens, rotate_token
    from rest_framework.permissions import BasePermission


    class HasCsrfTokenValid(BasePermission):
        def has_permission(self, request, view):
            token_valid = False
            try:
                csrf_token = request.headers.get("api-csrftoken")
                csrf_cookie = request.META.get("CSRF_COOKIE")

                """
                Check if both alphanumerics(strings) values are differents to prevent 
                a malicious user get the csrf cookie and send it from the ajax.
                """
                if csrf_token == csrf_cookie:
                    rotate_token(request)
                    return False

                token_valid =  _compare_salted_tokens(csrf_token, csrf_cookie)
            except ValueError: # if csrf_token & csrf_cookie are not a valid alphanumeric
                return False
            return token_valid

add permission class to api view

views.py
class SnippetSearchAPI(generics.ListAPIView):
    model = Snippet
    serializer_class = SnippetSearcSerializer
    permission_classes = [HasCsrfTokenValid,]
    queryset = Snippet.objects.all()

Ajax request:

$('#search-snippet').keyup(function () {
    $.ajax({
       url:"{% url 'snippet-search-space:api' %}",
       headers:{'api-csrftoken':'{{ csrf_token }}'},
       success:function(data){
         console.log(data)
       }
   });
 });
LuisSolis
  • 358
  • 3
  • 11