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