欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Django中和時(shí)區(qū)相關(guān)的安全問(wèn)題詳解

 更新時(shí)間:2020年10月12日 09:26:13   作者:PHITHON  
這篇文章主要給大家介紹了關(guān)于Django中和時(shí)區(qū)相關(guān)的安全問(wèn)題的相關(guān)資料,需要的朋友可以參考下

在開(kāi)發(fā)國(guó)際化網(wǎng)站的時(shí)候,難免會(huì)與時(shí)區(qū)打交道,通用CMS更是如此,畢竟其潛在用戶可能是來(lái)自于全球各地的。Django在時(shí)區(qū)這個(gè)問(wèn)題上下了不少功夫,但是很多資深的開(kāi)發(fā)者都有可能尚未完全屢清楚Django中各種時(shí)間的實(shí)際意義和使用方法,導(dǎo)致寫出錯(cuò)誤的代碼;作為安全研究人員,時(shí)區(qū)問(wèn)題也可能和一些安全問(wèn)題掛鉤,比如優(yōu)惠券的過(guò)期時(shí)間、訂單的下單與取消時(shí)間等,如果沒(méi)有考慮時(shí)區(qū)問(wèn)題,有可能將導(dǎo)致一些邏輯漏洞。

本文就從多個(gè)常用模塊開(kāi)始,了解一下Django中的時(shí)區(qū)究竟是怎么回事,以及在時(shí)間的比較中可能出現(xiàn)的一些邏輯錯(cuò)誤。

從“兩種時(shí)間”說(shuō)起

我們都知道,在Python中表示“時(shí)間”的對(duì)象是datetime.datetime

其實(shí)在Python中,這個(gè)對(duì)象被分成了兩個(gè)類型:

  • aware datetime
  • naive datetime

他們的區(qū)別是:如果datetime對(duì)象的tzinfo屬性有設(shè)置時(shí)區(qū)值,則這個(gè)對(duì)象是一個(gè)aware datime;否則它是一個(gè)naive datetime。

舉個(gè)例子,我們平時(shí)在編寫Python腳本的時(shí)候,使用下面這行代碼獲取當(dāng)前時(shí)間:

from datetime import datetime
t = datetime.now()

此時(shí),t是一個(gè)naive datetime,因?yàn)槲覀儧](méi)有給他設(shè)置時(shí)區(qū):

image-20201011010557126.png

naive的中文意思大家應(yīng)該都很熟悉,這里的大概意思就是“simple”,這是一個(gè)很簡(jiǎn)單、原始的時(shí)間對(duì)象。實(shí)際上就是指,計(jì)算機(jī)不知道這個(gè)時(shí)間,他的時(shí)區(qū)究竟是什么,它可能代表著北京時(shí)間,也可能是UTC時(shí)間,因?yàn)槲覀儧](méi)有指定時(shí)區(qū),我們無(wú)法“假設(shè)”其是計(jì)算機(jī)系統(tǒng)所在的時(shí)區(qū),也無(wú)法“假設(shè)”其是UTC時(shí)區(qū)。也就是說(shuō),計(jì)算機(jī)拿到了一個(gè)naive datetime,是無(wú)法準(zhǔn)確地定位到某一個(gè)時(shí)間點(diǎn)的,也無(wú)法直接轉(zhuǎn)換成一個(gè)unix時(shí)間戳。

那么相對(duì)的,aware datetime就是計(jì)算機(jī)能準(zhǔn)確知道其時(shí)區(qū)的時(shí)間對(duì)象,他是一個(gè)準(zhǔn)確的時(shí)間點(diǎn),就落在時(shí)間軸上的某個(gè)地方,不管從哪個(gè)時(shí)區(qū)看,這個(gè)點(diǎn)都是絕對(duì)固定的。所以,我們可以將一個(gè)aware datetime轉(zhuǎn)換成unix時(shí)間戳。

有的同學(xué)可能比較好奇,你說(shuō)naive datetime無(wú)法轉(zhuǎn)換成時(shí)間戳,那么為什么這個(gè)對(duì)象有一個(gè)timestamp()方法呢:

image-20201011012655581.png

