如何產出規範、安全、高質量的代碼?

對於一個軟件開發團隊,可以通過哪些代碼質量指標和掃描方法讓團隊產出規範、安全、高質量的代碼?讓開發團隊運行的安全、透明、可靠?本文總結了其中一些實踐和工具,包含常見代碼質量掃描工具、代碼質量指標、第三方依賴管理、安全運維等幾個方面,主要適用於 Java/JavaScript 技術棧的 web 項目,希望對於想要規範化自己的項目的 Tech Lead 有所幫助。

對於一個軟件開發團隊,可以通過哪些代碼質量指標和掃描方法讓團隊產出規範、安全、高質量的代碼?讓開發團隊運行的安全、透明、可靠?

本文總結了其中一些實踐和工具,包含常見代碼質量掃描工具、代碼質量指標、第三方依賴管理、安全運維等幾個方面,主要適用於 Java/JavaScript 技術棧的 web 項目,希望對於想要規範化自己的項目的 Tech Lead 有所幫助。

代碼掃描和常見質量指標

“禍患常積於忽微”,往往一些奇怪的 bug 都是一些不規範的小問題造成的。德國飛機渦輪機的發明者帕布斯·海恩提出的一個在航空界關於飛行安全的法則,法則指出: 每一起嚴重事故的背後,必然有 29 次輕微事故和 300 起未遂先兆以及 1000 起事故隱患。應用於軟件開發中,如果項目中代碼混亂不堪,必然會在某個時候最終爆發大量的問題。

如何產出規範、安全、高質量的代碼?

這裡整理了一些常見的掃描工具和代碼質量指標,可以在搭建項目基礎設施時引入,用於自動化的檢查代碼中潛在的問題,達到控制代碼產出質量的目的。

掃描工具

checkstyle

checkstyle 是常用於 java 項目的掃描工具,檢查源代碼是否與代碼規範相符,檢查項目主要包括:Javadoc 註釋、imports、過長的類和方法、空格、重複文件、圈複雜度等,默認使用 sun 的代碼規則,也可以配置自定義的代碼規則,例如阿里就發佈了相應的檢查規則。

findbugs

通過 Bug Patterns 的概念,尋找代碼中可能出現的 bug,檢查項目主要包括:不良編程習慣導致的問題、性能問題、安全問題、線程問題等。例如,應使用 equals 判斷相等,而不是 “ =” 操作符、流需要關閉、線程資源需要釋放等問題。findbugs 的模式庫對編程經驗也有較好的提升作用。還可以導入和編寫自己的 Bug Patterns 完善檢查機制。

simian

simian 是一個用於檢查重複和相似代碼的工具,它的重複檢查類似於論文查重,會提示一定的相似度。可以單獨運行,也可以作為 checkstyle 插件來使用,相對來來說比較小眾。

pmd

pmd 是一款跨語言的通用靜態掃描工具,具備一部分 checkstyle、findbugs 的功能,不再贅述。

ESlint/TSlint

前端界的 checkstyle , TSlint 設計用來做 TypeScript 類型檢查,ESlint 作為代碼風格檢查工具。不過現在 ESlint 也提供了TypeScript 類型檢查功能,基本上 ESlint 能整合這兩個功能。由於性能問題, TypeScript 也採用了 ESLint 作為 TSlint替代的檢查工具。

SonarQube

SonarQube 是一款用於代碼質量管理的開源工具,它主要用於管理源代碼的質量。 SonarQube 和上面的工具不太一樣,SonarQube 設計目的是提供一個平臺,通過插件的方式提供對各個語言進行支持,也可以和 checkstyle、pmd、simian 等工具進行集成。SonarQube 一般需要單獨部署成一個服務,提供數據庫,可以記錄掃描結果等信息。

npm audit

npm audit 是 npm 6 之後的版本 自帶的一個前端安全掃描工具,可以掃描 npm 依賴中的潛在的漏洞威脅。這些引入的漏洞可能威脅用戶開發的機,另外也可能被帶入 bundle 文件發佈到線上,帶來安全問題。目前 npm audit 會在 npm install 完成後自動執行,需要留意安全威脅報告。

Fortify SCA

