■ 上海 陳峻
編者按: 筆者分享了如何在遵守OWASP Top 10的前提下,如何對單位PHP網(wǎng)站進行全方位安全開發(fā)實踐工作,筆者在幾個關(guān)鍵點方面進行了闡述。
經(jīng)歷了大半年的研發(fā)與測試,筆者單位網(wǎng)站的新版本在上個月正式上線運營。該版本采用Apache2.4+PHP 7.2+MySQL 5.7的應(yīng)用環(huán)境。本月,筆者和網(wǎng)站開發(fā)部門負(fù)責(zé)PHP部分的工程師們進行了有關(guān)DevSecOps方面的交流。他們分享了如何在遵守OWASP Top 10的前提下進行全方位安全開發(fā)實踐工作。
總的說來,他們是在分析了現(xiàn)有網(wǎng)站架構(gòu)特點、并審查了過往事故記錄的基礎(chǔ)上,逐步摸清了整體設(shè)計上存在的漏洞,以及當(dāng)前各類代碼程序之間的依賴關(guān)系。因此在新的網(wǎng)站構(gòu)建過程中,他們從代碼層面和架構(gòu)層面兩個維度進行了設(shè)計與開發(fā)。下面便是他們總結(jié)出的針對PHP安全加固與防護的各個關(guān)鍵點。
1.過濾并驗證數(shù)據(jù)
過去,他們對網(wǎng)站的傳入數(shù)據(jù)以及操作符類型,并未進行嚴(yán)格的檢查。因此,內(nèi)部審計人員曾建議通過使用strict_types的類型聲明,以避免出現(xiàn):浮點型參數(shù)被強制轉(zhuǎn)換為整形、再被判斷為布爾型的潛在攻擊邏輯。
如今,考慮到新版網(wǎng)站的內(nèi)容會豐富許多,而且經(jīng)常需要與訪客及用戶進行信息互動,因此他們需要將內(nèi)容進行轉(zhuǎn)義和預(yù)編譯,并生成HTML、CSS和 JavaScript等輸出形式。于是,在頁面的源代碼中,他們增設(shè)了各種HTTP的安全頭部,并且在服務(wù)器應(yīng)用上添加了內(nèi)容安全策略(CSP),以類似于“白名單”的形式限制訪客及用戶瀏覽器的各種潛在加載和執(zhí)行行為,進而避免網(wǎng)站受到CSRF和XSS攻擊。
同時,對于網(wǎng)站上的數(shù)據(jù),無論它們是來自應(yīng)用內(nèi)部的某個配置文件,還是源于服務(wù)器的環(huán)境變量,亦或是經(jīng)由GET或POST方法傳遞而來,都絕不盲目地信任與接收,而是采取相應(yīng)的程序過濾(如 filter_var)與工具驗證。在具體實現(xiàn)的過程中,借鑒了Symfony框架來分離各種事務(wù)與邏輯。特別針對PHP程序的開發(fā)部分,更是用到了Laravel Web開發(fā)框架。
2.使用參數(shù)化查詢
過去,訪客及用戶在網(wǎng)站上觸發(fā)定制化查詢時,前端頁面會自動生成相應(yīng)查詢語句,然后經(jīng)由業(yè)務(wù)層傳遞給后端的數(shù)據(jù)庫,后續(xù)的分析、編譯、優(yōu)化和執(zhí)行等工作則全部由數(shù)據(jù)庫來完成??梢?,該過程不但耗時較長,而且一旦出現(xiàn)略有不同的查詢參數(shù),數(shù)據(jù)庫則需要頻繁地執(zhí)行重復(fù)性解析,以滿足查詢需求。網(wǎng)站的整體響應(yīng)速度和系統(tǒng)性能也會在面對并發(fā)量大的循環(huán)查詢時受到一定影響。
同時,從安全的角度來看,由各種外部數(shù)據(jù)被動態(tài)且臨時性拼裝出的SQL字符串,對于后端數(shù)據(jù)的查詢、乃至插入與更新都是十分危險的,網(wǎng)絡(luò)攻擊者很容易構(gòu)成SQL注入攻擊。
針對上述兩點隱患,為了減少硬件資源占用率、提高軟件運行效率、并保證查詢的安全性,他們采用了MySQLi(不再是以前傳統(tǒng)的mysql_函數(shù))和pg_query_params擴展庫,同時也引入了預(yù)處理語句和相應(yīng)的存儲過程。當(dāng)然,對于其他項目而言,不同的數(shù)據(jù)庫可能會用到不同的擴展庫和對應(yīng)的PDO(PHP數(shù)據(jù)對象)。
3.限制訪問路徑
過去,他們直接通過編寫PHP腳本及接口的調(diào)用去讀取服務(wù)器的系統(tǒng)文件(包括諸如/etc/passwd之類的敏感文件),修改各種網(wǎng)絡(luò)連接方式,以及發(fā)送打印作業(yè)等任務(wù)。而攻擊者恰好可以利用此類后門,通過include()或fopen()方法來查找系統(tǒng)的文件路徑。
如今,他們通過配置和使用open_basedir,來限制程序只訪問指定目錄里的文件。也就是說,外部訪客或PHP進程完全無法訪問到指定目錄之外的任何信息。而且通過配置safe_moade_exec_dir,避免將PHP腳本與會話直接存放到可執(zhí)行目錄下。上升到理論層面,這實際上是對應(yīng)用程序采取了運行環(huán)境的隔離(類似于沙箱的概念)。另外,通過諸如chroot之類的配置,將部分容器進程及應(yīng)用服務(wù)限制為只允許操作其對應(yīng)的運行環(huán)境。
4.防范XXE
無論是哪一版的OWASP Top 10,其中有一項風(fēng)險便是安全配置的缺失。而在本次新網(wǎng)站的設(shè)計中,為了防止出現(xiàn)讀取任意文件、執(zhí)行系統(tǒng)命令、探測內(nèi)網(wǎng)端口等XML外部實體注入的攻擊,他們既通過啟用libxml_disable_entity_loader來禁用外部實體,又對訪客及用戶所提交的XML數(shù)據(jù)進行了關(guān)鍵字過濾。另外也對文件的上傳實施了相應(yīng)的過濾,甚至是限制。
5.配置SSL/TLS
過去,由于沒有用到https://的安全防護,訪客從網(wǎng)站的URL上便可直觀地獲取各類參數(shù)信息。
如今,他們不但在服務(wù)器上通過配置TLS的最新版本實現(xiàn)了流量的強加密,而且啟用了session.cookie_secure。同時,為保證訪問請求的合法性及網(wǎng)站數(shù)據(jù)的機密性,對于與MySQL數(shù)據(jù)庫、Apache服務(wù)器、以及遠(yuǎn)程調(diào)用等連接,都采用了TLS和公鑰加密等“加持”方式。
6.隱蔽特征信息
默 認(rèn) 情 況 下,PHP和Apache都會在HTTP包頭中帶有其相應(yīng)的版本信息,這樣對于那些諳熟軟件版本漏洞的攻擊者或工具來說,便有了可乘之機。此次,他們在新版網(wǎng)站中既對傳輸數(shù)據(jù)的包頭進行了信息隱藏,又在生產(chǎn)環(huán)境中避免了各種程序相關(guān)錯誤、警告或異常信息的出現(xiàn)或泄露。
7.做好斷舍離
既然理解了原有各類代碼程序之間的依賴關(guān)系,他們在開始添加新的代碼之前對已有程序進行了梳理和重構(gòu),重點“清洗”了生產(chǎn)環(huán)境中各種不再用到或從沒用到的類、接口、方法及調(diào)用庫。而對更新的代碼,則使用Composer進行了各種依賴項的檢測。同時,在測試環(huán)節(jié)中,分別運用SensioLabs DeprecationDetector來檢查靜態(tài)代碼;運用IsDeprecated來檢查PHP程序。
8.安全編碼
為了最小化在開發(fā)過程中所產(chǎn)生的代碼漏洞,盡量避免在程序中使用引用傳遞,而且使用持續(xù)集成(CI)工具phpsecurity-scanner,來對程序進行掃描和測試。一旦出現(xiàn)問題,工具將無法完成編譯。
另外,為了避免在緩存或本地配置文件中存儲SSH密鑰、訪問密碼、API令牌等敏感信息,還采用了PHP dotenv,來自動且動態(tài)地部署并加載各類環(huán)境變量。
1.非暴力,拒絕DDoS
DDoS攻擊曾給單位網(wǎng)站造成巨大破壞。當(dāng)時就算做了流量清洗,也是治標(biāo)不治本。因此,在本輪新架構(gòu)設(shè)計中針對暴力登錄之類的密集型連接攻擊,啟用了Fail2Ban。即通過監(jiān)控系統(tǒng)的各類日志(特別是預(yù)定義好的失敗登錄數(shù)),實時調(diào)用正則表達式來匹配和篩選日志中的錯誤信息,進而調(diào)用防火墻(iptables)來屏蔽由此類IP地址所發(fā)出的連接請求。
2.會話管理
會話(Session)如同訪客及用戶伸向網(wǎng)站的“鉤子”。它們不但會占用一定的系統(tǒng)資源,更可能會“鉤破”(會話劫持)系統(tǒng)。因此,PHP團隊成員進行了如下針對會話的管控與實踐:
(1)如前所述,使用SSL來安全地傳遞只包含會話ID的cookie。
(2)除了設(shè)定基本的會話過期時長(session.gc_maxlifetime),還在用戶更新密碼、或出現(xiàn)違規(guī)事件時,做到了如下四步:
及時中斷當(dāng)前會話;刪除含有會話信息的cookie;要求重新進行身份認(rèn)證;運用session_regenerate_id新產(chǎn)生會話。
(3)準(zhǔn)備專用的MySQL數(shù)據(jù)庫來對會話,特別是Session Handler,進行存儲、獲取、日志和交互。
(4)配置session.use_strict_mode,讓會話模塊僅接受由它自己創(chuàng)建的有效會話ID,而拒絕由用戶自己提供的會話ID。
(5)配置session.sid_length為48,通過會話 ID的長度來提高抗攻擊能力。
(6)配置session.hash_function,用高強度的哈希算法來保護會話 ID。
(7) 配 置 session.cookie_lifetime,保證訪客及用戶關(guān)閉瀏覽器時,會話ID cookie 能被立即刪除。
3.事無巨細(xì)
網(wǎng)絡(luò)安全法中規(guī)定了企業(yè)應(yīng)當(dāng)監(jiān)測、記錄網(wǎng)站的運行狀態(tài)、安全事件、用戶登錄等日志,并留存不少于六個月。因此,針對網(wǎng)站上線運行后可能出現(xiàn)的各種異常、以及訪客與用戶的非法輸入和違規(guī)使用行為,利用monolog之類的日志擴展庫對各種日志進行抓取和導(dǎo)出,以便后端的事件管理工具予以深入分析。
新版網(wǎng)站上線運行已一月有余,各方面狀態(tài)比較穩(wěn)定,并未出現(xiàn)顯著的安全事故。網(wǎng)站開發(fā)團隊也將上述針對PHP方面的安全加固與防護實踐以清單的形式分享了出來,希望能夠在整個技術(shù)部門形成正反饋,持續(xù)迭代、不斷完善,通過優(yōu)雅的代碼打造出真正意義上的“硬核”站點。