原因我們查文檔可以得出結(jié)論,如果對(duì)象是naive datetime,則會(huì)以當(dāng)前系統(tǒng)本地時(shí)區(qū)為準(zhǔn)。

Django的時(shí)區(qū)配置

回到Django。由于Django是一個(gè)國(guó)際化框架,時(shí)區(qū)相關(guān)處理自然是其必不可少的組成部分。Django的配置項(xiàng)中,有下面兩個(gè)選項(xiàng)與時(shí)區(qū)相關(guān):

  • USE_TZ
  • TIME_ZONE

USE_TZ用來(lái)指定整個(gè)項(xiàng)目是否使用時(shí)區(qū),TIME_ZONE是默認(rèn)時(shí)區(qū)的值。

如果USE_TZ的值設(shè)置為False,那么Django項(xiàng)目中所有時(shí)間都使用naive datetime(除非有明確指定時(shí)區(qū)的情況)。也就是說(shuō),網(wǎng)站內(nèi)存儲(chǔ)和使用的時(shí)間全部是TIME_ZONE的值所指定的時(shí)區(qū)。

這樣做有一些弊端:

  • 數(shù)據(jù)庫(kù)中保存的是naive datetime,導(dǎo)致在跨區(qū)域遷移數(shù)據(jù)的時(shí)候,可能無(wú)法準(zhǔn)確定位到某個(gè)時(shí)間點(diǎn)
  • 國(guó)際化企業(yè)可能面向不同國(guó)家有不同的網(wǎng)站,但后臺(tái)數(shù)據(jù)庫(kù)相同,此時(shí)究竟使用哪個(gè)時(shí)區(qū)保存和展示時(shí)間,將引起混亂
  • 即使是同一個(gè)網(wǎng)站的用戶,他們可能來(lái)自于全球各地,查看到的時(shí)間卻是統(tǒng)一的服務(wù)器時(shí)間,對(duì)于高交互式的應(yīng)用十分不友好
  • 即使網(wǎng)站面向的用戶僅來(lái)自于某一個(gè)地區(qū),也會(huì)涉及到“夏時(shí)令”(Daylight Saving Time)相關(guān)的問(wèn)題,每年可能將會(huì)導(dǎo)致兩次時(shí)間誤差

默認(rèn)情況下,用django-admin生成的項(xiàng)目,其設(shè)置中USE_TZ等于True,這也是Django官方建議的配置。此時(shí),在網(wǎng)站內(nèi)部存儲(chǔ)與使用的是UTC時(shí)間,而與用戶交互時(shí)使用TIME_ZONE或手工的時(shí)區(qū)。

我們后文中也以Django的默認(rèn)配置USE_TZ=True為前提條件,否則也沒(méi)有討論的必要了。

Django的時(shí)間函數(shù)

Django的包django.utils.timezone中有下面幾個(gè)常用的時(shí)間相關(guān)函數(shù):

  • now(),返回當(dāng)前的UTC時(shí)間
  • localtime(),返回當(dāng)前的本地時(shí)間(默認(rèn)是TIME_ZONE配置指定的時(shí)區(qū)時(shí)間)
  • is_aware(),傳入的時(shí)間是否是aware datetime
  • is_naive(),傳入的時(shí)間是否是naive datetime
  • make_aware(),將naive時(shí)間轉(zhuǎn)換成aware時(shí)間
  • make_naive(),將aware時(shí)間轉(zhuǎn)換成naive時(shí)間

因?yàn)殚_(kāi)啟了USE_TZ,Django內(nèi)部操作時(shí)間時(shí)都應(yīng)該使用aware時(shí)間,否則會(huì)出現(xiàn)異常。所以,我們?cè)讷@取當(dāng)前時(shí)間的時(shí)候,一定要使用Django自帶的now()localtime()函數(shù),而不能使用Python的datetime.datetime.now()函數(shù)。

數(shù)據(jù)庫(kù)存儲(chǔ)的時(shí)間

我們?cè)谑褂肙RM的DatetimeField時(shí),常常會(huì)有這樣的疑慮:我們究竟應(yīng)該給DatetimeField傳入哪個(gè)時(shí)區(qū)的時(shí)間呢?

可以做個(gè)試驗(yàn),編寫下面這個(gè)model:

class Archive(models.Model):
  title = models.CharField('title', max_length=256)

  now_time = models.DateTimeField(default=timezone.now)
  local_time = models.DateTimeField(default=timezone.localtime)

這個(gè)model有三個(gè)屬性,title是他的名字,now_time和local_time是兩個(gè)時(shí)間,他們的默認(rèn)值分別是timezone.now和timezone.localtime。

也就是說(shuō),默認(rèn)情況下,now_time字段傳入的是UTC時(shí)區(qū)的當(dāng)前時(shí)間,local_time字段傳入的是本地時(shí)區(qū)的當(dāng)前時(shí)間,我這里是Asia/Shanghai。

然后,我們創(chuàng)建一個(gè)Archive對(duì)象:

image-20201011024130489.png

可以發(fā)現(xiàn),不管我們使用a.now_time還是a.local_time,讀取到的datetime對(duì)象的tzinfo都是UTC。

這也印證了Django文檔中說(shuō)到的,不管傳入的時(shí)間對(duì)象時(shí)區(qū)是什么,其內(nèi)部存儲(chǔ)的時(shí)間均為UTC時(shí)區(qū)。但是,值得注意的是,如果我們傳入了一個(gè)不帶時(shí)區(qū)的naive datetime,將會(huì)出現(xiàn)一個(gè)警告,并使用默認(rèn)時(shí)區(qū)填充其tzinfo:

image-20201011024854993.png

模板中展示的時(shí)間

對(duì)于網(wǎng)站的用戶來(lái)說(shuō),他們想看到的時(shí)間顯然不是UTC時(shí)間,而是某一個(gè)具體時(shí)區(qū)的時(shí)間。比如,我的網(wǎng)站幾乎全部是中國(guó)用戶,那么展示時(shí)使用的時(shí)區(qū)應(yīng)該是Asia/Shanghai。

這一部分的轉(zhuǎn)換,Django放在的模板引擎中。

Django在渲染模板變量時(shí),將會(huì)遇到兩種與時(shí)間有關(guān)的情況:

<p>origin value: {{ object.now_time }}</p>
<p>date filter: {{ object.now_time | date:'Y-m-d H:i:s' }}</p>

前者是直接將時(shí)間渲染到頁(yè)面中,后者是通過(guò)date這樣的模板filter處理后渲染在頁(yè)面中。這兩種情況在內(nèi)部處理方式略有不同此處不細(xì)表,總體而言,任意模板中變量的渲染,都會(huì)被轉(zhuǎn)換時(shí)區(qū)。

那么,脫離模板引擎,我們會(huì)得到怎樣的結(jié)果呢?

在流行的前后端分離架構(gòu)中,后端服務(wù)器通常只提供JSON格式的接口給前端,那么,我們編寫下面這樣一個(gè)view,看看返回值是什么:

from django.shortcuts import get_object_or_404
from django.http.response import JsonResponse
from django.utils import timezone

from . import models


def json(request):
  object = get_object_or_404(models.Archive, pk=1)
  data = dict(
    id=object.pk,
    now_time=object.now_time,
    local_time=timezone.localtime(object.local_time)
  )
  return JsonResponse(data=data)

返回對(duì)象的now_time,我直接將object.now_time返回;返回對(duì)象的local_time,我將數(shù)據(jù)庫(kù)值轉(zhuǎn)換成本地時(shí)間timezone.localtime(object.local_time)返回。

我前文說(shuō)過(guò),這兩個(gè)值在數(shù)據(jù)庫(kù)中的值是完全相等的,不過(guò)在json返回中,now_time是UTC時(shí)間,而local_time是北京時(shí)間:

image-20201011031507193.png

也就是說(shuō),在前后端分離的網(wǎng)站中,如果直接使用Model的字段,那么前端需要負(fù)責(zé)進(jìn)行時(shí)區(qū)的轉(zhuǎn)換,否則將會(huì)出現(xiàn)時(shí)間的偏差。

時(shí)間的校驗(yàn)和比較

在一些業(yè)務(wù)場(chǎng)景下,我們可能會(huì)涉及到時(shí)間的校驗(yàn)和比較,如:

  • 付費(fèi)服務(wù)、商品、用戶的有效期檢查
  • 活動(dòng)的開(kāi)始與結(jié)束時(shí)間檢查
  • 訂單、商品的收貨、取消時(shí)間檢查

