預(yù)防對(duì)象級(jí)授權(quán)失效

由于對(duì)象級(jí)授權(quán)失效會(huì)影響企業(yè),因此了解如何防止此類(lèi)攻擊將使企業(yè)免于丟失敏感數(shù)據(jù)。在本節(jié)中,我們將討論處理對(duì)象級(jí)授權(quán)失效的各種方法。

隨機(jī)生成的唯一標(biāo)識(shí)符

建議使用全局唯一標(biāo)識(shí)符 (GUID) 或通用唯一標(biāo)識(shí)符 (UUID) 來(lái)減少可預(yù)測(cè)對(duì)象資源標(biāo)識(shí)符帶來(lái)的威脅。UUID 更常用,因?yàn)樗鼈儠?huì)生成較大的值(通常是 128 位隨機(jī)整數(shù)),并且發(fā)生沖突的可能性較低。當(dāng)兩個(gè)或多個(gè)標(biāo)識(shí)符指向同一個(gè)對(duì)象時(shí),就會(huì)發(fā)生沖突。UUID 字符串的一個(gè)示例是092a5461-e71b-32d1-e544-792314582966

要生成 UUID,請(qǐng)運(yùn)行以下命令:

pip install uuid

models.py中,運(yùn)行以下命令:

import uuid
from django.db import models

class Subscription(models.Model):
id = models.UUIDField(unique=True, primary_key=True, editable=False, default=uuid.uuid4)
...

使用 UUID 使得攻擊者更難猜測(cè)資源對(duì)象標(biāo)識(shí)符。

Django 群組

除了使用 UUID 之外,建立某種訪問(wèn)控制來(lái)控制授權(quán)和未經(jīng)身份驗(yàn)證的用戶(hù)能夠操作的數(shù)據(jù)類(lèi)型也至關(guān)重要。建立訪問(wèn)控制的方法之一是在 Django 中對(duì)用戶(hù)進(jìn)行分組。這可確保特定用戶(hù)組可以執(zhí)行特定操作,從而有助于管理訪問(wèn)。

通過(guò)使用 Django Group,我們可以限制對(duì)資源對(duì)象的訪問(wèn)。例如,在manager.py文件中,運(yùn)行以下命令:

from django.contrib.auth.models import UserManager, Group

class CustomUserManager(UserManager):
use_in_migration = True
def _create_user(self, email, password, **extra_fields):
"""Create user with a given email and password"""
if not email or not password:
raise ValueError("Users must have an email and password")
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)

if extra_fields["is_supplier"] == True:
self._add_user_to_group("supplier", user)
...

def _add_user_to_group(self, group_name, user):
user_group, _ = Group.objects.get_or_create(name=group_name)
user_group.user_set.add(user)
...

我們可以創(chuàng)建一個(gè)包含 Django 組的自定義用戶(hù)管理器。根據(jù)用戶(hù)角色(例如供應(yīng)商),我們可以在上面的代碼片段中創(chuàng)建用戶(hù)時(shí)將其分配到組中。在models.py中,運(yùn)行以下命令:

import uuid
from django.db import models
from django.contrib.auth.models import AbstractUser
from managers.py import CustomUserManager

class User(AbstractUser):
id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4)
...
objects = CustomUserManager()

然后,我們可以在 User 資源中使用自定義用戶(hù)管理器。雖然對(duì)象級(jí)授權(quán)沒(méi)有發(fā)生太多變化,但我們已經(jīng)成功為在平臺(tái)上注冊(cè)的所有用戶(hù)創(chuàng)建了權(quán)限組,這是對(duì)資源對(duì)象進(jìn)行訪問(wèn)控制的一步。在業(yè)務(wù)邏輯中,我們可以將內(nèi)容限制為供應(yīng)商組中的用戶(hù)。例如,在views.py中使用 Django REST 框架,運(yùn)行以下命令:

from rest_framework import viewsets, status, permissions
from rest_framework.response import Response
...

class ReportView(viewset.ModelViewSet):
queryset = Report.objects.all()
permission_classes = [permissions.IsAuthenticated]