Fortify SCA(Source Code Analyzer) 是一款非常優秀的代碼安全掃描工具,用於分析代碼中潛在的安全問題。通過調用語言的編譯器或者解釋器把代碼(Java、C、C++等源代碼)轉換成一種中間媒體文件 NST(Normal Syntax Trcc),然後通過模式匹配相關的方式抓取存在於漏洞庫中的漏洞。例如,上傳的文件沒有做檢查等 XSS 攻擊。

OWASP Dependency-Track

開放式 Web 應用程序安全項目(OWASP)是一個非營利組織,提供了很多安全標準、數據庫、社區和培訓。其中一個工具就是 OWASP Dependency-Track,可以對第三方依賴包中的知名漏洞進行檢查,掃描結果受到漏洞數據庫的更新影響。

archunit 架構規範檢查

前面的檢查是代碼層面,archunit 可以用於代碼架構檢查,可以定義規則檢查每個包中的實現是否符合規範。例如,controller 包中的類不能實現 service 的接口,repository 下的類必須實現 Repository 接口。通過 archunit 可以減少 codereview 的工作量,避免項目的結構被破壞。

如何產出規範、安全、高質量的代碼?

統計工具

sloccount、sourcemointor 這兩個工具可以用於統計代碼數量,包括行數、文件數、註釋等。除了在項目中掃描 bug 之外,配置代碼統計工具可以對項目有一個整體的認知。
其他的掃描工具還很多,例如 coverity、codemars、binscope、synk、appscan、retire.js 等工具,不再一一列舉。

最佳搭配

這幾款工具之間的功能有所重疊,在實際工作中,我們可以根據上面推薦的關注的點,重點清除這些問題。這些掃描工具全部用上除了會帶來團隊壓力和維護成本之外,代碼質量不會隨著引入的插件增多。除開有質量團隊的大廠提供這些掃描平臺外,敏捷團隊往往不會太大,團隊持續關注一個精簡的掃描組合更好。

Java 後端

  1. checkstyle Java 代碼風格守護,Java 項目至少應該配置一個默認的 checkstyle 規則。至少讓項目乾淨,沒有無用、重複的代碼,以及超大的類和方法。建議做到每次提交代碼前檢查。
  2. findbugs 常見不規範的代碼檢查,一些空指針、equals 檢查非常有用,而且 IDE 的插件也很好用。

前端

  1. eslint 守護 JavaScript 代碼風格,eslint 搭配一個 .editorconfig ,可以方便的讓編輯器保持同 eslint 一致的代碼風格。
  2. npm audit 項目中第三方包的威脅掃描,npm 自帶無需額外安裝,npm 6 以後自運行,需要關注並修復報出的安全問題。

安全

  1. fortify 掃描代碼中的漏洞,用它檢查出來的大部分安全問題都是注入攻擊、XSS 等攻擊,這些問題明顯可以在開發過程中避免。可以作為 Jenkins 插件配置,和單元測試作為同一階段運行。
  2. OWASP 插件 用來掃描第三方依賴漏洞,因為項目中的依賴不會像源代碼一樣頻繁變化,推薦使用 Jekins 插件,定期執行即可。

為什麼不用 SonarQube 呢,SonarQube 是一個非常優秀的代碼質量開放平臺,需要單獨的配置安裝,需要花費額外的時間維護,對於小團隊來說成本較高,如果有專門的質量團隊可以考慮維護一套。

常用代碼質量指標參考

  1. 編譯告警數,大部分程序員基本上忽略 warning,但是編譯器出現了告警是一種不好的體現,意味著軟件可能工作,但是存在不好的實踐,而這種不確定性,會帶來不確定的 bug 最終讓人一頭霧水。編譯過程中的告警,儘量消除掉,編譯告警的值推薦消除到 0。
  2. 平均函數代碼行數,過大的函數會導致閱讀困難,而且往往過大的函數職責不夠單一,一般將一個方法代碼行數控制到 30 - 50 行。
  3. 平均文件代碼行,和平均函數代碼行一樣,過長的文件一樣難以維護,一般一個文件10多個方法,因此文件的代碼行數一般控制到 300 - 500 行。
  4. 冗餘代碼,有時候我們代碼中可能存在未使用的方法、變量等代碼,這讓維護者一頭霧水,通常需要清零。
  5. 總文件重複率,出現重複文件的次數。除了編寫單元測試的情況下,業務代碼不應該出現重複代碼,推薦值為 0。
  6. 總代碼重複度,代碼的重複度檢查,限於掃描工具的識別模式,需要有一定的容忍度,推薦值在 5% - 10%
  7. 平均函數圈複雜度,圈複雜度用來衡量一個模塊判定結構的複雜程度。如果一個方法內部有大量的 if 語句嵌套,意味著這個方法的實現質量低下,且程序複雜度高不利於維護,推薦值小於 5%。
  8. 安全告警,如果配置了安全掃描工具,例如 Fortify,安全威脅應該被清零。
  9. 代碼缺陷,如果配置了缺陷掃描工具,例如 Findbugs,需要清零。