我們就以付費(fèi)用戶為例:用戶購(gòu)買了30天的VIP會(huì)員,我們需要給用戶表中設(shè)置一個(gè)過(guò)期時(shí)間,比如下面這個(gè)model。

from django.db import models
from django.utils import timezone

class Account(models.Model):
  username = models.CharField(max_length=256)
  password = models.CharField(max_length=64)

  created_time = models.DateTimeField(default=timezone.now)
  expired_time = models.DateTimeField()

如果某個(gè)用戶某一個(gè)時(shí)刻對(duì)網(wǎng)站進(jìn)行訪問(wèn),我們?nèi)绾闻袛嗨欠窬哂蠽IP權(quán)限呢?

通常情況下我們有兩種常見(jiàn)的判斷方法。一是,用戶訪問(wèn)時(shí),直接從model中取出這個(gè)對(duì)象,然后和now()進(jìn)行比較:

image-20201011033452219.png

這種情況下,當(dāng)前時(shí)間不管是now()還是localtime()都不影響比較的結(jié)果,因?yàn)閮蓚€(gè)datetime對(duì)象在比較時(shí)會(huì)考慮時(shí)差。

另一種情況是,通過(guò)ORM的queryset進(jìn)行比較,等于在數(shù)據(jù)庫(kù)層面進(jìn)行操作:

if models.Account.objects.filter(expired_time__gt=timezone.now()).exists():
  # doing sth

image-20201011034352025.png

Django也幫我們考慮過(guò)這種情況,即使此時(shí)我們使用本地時(shí)間timezone.localtime()進(jìn)行查詢,系統(tǒng)也會(huì)將其轉(zhuǎn)換成UTC時(shí)間傳入SQL語(yǔ)句:

image-20201011034633005.png

但是,如果我們使用到了和日期、時(shí)間有關(guān)的lookups,將產(chǎn)生相反的結(jié)果。

怎么理解這個(gè)問(wèn)題呢,我們還是來(lái)舉個(gè)例子。比如,網(wǎng)站以用戶注冊(cè)當(dāng)天的日子作為“會(huì)員日”(比如1月2日注冊(cè)的會(huì)員,以后每月的2日都是他的會(huì)員日),會(huì)員日這一天會(huì)給這個(gè)用戶贈(zèng)送優(yōu)惠券。

那么,發(fā)送優(yōu)惠券時(shí),我們?nèi)绾魏Y選網(wǎng)站內(nèi)會(huì)員日是今日的所有用戶?

下面這個(gè)filter是否正確?

models.Account.objects.filter(created_time__day=timezone.now().day).all()

答案是否定的,我們應(yīng)該使用timezone.localtime()表示今天,而非timezone.now()

models.Account.objects.filter(created_time__day=timezone.localtime().day).all()

這是為什么呢?你不是說(shuō)數(shù)據(jù)庫(kù)中存儲(chǔ)的都是UTC時(shí)間嗎,為何會(huì)使用到timezone.localtime()?

原因是,Django在使用日期、時(shí)間有關(guān)的lookups時(shí),會(huì)在數(shù)據(jù)庫(kù)層面對(duì)時(shí)間進(jìn)行時(shí)區(qū)的轉(zhuǎn)換再進(jìn)行比較,所以我們需要使用本地時(shí)間而不是UTC時(shí)間。

可以看看原始的SQL語(yǔ)句:

image-20201011041652087.png

可見(jiàn),SQL語(yǔ)句中使用了django_datetime_extract('day', "sample_account"."created_time", 'Asia/Shanghai', 'UTC')將UTC時(shí)間轉(zhuǎn)換成了北京時(shí)間,因此后面比較的時(shí)候,也應(yīng)該使用北京時(shí)間。

這一點(diǎn)需要格外注意。時(shí)間比較的不謹(jǐn)慎,說(shuō)小點(diǎn)是一個(gè)Bug,說(shuō)大點(diǎn)就是漏洞,畢竟很多涉及到時(shí)間比較的情景,都是非常需要嚴(yán)謹(jǐn)?shù)摹?/p>