def list(self, request):
if request.user.groups.filter(name="supplier").exists():
# PERFORM ACTIONS FOR SUPPLIERS ONLY
return Response({
"detail": "You do not have permission to perform this action"
}, status=status.HTTP_403_FORBIDDEN)

代碼片段根據(jù)用戶(hù)組篩選經(jīng)過(guò)身份驗(yàn)證的用戶(hù)。如果用戶(hù)不屬于供應(yīng)商組,則他們無(wú)法訪問(wèn)資源內(nèi)容。

我們可以注意到,如果用戶(hù)屬于供應(yīng)商組,那么無(wú)論他們是否是資源對(duì)象的所有者,他們都可以訪問(wèn)數(shù)據(jù)。

資源所有權(quán)

為了確保不會(huì)將數(shù)據(jù)泄露給其他用戶(hù),我們將有關(guān)該資源對(duì)象創(chuàng)建者的信息存儲(chǔ)在對(duì)象中。例如,假設(shè)屬于供應(yīng)商組的用戶(hù)創(chuàng)建了一個(gè)報(bào)告實(shí)例。為了跟蹤誰(shuí)擁有該報(bào)告實(shí)例,我們?cè)谫Y源數(shù)據(jù)中添加了一個(gè)名為“用戶(hù)”或“所有者”的字段,如下所示。

models.py中,運(yùn)行以下命令:

import uuid
from django.db import models

class Report(models.Model):
id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4)
owner = models.ForeignKey("User", on_delete=models.CASCADE, related_name=reports)
title = models.CharField(max_length=250)
...

在這里,我們通過(guò)將外鍵分配給用戶(hù)資源將報(bào)告附加到其所有者。

{
"owner": "as34-ade42315d-893a-0934d",
"title": "An unsecure report that exposes the owner ID"
}

現(xiàn)在,前端將提供如上所示的 JSON 對(duì)象。

從身份驗(yàn)證令牌中提取 ID

為了防止前端提供報(bào)告創(chuàng)建者,我們從經(jīng)過(guò)身份驗(yàn)證的用戶(hù)那里獲取它。我們更新views.py,如下所示。

from rest_framework import viewsets, status, permissions
from rest_framework.response import Response
...

class ReportView(viewset.ModelViewSet):
queryset = Report.objects.all()
permission_classes = [permissions.IsAuthenticated]
serializer_class = ReportSerializer

def create(self, request):
serializer = self.serializer_class(request.data)
if serializer.is_valid(raise_exception=True):
serializer.save(user=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.error_messages, status=status.HTTP_400_BAD_REQUEST)

前端客戶(hù)端只需要提供報(bào)表數(shù)據(jù),而不需要明確給出“所有者”字段數(shù)據(jù)。

{
"title": "This report no longer requires the owner ID on creation"
...
}

按所有權(quán)查詢(xún)

盡管報(bào)告現(xiàn)在屬于特定用戶(hù),但我們?nèi)詴?huì)檢索平臺(tái)上的所有報(bào)告,無(wú)論所有權(quán)如何。為了防止查看他人的報(bào)告,我們可以按所有權(quán)過(guò)濾查詢(xún),如下所示。

from rest_framework import viewsets, status, permissions
from rest_framework.response import Response
...
class ReportView(viewset.ModelViewSet):
queryset = Report.objects.all()
permission_classes = [permissions.IsAuthenticated]

def get_queryset(self):
if not request.user.is_superuser:
return Report.objects.filter(user=request.user)
return super().get_queryset()

代碼片段確保只有經(jīng)過(guò)身份驗(yàn)證的用戶(hù)的報(bào)告才可用。如果用戶(hù)是超級(jí)用戶(hù)(即管理員),則經(jīng)過(guò)身份驗(yàn)證的用戶(hù)可以查看所有報(bào)告。解決對(duì)象級(jí)授權(quán)的另一種方法是擴(kuò)展 Django(Django REST 框架)中可用的基本權(quán)限,如下所示。

 在permissions.py中,運(yùn)行以下命令:

from rest_framework import permissions

class IsOwnerOrAdmin(permissions.BasePermission):

def has_permission(self, request, view):
# This method only allows suppliers or admin to view this route.
return request.user.groups.filter(name="supplier") or request.user.is_superuser