如何產出規範、安全、高質量的代碼?

常用代碼質量指標參考

第三方依賴規範化

軟件開發過程中,不可避免的需要引入第三方或者開源軟件包作為庫或者框架引入。“第三方” 其實不是一個軟件工程術語,現今在軟件行業裡面的理解是:第一方為自研的軟件,第二方為內部發布的軟件,第三方為從社區或者外部商業途徑引入的軟件包。

對於個人開發者而言,面向“搜索引擎”編程往往將來源不明的代碼片段和程序包引入到項目中。對於企業來說,考慮到的不僅僅是功能是否能實現,還要考慮引入時帶來的成本和問題,例如是否需要授權、開源協議是否合理、是否會帶來安全威脅。

企業對於第三方依賴的引入分為幾種情況:

  1. 作為開發工具引入,例如 gcc、Jenkins,基本沒有開源協議問題,但是需要注意開發機、CI 會有安全風險。Jenkins 曾出現過漏洞,CI 服務器被當做遠程礦機使用。
  2. 作為服務部署使用(SaaS
    ),部分開源協議會限制這種使用方式,第三方依賴的安全問題會威脅服務器。
  3. 通過軟件包再發布,大部分開源軟件對這種使用方式有較多要求,例如 GPL 開源協議具有傳染性,要求使用了 GPL 的項目也要開源。
  4. 拷貝源代碼引入項目,非常不推薦這種方式,儘量通過包管理的方式引入。

引入第三方依賴需要充分考慮,儘可能最小成本的引入。在一個 React 的前端項目中,有不熟悉的工程師,為了使用一個簡單的手風琴效果,引入了整套 bootstrap。不僅破壞了使用 React 的最佳實踐,而且讓輸出的 bundle 文件大小激增數倍,造成首屏加載的性能問題。

常見商業友好的開源協議

商業用戶常用的開源協議實際上只有6種左右,即 LGPL、Mozilla、GPL、BSD、MIT、Apache,另外還有極其寬鬆的 The Unlicense,但採用的開源軟件不多。

GitHub 提供了一個 license 清單的列表 https://choosealicense.com/licenses/,
我根據開源協議的寬鬆程度,整理了一個列表,方便查看:

如何產出規範、安全、高質量的代碼?

開源協議

幾乎所有的開源協議有一個共同的注意事項:採用該開源協議的軟件項目,不提供任何責任轉移和質量保證。也就是說採用開源軟件造成的法律問題和開源項目無關,另外需要使用者承擔因質量問題造成的所有後果。另外,除了引入的程序包之外,字體、圖片、特效音、手冊等媒體資源也算廣義上的“軟件”需要考慮開源協議和使用場景。

第三方依賴管理

對項目中出現的任何第三方依賴有效的管理有非常重要的意義,通過掃描工具,識別出項目中是否有源碼、jar包、二進制文件是否來源於某個開源項目。

任何的第三方軟件需要申請入庫管理(內部其他團隊申請通過可以直接使用),質量團隊對申請的軟件進行評估:

  • 是否有開源義務需要履行
  • 引入的第三方依賴是否有 CVEs等漏洞
  • 第三方開源軟件是否仍然在維護

質量團隊根據上面的一些條件,決定出申請的軟件能否在項目中使用,允許被採用的軟件會定義出優選級別,優先推薦團隊使用較為優選的軟件,並對項目整體的優選率有一定要求。如果項目中出現了無法識別的二進制文件、非約定目錄下的代碼片段,需要報備。通過良好的依賴管理和規範化,能減少不良第三方依賴的引入,讓軟件項目透明、可信。