所以,我們總結(jié)一下:

  • 任何比較都使用aware時(shí)間,不能使用naive時(shí)間
  • 時(shí)間屬性直接比較時(shí),使用任何aware時(shí)間均可(會(huì)被自動(dòng)轉(zhuǎn)換成UTC)
  • queryset查詢,不涉及__day、__date、__year等時(shí)間lookups時(shí),使用任何aware時(shí)間均可(會(huì)被自動(dòng)轉(zhuǎn)換成UTC)
  • queryset查詢,涉及到時(shí)間lookups時(shí),使用本地時(shí)間

到此這篇關(guān)于Django中和時(shí)區(qū)相關(guān)的安全問(wèn)題詳解的文章就介紹到這了,更多相關(guān)Django時(shí)區(qū)安全問(wèn)題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用Python代碼實(shí)現(xiàn)對(duì)Excel單元格的鎖定

    使用Python代碼實(shí)現(xiàn)對(duì)Excel單元格的鎖定

    在Excel表格中,我們可以通過(guò)鎖定特定的單元格或區(qū)域,防止對(duì)單元格內(nèi)容進(jìn)行隨意修改,確保關(guān)鍵數(shù)據(jù)、公式或格式不被誤改,本文將介紹如何使用Python代碼來(lái)實(shí)現(xiàn)對(duì)Excel單元格的鎖定,實(shí)現(xiàn)批量操作以及自動(dòng)化,需要的朋友可以參考下
    2024-06-06
  • python—sys模塊之獲取參數(shù)的操作

    python—sys模塊之獲取參數(shù)的操作

    這篇文章主要介紹了python—sys模塊之獲取參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-05-05
  • 使用PyQt4 設(shè)置TextEdit背景的方法

    使用PyQt4 設(shè)置TextEdit背景的方法

    今天小編就為大家分享一篇使用PyQt4 設(shè)置TextEdit背景的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-06-06
  • Python迭代器和生成器定義與用法示例

    Python迭代器和生成器定義與用法示例

    這篇文章主要介紹了Python迭代器和生成器定義與用法,結(jié)合實(shí)例形式詳細(xì)分析了Python迭代器和生成器的概念、原理、定義、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2018-02-02
  • Python中defaultdict與lambda表達(dá)式用法實(shí)例小結(jié)

    Python中defaultdict與lambda表達(dá)式用法實(shí)例小結(jié)

    這篇文章主要介紹了Python中defaultdict與lambda表達(dá)式用法,結(jié)合實(shí)例形式分析了Python中defaultdict與lambda表達(dá)式的功能、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2018-04-04
  • Python實(shí)現(xiàn)文件壓縮和解壓的示例代碼

    Python實(shí)現(xiàn)文件壓縮和解壓的示例代碼

    這篇文章主要介紹了Python實(shí)現(xiàn)文件壓縮和解壓的方法,幫助大家更好的理解和學(xué)習(xí)python,感興趣的朋友可以了解下
    2020-08-08
  • python selenium自動(dòng)化測(cè)試框架搭建的方法步驟

    python selenium自動(dòng)化測(cè)試框架搭建的方法步驟

    這篇文章主要介紹了python selenium自動(dòng)化測(cè)試框架搭建的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • python樹(shù)的雙親存儲(chǔ)結(jié)構(gòu)的實(shí)現(xiàn)示例

    python樹(shù)的雙親存儲(chǔ)結(jié)構(gòu)的實(shí)現(xiàn)示例

    本文主要介紹了python樹(shù)的雙親存儲(chǔ)結(jié)構(gòu),這種存儲(chǔ)結(jié)構(gòu)是一種順序存儲(chǔ)結(jié)構(gòu),采用元素形如“[結(jié)點(diǎn)值,雙親結(jié)點(diǎn)索引]”的列表表示,感興趣的可以了解一下
    2023-11-11
  • python3如何使用Requests測(cè)試帶簽名的接口

    python3如何使用Requests測(cè)試帶簽名的接口

    這篇文章主要介紹了python3如何使用Requests測(cè)試帶簽名的接口,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • python人工智能算法之線性回歸實(shí)例

    python人工智能算法之線性回歸實(shí)例

    這篇文章主要為大家介紹了python人工智能算法之線性回歸實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03

最新評(píng)論