def has_object_permission(self, request, view, obj):
# This method authorizes only the resource owner or admin from using object.
return obj.owner == request.user or request.user.is_superuser

在上面的代碼片段中,has_permission()方法負(fù)責(zé)授權(quán)用戶(hù)查看或調(diào)用特定資源或端點(diǎn)。has_object_permission ()方法負(fù)責(zé)檢查某個(gè)用戶(hù)是否可以訪問(wèn)給定資源中的特定對(duì)象。在上面的示例中,只有供應(yīng)商組中的用戶(hù)或管理員才能查看資源或路線。為了對(duì)資源中的特定對(duì)象執(zhí)行操作,用戶(hù)必須是對(duì)象所有者或平臺(tái)上的管理員。

要使用此功能,請(qǐng)?jiān)?strong>views.py中運(yùn)行以下命令:

from rest_framework import viewsets, status, permissions
from rest_framework.response import Response
from .permissions import IsOwnerOrAdmin
...
class ReportView(viewset.ModelViewSet):
queryset = Report.objects.all()
permission_classes = [permissions.IsAuthenticated, IsOwnerOrAdmin]
...

請(qǐng)注意,現(xiàn)在GET請(qǐng)求中的URL www.example.com/api/reports/仍將向供應(yīng)商組中經(jīng)過(guò)身份驗(yàn)證的用戶(hù)顯示所有報(bào)告。但是,如果經(jīng)過(guò)身份驗(yàn)證的用戶(hù)不是對(duì)象所有者,則禁止對(duì)特定資源對(duì)象本身執(zhí)行任何操作。為了確保它僅顯示擁有報(bào)告的經(jīng)過(guò)身份驗(yàn)證的用戶(hù),我們必須將其包含在查詢(xún)集中,如下所示。

from rest_framework import viewsets, status, permissions
from rest_framework.response import Response
from .permissions import IsOwnerOrAdmin
...
class ReportView(viewset.ModelViewSet):
queryset = Report.objects.all()
permission_classes = [permissions.IsAuthenticated, IsOwnerOrAdmin]
...

def get_queryset(self):
if not request.user.is_superuser:
return Report.objects.filter(user=request.user)
return super().get_queryset()

這些是一些已知的防止 Django API 中的對(duì)象級(jí)身份驗(yàn)證中斷的方法。

信任是靠努力贏得的

隨著攻擊者不斷發(fā)現(xiàn)每個(gè)系統(tǒng)的安全漏洞,我們必須通過(guò)驗(yàn)證數(shù)據(jù)來(lái)源來(lái)確保用戶(hù)數(shù)據(jù)受到保護(hù)。我們永遠(yuǎn)不應(yīng)該相信用戶(hù)提供的數(shù)據(jù)。為了避免因在企業(yè)中存儲(chǔ)危險(xiǎn)數(shù)據(jù)而導(dǎo)致災(zāi)難,我們必須審查數(shù)據(jù)。

我們?cè)谶@篇文章中了解到攻擊者如何嘗試通過(guò)將資源 ID 更改為從可預(yù)測(cè)的 API ID 結(jié)構(gòu)推斷出的新資源 ID 來(lái)獲取對(duì)資源對(duì)象的授權(quán)。在授權(quán)某些資源操作之前未測(cè)試經(jīng)過(guò)身份驗(yàn)證的用戶(hù)權(quán)限的端點(diǎn)可能會(huì)讓攻擊者獲得對(duì)資源的訪問(wèn)權(quán)限。

我們考慮了各種方法來(lái)對(duì)抗這些已知的對(duì)象級(jí)授權(quán)漏洞,即鼓勵(lì)使用可生成長(zhǎng)串低沖突 ID 的 UUID。我們還演示了通過(guò)將所有者數(shù)據(jù)附加到查詢(xún)期間要訪問(wèn)的資源對(duì)象來(lái)防止泄露其他用戶(hù)數(shù)據(jù)的方法。

文章來(lái)源:Django Broken Object-Level Authorization Guide: Examples and Prevention

上一篇:

.NET XSS:示例與預(yù)防

下一篇:

探討路徑遍歷攻擊的概念與預(yù)防方法
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊(cè)

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門(mén)場(chǎng)景實(shí)測(cè),選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)