修復漏洞的最佳時機就是開發的時候。
在Django的安全框架中,CSRF TOKEN是一項至關重要的安全策略。但很多情況下,一些剛接觸Django的同學會發現自己好不容易寫出來的表單,在POST的時候報錯了,經過一番查找發現是CSRF TOKEN的問題,然後按照網上的方法三下五除二將settings.py中的CSRF TOKEN配置全部移除了,程式碼正常跑起來了。熟不知這種操作將極大的影響網站的安全性,且提高了後期修補漏洞的成本;而在開發階段消滅安全問題,是成本最低的時候。
因為官方文件已經詳細解釋了CSRF TOKEN的相關內容,所以在這裡就不再贅述具體用法。這裡推薦一種比較方便的用法,在開發的時候對開發人員的感知較小,不用特別去關心Token是否已經發送成功了。
在總的父範本頁面中加入{% csrf_token %},並且在<script>部分進行如下設定:</script>
這樣所有透過JQuery中的的AJAX發起的非GET|HEAD|OPTIONS|TRACE請求都會自動包含CSRF TOKEN(取得CSRF TOKEN的部分程式碼未列出),如果使用的是其他HTTP庫或Fetch,進行對應的設定即可。
某些情況下,我們的Web服務還會對外提供一些API服務,供其他系統調用,對於這些API接口,CSRF是一定要關閉的,不然根本無法正常使用。我們也應該採取其他安全措施來防止API介面被濫用。除了正常的傳遞參數外,我們應該保證這些參數不會被中間人篡改,並且只允許有權限的人調用對應的接口,為此我們需要額外引入幾個傳遞的參數:timestamp, sign, access_key, access_token。
這對參數包括access_key和access_token。 access_key相當於標識呼叫方是誰,access_token則是相當於呼叫方的秘鑰,access_token內容不應該能被簡單的預測到,而access_key可以為了方便記憶選擇較為簡單的字串。只有當兩個參數匹配後,才認為本次API的呼叫是合法的。
根據業務需求選擇時間戳記的精確度,可以選擇秒級或毫秒。新增時間戳記主要是為了防止本次呼叫被重播攻擊,服務端應當校驗客戶端傳遞的時間戳記是否在一個時間範圍內,超時的時間戳都應被認為是非法的請求。為了確保參數的真實性,需要引入另一個參數sign,因為即使時間戳被重播,仍存在篡改的風險。
sign是所有參數的簽章值,是所有參數值參與hash計算產生的,參數每變動一點,sign都需要重新生成,藉此保證參數的真實性。常用的一套演算法是:根據參數key的字母序,將參數value進行排序,並且使用特定的分隔符號連接起所有的參數,然後進行hash計算,並將sign參數一同傳遞給服務端。舉個例子,現有參數ak=2222&at=1111&timestamp=3333&key1=aaa,依字母序排序完成後為22221111aaa3333,加入分隔符號(|)後為2222(|)1111(|)aaa(|)3333,然後將這個字串計算sha1,產生sign值。用Python程式碼寫的話比較簡單:
除了上面兩個比較重要的點之外,還有一些地方需要額外關註一下。
在生產環境關閉DEBUG模式;
不要將settings.py加入到版本管理中,並且保護好SECRET_KEY;
#設定好ALLOWED_HOSTS;
盡可能的使用Django提供的ORM,避免透過.raw()等方法直接執行SQL語句,如果無法避免,一定要將參數正確轉義以避免出現SQL注入漏洞;
#盡可能的停用Django Admin,如果一定需要使用,請修改預設的Admin URL;
從git上拉下程式碼,使用pip安裝專案依賴,透過manage.py運行服務,這一切看起來很美好,可是你真的打算在生產環境中這樣做嗎?
一般情況下,我們的伺服器上只會有一個Python環境,部署的時候一般都是透過pip來安裝專案所需要的依賴,這些套件都會被安裝到全域的site-packages目錄中。
多個專案部署時,採用這種安裝依賴的方式很容易導致依賴衝突發生。舉個簡單的例子,我們現在有Project-A和Project-B兩個項目,A和B都依賴第三方套件third-A的不同版本,當我們透過pip install -r requirements-a.txt的時候,依賴third-A被安裝到了全域的Python環境中,當我們再次安裝pip install -r requirements-b.txt的時候,也會再次安裝third-A,這個時候,如果兩個專案依賴的版本不一致,譬如A專案需要1.0版本,而B專案需要2.0版本,就會產生依賴衝突,進而導致安裝依賴失敗。
那麼要如何解決這個問題呢?我們很容易想到的就是,如果我們有多個互不相干的隔離環境,每個專案部署在一個獨立的環境中,那麼這個問題就迎刃而解了。 virtualenv正是為了解決這個問題而誕生的,它可以為每個專案創造一個單獨的運作環境,從而避免依賴衝突的問題。
前面我們知道如何創建隔離環境並且在不同的環境中部署不同的項目,但是這裡有個問題,所有環境使用的Python版本是一樣的。如果剛好你需要部署多個不同版本的Python項目,例如Python2.7(我知道這個版本即將不維護了,但是我在這裡舉個例子)、Python3.6和Jython項目,一個一個安裝就顯得有些複雜,甚至編譯安裝的時候一不小心少了某個編譯參數而需要重新編譯,都在某種程度上加大了部署工作量。
我們可以透過使用pyenv來管理多個Python版本的問題,進一步透過pyenv的外掛程式pyenv-virtualenv來管理多Python版本、多虛擬環境的問題。
當我們解決了各種環境的問題後,是時候來考慮如何將專案跑起來了,如果你想的是python manage.py runserver 0.0.0.0 :80,那就有點太過於簡單了。
Django中已經內建了一個簡單的WSGI實作來供我們透過上述方式啟動Web服務,如果你只是想調試或只提供服務給幾個人用的小程序,那也不失為一種可選擇的方案,雖然這種方案看起來並不是那麼優雅。
如果你真的想將應用程式部署到實際的生產環境中,那麼你還需要一個高效能的WSGI Server,而不是django提供的簡單的WSGI Server。 Gunicorn和uWSGI兩種都是比較主流的WSGI Server,根據實際部署環境,從中選擇一個就好。
不過我個人比較偏向Gunicorn,雖然在眾多的性能測試中uWSGI都佔了上風,選擇Gunicorn的理由是它與uWSGI相比十分簡單,沒有非常複雜的極少用到的功能,而uWSGI中的一些功能已經逐步被Nginx所支持,且Gunicorn配置起來也較為簡單方便。另外要注意的是,如果你想在Windows上部署,可能需要使用Apache mod_wsgi。
當我們的WSGI Server啟動就緒後,就要考慮一下反向代理的問題了,之所以前面再擋一層Nginx進行反向代理,有以下幾點原因:
需要Nginx來處理靜態資源。如果你將Django的DEBUG模式設定為False,你會發現很多CSS以及JS等靜態資源載入不到了,這是因為Django並不會主動去處理這些請求,這些都需要Nginx來幫忙處理;
透過Nginx來進行多個backend的負載平衡。如果你的服務部署在多台伺服器上,或進行了一主一備的部署,這些都可以透過在Nginx上進行簡單的設定來實現;
直接將uWSGI或Gunicorn暴露出來有一定的安全隱患,使用Nginx處理HTTP的問題會更加方便;
除此之外,還有一些理由就不在此列舉了,還是上面的那句話,如果你的服務很簡單,只有幾個人訪問,是不需要做這麼複雜的設定的。
截至目前,我們已經成功部署了服務,並且可以正常開始提供服務了。但是我們少考慮了一點,如果我們的Django不幸因為一些未知的原因退出了,那麼我們的Web服務就會變成502了。為了確保服務的穩定性,我們需要對Django程序進行守護,當其出現未知問題而導致異常退出的時候,就需要自動將所需的程序拉起來。
使用supervisor作為監控工具可以確保Django進程的穩定存活。要注意的是要小心避免Supervisord遠端指令執行漏洞,以免發生更嚴重的事故。
通常來講,如果想啟動後台服務的話,celery是一個萬能的選擇,但是很多時候我們不想引入這麼重的依賴,就需要自己想辦法來啟動後台服務了。
一個簡單的方法就是做成manage.py的command,透過./manage.py runcommand的方式來啟動我們的後台服務,並且透過編寫shell腳本控制服務的啟動與停止,或透過supervisor進行管理。
如果你想讓後台程序隨著Web服務同時啟動和停止,那麼放到wsgi.py中是個不錯的選擇,在wsgi.py中初始化相關的後台服務,並且啟動。但是這樣的做法不夠靈活,當需要單獨更新Web服務或後台服務時,需要將二者全部重啟,而採用第一種方式的情況下可以單獨更新其中的某個服務。
以上是Django怎麼部署的詳細內容。更多資訊請關注PHP中文網其他相關文章!