一些商業公司提供這些完整的服務,例如 fossid、blackduck、code-climate 等。

運維安全

大的軟件公司,往往有一堆流程和要求。雖然一線開發對堡壘機、防火牆、各種安全規範顯得不耐煩,但這些安全措施也在保護開發者。

防火牆用於環境隔離

往往開發者理解的防火牆用於防止網絡入侵、審計、入侵檢測等功能,除此之外,防火牆還可以用於各個環境的隔離。一般來說,企業對於生產環境的數據控制比較嚴格,不會將生產環境的權限交給團隊所有開發者,但網絡連接有可能疏漏。

曾經出現過一次線上事故,由於配置文件錯誤,將原本應該連接到測試的數據庫連接到了生產環境,造成大量髒數據寫入。如果通過防火牆規則對各個環境進行隔離,這類問題將不會出現。

另外也可以設計 DMZ 區,將面向用戶側的網關部署到 DMZ 區,僅僅開放必要的端口給網關,實現內外網的物理隔離。同時,對整個系統的防火牆策略應該清晰地記錄,否則在做大的基礎設施更新時,梳理出所有的防火牆策略,是一件比較困難的事情。

如何產出規範、安全、高質量的代碼?

憑據管理

項目中會用到大量的憑據,例如數據庫、第三方系統對接的 key,使用明文不是一件好事。理想的情況下,對項目中所有的密碼信息進行掩蓋(mask),避免 CI、日誌中敏感信息的洩露。

有很多種方法可以掩蓋項目中的密碼信息:

  1. 使用環境變量對密碼信息進行覆蓋。
  2. 使用Spring boot 的項目可以配置 jasypt,使用 jasypt 將密碼加密,將生成的加密串配置 ENC(加密串) 到工程的配置文件中。加密過程可以加鹽作為解密的憑據,“鹽” 可以不存放到工程中,在工程部署的時候注入即可。
  3. 如果使用 Jenkins 等 CI/CD 工具,可以使用構建平臺提供的憑證管理工具。
  4. 如果使用 Spring cloud,可以使用 spring cloud vault 組件部署一個憑證管理服務

另外,建議不要用任何個人憑據用作系統對接,應該使用一個公共的應用憑據。

堡壘機

一般來說我們管理服務器,所有的運維操作需要通過堡壘機進行操作。開放 22 等高危端口,允許開發者直接登錄到服務器是一種不安全的做法。

堡壘機,通俗的來說是跳板機 + 監控。最初使用的跳板機配置了兩張網卡,用於連接開發環境和生產環境,並沒有監控功能。在此基礎上,堡壘機增加了統一運維管理的功能,往往需要兩步驗證(SMS 或 Email),並對所有的操作進行記錄和監控。

在需要團隊參與運維工作的場景中,非常有必要部署一套堡壘機服務,並使用 LDAP 對接到團隊成員的 ID 上,便於集中運維管理。

定期對系統軟件掃描

Linux 系統往往有云廠商推送安全補丁和風險提示,但是安裝到服務器上的軟件,例如 JDK、nodejs,需要自己檢查安全問題。因此需要在系統中安裝並定期運行 CVEs 檢查並及時更新。有一款 cvechecker 可以幫助運維人員,編寫一個腳本定期運行 cvechecker 檢查系統中已知的軟件是否存在 CVEs 漏洞,並提醒開發者及時更新。

寫在後面

剛開始工作時候,喜歡動態的、靈活的編程語言,討厭的死板的、套路化的編程語言,然而需要很長一段時間,才能意識到 “約束是程序員的朋友”。對一些安全知識瞭解的來源大多來自修復 SonarQube 的經歷,使用 findbugs 也讓我對 Java 基礎認識的更加深刻。

類似的,在使用一些框架、平臺的時候往往存在大量的限制,有時候開發者難以意識到 “限制” 正是框架、平臺的作者 “保護” 應用開發者的一種方式。有一些開發者以 Hack 框架、平臺為樂,但是這樣會帶來潛在的隱患,在用戶量上來之後負面效應表現的尤為明顯。

項目的規範化對於 Tech Lead來說可以減少程序的運行事故和 codereview 時間,對於團隊來說也許可以少加班吧。

文/ThoughtWorks少個分號


分享到:


相關文章: