Upgrade to Pro — share decks privately, control downloads, hide ads and more …

REST Easy — API Security Done Right

Jeff Schenck
September 07, 2015

REST Easy — API Security Done Right

As frontend web frameworks like AngularJS and Backbone.js become more common, running a REST API using Django and Django REST Framework is becoming unavoidable, and security is absolutely critical. I'll show you the tools we're working with and how to wrangle them. I'll also talk about where we need to take these tools to make the world safe for Django and frontend frameworks.

2015-09-07 — DjangoCon 2015 — https://2015.djangocon.us/schedule/presentation/76/

Jeff Schenck

September 07, 2015
Tweet

More Decks by Jeff Schenck

Other Decks in Programming

Transcript

  1. REST Easy 1. WHY REST 2. STATE OF REST 3.

    REST SECURITY 4. REST WELL TODAY 5. REST EASY TOMORROW
  2. REST Easy 2. STATE OF REST 1. WHY REST 3.

    REST SECURITY 4. REST WELL TODAY 5. REST EASY TOMORROW
  3. REST Easy 3. REST SECURITY 1. WHY REST 2. STATE

    OF REST 4. REST WELL TODAY 5. REST EASY TOMORROW
  4. REST Easy 4. REST WELL TODAY 1. WHY REST 2.

    STATE OF REST 3. REST SECURITY 5. REST EASY TOMORROW
  5. REST Easy 5. REST EASY TOMORROW 1. WHY REST 2.

    STATE OF REST 3. REST SECURITY 4. REST WELL TODAY
  6. REST Easy 1. WHY REST 2. STATE OF REST 3.

    REST SECURITY 4. REST WELL TODAY 5. REST EASY TOMORROW
  7. REST Easy FRONTEND BACKEND ← HTML • CSS • JavaScript

    Forms → ← → Display App Logic Datastores
  8. REST Easy FRONTEND BACKEND ← → ← HTML • CSS

    • JavaScript Forms • AJAX → Display App Logic Datastores
  9. REST Easy FRONTEND BACKEND ← → ← HTML • CSS

    • JavaScript Forms • AJAX → Display App Logic Datastores
  10. REST Easy FRONTEND BACKEND ← → ← HTML • CSS

    • JavaScript ← REST → Display App Logic Datastores
  11. REST Easy 2. STATE OF REST 1. WHY REST 3.

    REST SECURITY 4. REST WELL TODAY 5. REST EASY TOMORROW
  12. REST Easy 3. REST SECURITY 1. WHY REST 2. STATE

    OF REST 4. REST WELL TODAY 5. REST EASY TOMORROW
  13. REST Easy AUTHENTICATION • HTTP Basic Auth • Django Session

    Auth • Token Auth • OAuth • JSON Web Token Auth
  14. REST Easy PERMISSIONS • Table Permissions • Row Permissions •

    Column Permissions • HTTP Verb Permissions
  15. REST Easy 4. REST WELL TODAY 1. WHY REST 2.

    STATE OF REST 3. REST SECURITY 5. REST EASY TOMORROW
  16. REST Easy HTTP VERBS class IsPOST(permissions.BasePermission): def has_permission(self, request, view):

    return request.method == 'POST' class IsPATCH(permissions.BasePermission): def has_permission(self, request, view): return request.method == 'PATCH' # Other HTTP verbs...
  17. REST Easy HTTP VERBS class IsPOST(permissions.BasePermission): def has_permission(self, request, view):

    return request.method == 'POST' class IsPATCH(permissions.BasePermission): def has_permission(self, request, view): return request.method == 'PATCH' # Other HTTP verbs...
  18. REST Easy HTTP VERBS class IsPOST(permissions.BasePermission): def has_permission(self, request, view):

    return request.method == 'POST' class IsPATCH(permissions.BasePermission): def has_permission(self, request, view): return request.method == 'PATCH' # Other HTTP verbs...
  19. REST Easy HTTP VERBS class IsPOST(permissions.BasePermission): def has_permission(self, request, view):

    return request.method == 'POST' class IsPATCH(permissions.BasePermission): def has_permission(self, request, view): return request.method == 'PATCH' # Other HTTP verbs...
  20. REST Easy HTTP VERBS class IsPOST(permissions.BasePermission): def has_permission(self, request, view):

    return request.method == 'POST' class IsPATCH(permissions.BasePermission): def has_permission(self, request, view): return request.method == 'PATCH' # Other HTTP verbs...
  21. REST Easy HTTP VERBS class IsPOST(permissions.BasePermission): def has_permission(self, request, view):

    return request.method == 'POST' class IsPATCH(permissions.BasePermission): def has_permission(self, request, view): return request.method == 'PATCH' # Other HTTP verbs...
  22. REST Easy USER STATUS class IsAuthenticated(permissions.BasePermission): def has_permission(self, request, view):

    return request.user.is_authenticated() class IsSuperuser(permissions.BasePermission): def has_permission(self, request, view): return request.user.is_authenticated() and \ request.user.is_superuser # Other authentication methods...
  23. REST Easy USER STATUS class IsAuthenticated(permissions.BasePermission): def has_permission(self, request, view):

    return request.user.is_authenticated() class IsSuperuser(permissions.BasePermission): def has_permission(self, request, view): return request.user.is_authenticated() and \ request.user.is_superuser # Other authentication methods...
  24. REST Easy USER STATUS class IsAuthenticated(permissions.BasePermission): def has_permission(self, request, view):

    return request.user.is_authenticated() class IsSuperuser(permissions.BasePermission): def has_permission(self, request, view): return request.user.is_authenticated() and \ request.user.is_superuser # Other authentication methods...
  25. REST Easy USER STATUS class IsAuthenticated(permissions.BasePermission): def has_permission(self, request, view):

    return request.user.is_authenticated() class IsSuperuser(permissions.BasePermission): def has_permission(self, request, view): return request.user.is_authenticated() and \ request.user.is_superuser # Other authentication methods...
  26. REST Easy USER STATUS class IsAuthenticated(permissions.BasePermission): def has_permission(self, request, view):

    return request.user.is_authenticated() class IsSuperuser(permissions.BasePermission): def has_permission(self, request, view): return request.user.is_authenticated() and \ request.user.is_superuser # Other authentication methods...
  27. REST Easy USER STATUS class IsAuthenticated(permissions.BasePermission): def has_permission(self, request, view):

    return request.user.is_authenticated() class IsSuperuser(permissions.BasePermission): def has_permission(self, request, view): return request.user.is_authenticated() and \ request.user.is_superuser # Other authentication methods...
  28. REST Easy OWNERSHIP class IsCatOwner(permissions.BasePermission): def has_permission(self, request, view): #

    WARNING: List-level permissions in get_queryset return True def has_object_permission(self, request, view, obj): return obj.owner == request.user # Ownership for other objects...
  29. REST Easy OWNERSHIP class IsCatOwner(permissions.BasePermission): def has_permission(self, request, view): #

    WARNING: List-level permissions in get_queryset return True def has_object_permission(self, request, view, obj): return obj.owner == request.user # Ownership for other objects...
  30. REST Easy OWNERSHIP class IsCatOwner(permissions.BasePermission): def has_permission(self, request, view): #

    WARNING: List-level permissions in get_queryset return True def has_object_permission(self, request, view, obj): return obj.owner == request.user # Ownership for other objects...
  31. REST Easy OWNERSHIP class IsCatOwner(permissions.BasePermission): def has_permission(self, request, view): #

    WARNING: List-level permissions in get_queryset return True def has_object_permission(self, request, view, obj): return obj.owner == request.user # Ownership for other objects...
  32. REST Easy OWNERSHIP class IsCatOwner(permissions.BasePermission): def has_permission(self, request, view): #

    WARNING: List-level permissions in get_queryset return True def has_object_permission(self, request, view, obj): return obj.owner == request.user # Ownership for other objects...
  33. REST Easy OWNERSHIP class IsCatOwner(permissions.BasePermission): def has_permission(self, request, view): #

    WARNING: List-level permissions in get_queryset return True def has_object_permission(self, request, view, obj): return obj.owner == request.user # Ownership for other objects...
  34. REST Easy FIELD RESTRICTION def IsFields(*fields): class IsFieldsPermission(permissions.BasePermission): _fields =

    set(fields) def has_permission(self, request, view): return set(request.data.keys()) <= self._fields def has_object_permission(self, request, view, obj): return set(request.data.keys()) <= self._fields return IsFieldsPermission
  35. REST Easy FIELD RESTRICTION def IsFields(*fields): class IsFieldsPermission(permissions.BasePermission): _fields =

    set(fields) def has_permission(self, request, view): return set(request.data.keys()) <= self._fields def has_object_permission(self, request, view, obj): return set(request.data.keys()) <= self._fields return IsFieldsPermission
  36. REST Easy FIELD RESTRICTION def IsFields(*fields): class IsFieldsPermission(permissions.BasePermission): _fields =

    set(fields) def has_permission(self, request, view): return set(request.data.keys()) <= self._fields def has_object_permission(self, request, view, obj): return set(request.data.keys()) <= self._fields return IsFieldsPermission
  37. REST Easy FIELD RESTRICTION def IsFields(*fields): class IsFieldsPermission(permissions.BasePermission): _fields =

    set(fields) def has_permission(self, request, view): return set(request.data.keys()) <= self._fields def has_object_permission(self, request, view, obj): return set(request.data.keys()) <= self._fields return IsFieldsPermission
  38. REST Easy FIELD RESTRICTION def IsFields(*fields): class IsFieldsPermission(permissions.BasePermission): _fields =

    set(fields) def has_permission(self, request, view): return set(request.data.keys()) <= self._fields def has_object_permission(self, request, view, obj): return set(request.data.keys()) <= self._fields return IsFieldsPermission
  39. REST Easy FIELD RESTRICTION def IsFields(*fields): class IsFieldsPermission(permissions.BasePermission): _fields =

    set(fields) def has_permission(self, request, view): return set(request.data.keys()) <= self._fields def has_object_permission(self, request, view, obj): return set(request.data.keys()) <= self._fields return IsFieldsPermission
  40. REST Easy FIELD RESTRICTION def IsFields(*fields): class IsFieldsPermission(permissions.BasePermission): _fields =

    set(fields) def has_permission(self, request, view): return set(request.data.keys()) <= self._fields def has_object_permission(self, request, view, obj): return set(request.data.keys()) <= self._fields return IsFieldsPermission
  41. REST Easy FIELD RESTRICTION def IsFields(*fields): class IsFieldsPermission(permissions.BasePermission): _fields =

    set(fields) def has_permission(self, request, view): return set(request.data.keys()) <= self._fields def has_object_permission(self, request, view, obj): return set(request.data.keys()) <= self._fields return IsFieldsPermission
  42. REST Easy FIELD RESTRICTION def IsFields(*fields): class IsFieldsPermission(permissions.BasePermission): _fields =

    set(fields) def has_permission(self, request, view): return set(request.data.keys()) <= self._fields def has_object_permission(self, request, view, obj): return set(request.data.keys()) <= self._fields return IsFieldsPermission
  43. REST Easy REST CONDITION from rest_condition import And, Or, Not

    combined = Or( And(FooPerm, Not(BarPerm)), BazPerm, )
  44. REST Easy REST CONDITION from rest_condition import And, Or, Not

    combined = Or( And(FooPerm, Not(BarPerm)), BazPerm, )
  45. REST Easy REST CONDITION from rest_condition import And, Or, Not

    combined = Or( And(FooPerm, Not(BarPerm)), BazPerm, )
  46. REST Easy REST CONDITION from rest_condition import And, Or, Not

    combined = Or( And(FooPerm, Not(BarPerm)), BazPerm, )
  47. REST Easy REST CONDITION from rest_condition import And, Or, Not

    combined = Or( And(FooPerm, Not(BarPerm)), BazPerm, )
  48. REST Easy REST CONDITION from rest_condition import And, Or, Not

    combined = Or( And(FooPerm, Not(BarPerm)), BazPerm, )
  49. REST Easy COMPLEX PERMISSIONS class CatViewSet(viewsets.ModelViewSet): permission_classes = [Or( And(

    Or(IsGET, IsHEAD, IsOPTIONS), IsAuthenticated, ), And( Or(IsPOST, IsPATCH), IsCatOwner, IsFields('hair', 'grumpiness'), ), IsSuperuser, )]
  50. REST Easy COMPLEX PERMISSIONS class CatViewSet(viewsets.ModelViewSet): permission_classes = [Or( And(

    Or(IsGET, IsHEAD, IsOPTIONS), IsAuthenticated, ), And( Or(IsPOST, IsPATCH), IsCatOwner, IsFields('hair', 'grumpiness'), ), IsSuperuser, )]
  51. REST Easy COMPLEX PERMISSIONS class CatViewSet(viewsets.ModelViewSet): permission_classes = [Or( And(

    Or(IsGET, IsHEAD, IsOPTIONS), IsAuthenticated, ), And( Or(IsPOST, IsPATCH), IsCatOwner, IsFields('hair', 'grumpiness'), ), IsSuperuser, )]
  52. REST Easy COMPLEX PERMISSIONS class CatViewSet(viewsets.ModelViewSet): permission_classes = [Or( And(

    Or(IsGET, IsHEAD, IsOPTIONS), IsAuthenticated, ), And( Or(IsPOST, IsPATCH), IsCatOwner, IsFields('hair', 'grumpiness'), ), IsSuperuser, )]
  53. REST Easy COMPLEX PERMISSIONS class CatViewSet(viewsets.ModelViewSet): permission_classes = [Or( And(

    Or(IsGET, IsHEAD, IsOPTIONS), IsAuthenticated, ), And( Or(IsPOST, IsPATCH), IsCatOwner, IsFields('hair', 'grumpiness'), ), IsSuperuser, )]
  54. REST Easy COMPLEX PERMISSIONS class CatViewSet(viewsets.ModelViewSet): permission_classes = [Or( And(

    Or(IsGET, IsHEAD, IsOPTIONS), IsAuthenticated, ), And( Or(IsPOST, IsPATCH), IsCatOwner, IsFields('hair', 'grumpiness'), ), IsSuperuser, )]
  55. REST Easy COMPLEX PERMISSIONS class CatViewSet(viewsets.ModelViewSet): permission_classes = [Or( And(

    Or(IsGET, IsHEAD, IsOPTIONS), IsAuthenticated, ), And( Or(IsPOST, IsPATCH), IsCatOwner, IsFields('hair', 'grumpiness'), ), IsSuperuser, )]
  56. REST Easy 5. REST EASY TOMORROW 1. WHY REST 2.

    STATE OF REST 3. REST SECURITY 4. REST WELL TODAY
  57. REST Easy REST SECURITY ISSUES • Endpoint-based • Scattered code

    • Batteries not included • Default-confused
  58. REST Easy EASY PERMISSIONS class AnonymousUser(rest_permissions.Role): def is_active(self, request): return

    request.user.is_anonymous() def get_permissions(self): return { 'api.cats.CatViewSet': { 'list': True, 'retrieve': True, }, # Other permissions for cat owners... }
  59. REST Easy EASY PERMISSIONS class AnonymousUser(rest_permissions.Role): def is_active(self, request): return

    request.user.is_anonymous() def get_permissions(self): return { 'api.cats.CatViewSet': { 'list': True, 'retrieve': True, }, # Other permissions for cat owners... }
  60. REST Easy EASY PERMISSIONS class AnonymousUser(rest_permissions.Role): def is_active(self, request): return

    request.user.is_anonymous() def get_permissions(self): return { 'api.cats.CatViewSet': { 'list': True, 'retrieve': True, }, # Other permissions for cat owners... }
  61. REST Easy EASY PERMISSIONS class AnonymousUser(rest_permissions.Role): def is_active(self, request): return

    request.user.is_anonymous() def get_permissions(self): return { 'api.cats.CatViewSet': { 'list': True, 'retrieve': True, }, # Other permissions for cat owners... }
  62. REST Easy EASY PERMISSIONS class AnonymousUser(rest_permissions.Role): def is_active(self, request): return

    request.user.is_anonymous() def get_permissions(self): return { 'api.cats.CatViewSet': { 'list': True, 'retrieve': True, }, # Other permissions for cat owners... }
  63. REST Easy EASY PERMISSIONS class AnonymousUser(rest_permissions.Role): def is_active(self, request): return

    request.user.is_anonymous() def get_permissions(self): return { 'api.cats.CatViewSet': { 'list': True, 'retrieve': True, }, # Other permissions for cat owners... }
  64. REST Easy EASY PERMISSIONS class AnonymousUser(rest_permissions.Role): def is_active(self, request): return

    request.user.is_anonymous() def get_permissions(self): return { 'api.cats.CatViewSet': { 'list': True, 'retrieve': True, }, # Other permissions for cat owners... }
  65. REST Easy EASY PERMISSIONS class AnonymousUser(rest_permissions.Role): def is_active(self, request): return

    request.user.is_anonymous() def get_permissions(self): return { 'api.cats.CatViewSet': { 'list': True, 'retrieve': True, }, # Other permissions for cat owners... }
  66. REST Easy EASY PERMISSIONS class AnonymousUser(rest_permissions.Role): def is_active(self, request): return

    request.user.is_anonymous() def get_permissions(self): return { 'api.cats.CatViewSet': { 'list': True, 'retrieve': True, }, # Other permissions for cat owners... }