iT邦幫忙

2026 iThome 鐵人賽

DAY 11
1

💡 今日學習目標:掌握在 SonarQube 介面中審查一筆資安問題的標準流程,學會把不安全的程式碼重構掉,並理解「修掉」與「標記」這兩條路各自該在什麼時候走。


📌 前言:發現問題後的關鍵一步,是修復與安全重構

經過昨天的掃描,我們在 SonarQube 的儀表板上拿到了專案報告:Security 面向有 1 筆待處理的問題。

今天就用它走完一次完整流程:解讀 ➔ 判斷 ➔ 重構 ➔ 重新掃描 ➔ 歸檔。

遇到這種告警,新手最常犯的兩個錯誤是:

  1. 視而不見:懶得看,直接全部標成「不是問題」把數字清空。
  2. 亂改一通:不知道正確做法,隨手改個寫法讓告警消失就算了。

這兩件事我們今天都會避開。


🧭 開始之前:一個你一定會發現的矛盾

先處理一件會讓你困惑的事,不然你會一直覺得哪裡怪怪的。

Day 08 在 IDE 裡,那條 md5 的告警被歸類為 Security Hotspot(資安熱點)。 但你昨天掃進伺服器之後,它出現在 Issues(問題) 清單裡,而不是獨立的 Hotspots 區塊。

點進去看,你會看到這個標籤:

SonarQube 的 Issues 清單,那筆問題被標上 Security、High 以及 former-hotspot 標籤

former-hotspot,直譯就是「曾經是 Hotspot」。SonarQube 自己承認了這件事。

為什麼會這樣?

Sonar 官方正在進行一項轉換:把過去歸類為 Security Hotspot 的規則,逐步改成直接產出資安問題。用官方文件的說法:

Rules that previously raised security hotspots will start raising vulnerabilities (Standard Experience) or security issues (MQR Mode).
(過去產出資安熱點的規則,將開始產出漏洞(標準模式)或資安問題(MQR 模式)。)

📖 資料來源:Sonar Documentation — Managing Security Hotspots

而且不只是規則層級的調整:在 v26 的左側選單裡,整個 Security Hotspots 頁面已經被掛上 Deprecated 標籤(步驟 4 的那張截圖就看得到)。這條路 Sonar 是真的打算收掉。

這對你來說其實是好事

Hotspot 這個設計原本的用意很好:「這段程式碼碰到了敏感領域,但我不知道你的用途,你自己判斷」。問題是它多了一層心智負擔,很多團隊看到 Hotspot 就不知道該不該當一回事,久了就整批忽略。

改成 Issue 之後,處理方式跟其他問題一致:要嘛修掉,要嘛明確標記為「已接受」並寫下理由。少一種特殊分類,就少一個被推託的藉口。

🔑 不過概念本身沒有消失。 「工具只能指出敏感用法,判斷用途的責任在你」這件事永遠成立,這也是 Day 08 心法 3 講的。變的只是它在介面上叫什麼名字。


🛠️ Step-by-Step:資安問題修復全流程

步驟 1:點開問題詳情,先讀完再動手

在儀表板點擊 Security 底下的 1 Open issues,會進到 Issues 清單。點那筆問題,右側會展開完整說明。

SonarQube 的問題詳情頁,顯示規則 javascript:S4790、Security High 影響,以及 Where is the issue 等分頁

畫面上有幾個地方值得看清楚:

  • 規則編號 javascript:S4790:點它會連到官方規則說明,那是最權威的出處。
  • Software qualities impacted:Security | High:它影響哪個品質面向、影響有多大。
  • Code attribute:Responsibility | Not trustworthy:Sonar 對這類問題的歸因分類。
  • Effort:30min:SonarQube 估計的修復成本。這個數字在排優先序時很有用。
  • 五個分頁:Where is the issue?(問題在哪)、Why is this an issue?(為什麼這是問題)、How can I fix it?(怎麼修)、Activity(處理紀錄)、More info(延伸資料)。

請務必把 Why is this an issue? 讀完再動手。 這幾分鐘是整個流程裡最有價值的部分。你不是在應付一個紅字,而是在讀一份寫得很好的資安教材。


步驟 2:閱讀程式碼現況

我們要修的就是 Day 09 建立的那個 checksum.js,它在計算檔案的完整性校驗碼:

❌ 不安全的寫法(修復前)

const crypto = require("node:crypto");

// 計算檔案的完整性校驗碼(checksum)
function generateChecksum(fileBuffer) {
    // ⚠️ MD5 已被證實可以人為製造碰撞
    return crypto.createHash("md5").update(fileBuffer).digest("hex");
}

module.exports = { generateChecksum };
import hashlib

def generate_checksum(file_bytes: bytes) -> str:
    # ⚠️ MD5 已被證實可以人為製造碰撞
    return hashlib.md5(file_bytes).hexdigest()

⚠️ 風險解析:MD5 與 SHA-1 在密碼學上都已被證實存在**雜湊碰撞(Collision)**缺陷:攻擊者有辦法構造出兩份內容完全不同、但雜湊值一模一樣的檔案。

一旦如此,「用雜湊值確認檔案沒被掉包」這件事就失效了:惡意檔案可以偽裝成正常檔案通過你的校驗。


步驟 3:進行安全重構 (Refactoring)

依照 How can I fix it? 分頁的建議,把弱雜湊換成現代標準的 SHA-256:

✅ 安全的寫法(修復後)

const crypto = require("node:crypto");

function generateChecksum(fileBuffer) {
    // 🛡️ 改用 SHA-256,目前沒有已知的實用碰撞攻擊
    return crypto.createHash("sha256").update(fileBuffer).digest("hex");
}

module.exports = { generateChecksum };
import hashlib

def generate_checksum(file_bytes: bytes) -> str:
    # 🛡️ 改用 SHA-256
    return hashlib.sha256(file_bytes).hexdigest()

🧭 順帶釐清一組很常被搞混的概念:

  • 校驗碼(Checksum):只是要確認「內容有沒有被改動」,用 SHA-256 這種強雜湊就夠了,不需要金鑰。
  • 簽章(Signature):要證明「這份資料確實出自持有金鑰的人」,就必須有金鑰,該用的是 Day 08 提過的 HMAC。

換句話說,如果你的函式名字裡有「signature」,那光把 MD5 換成 SHA-256 是不夠的。你缺的不是更強的雜湊,而是根本還沒有金鑰參與。


步驟 4:重新掃描與狀態歸檔

審查一筆資安問題,最後一定會走到兩條路的其中一條。分清楚這兩條路,是這整篇最重要的觀念。

路徑 A:程式碼真的有問題 ➔ 修掉它

  1. 存檔改好的程式碼。
  2. 重新執行一次昨天那段 sonar-scanner 指令。
  3. 回到 SonarQube 重新整理,這筆問題會自己從清單上消失,Security 評分也會跟著回到 A。

因為程式碼裡已經沒有 md5 了,規則自然不再命中。這種情況你不需要去按任何按鈕,什麼都不用標記。

💡 這也是為什麼儀表板上的數字會自己變好。你要做的不是「處理掉那個數字」,是處理掉那段程式碼。

路徑 B:這段程式碼在你的情境下確實安全 ➔ 標記並留下理由

有些時候,工具點出的敏感用法在你的情境裡是無害的。例如:這個 MD5 只是用來為快取產生一個鍵值,跟安全性完全無關。

這時候才輪到標記功能上場。在問題詳情頁點開狀態下拉選單(預設是 Open),會跳出 Change issue status:

SonarQube 的 Change issue status 下拉選單,列出 Accept、False Positive,以及被標為 Deprecated 的 Confirm 與 Fixed

四個選項,但實際上你只該用前兩個:

選項 官方說明 什麼時候用
Accept Won't fix immediately, but will no longer affect the quality gate 風險已知、已評估,且在這個情境下可以接受
False Positive Analysis is incorrect 規則誤判,例如它把測試用的假資料當成真金鑰
Confirm 已標示 Deprecated 不要用
Fixed 已標示 Deprecated 不要用

Accept 與 False Positive 的差別很重要:前者是「我知道它是問題,我選擇承擔」,後者是「它根本不是問題」。用錯會誤導下一個看報告的人。

⚠️ 請把 Accept 的官方說明再讀一次:「will no longer affect the quality gate」。

按下去的那一刻,這筆問題就不再擋你的品質關卡了。這正是它危險的地方:它是一個可以把紅燈變成綠燈的按鈕。 所以下面那句「一定要寫理由」不是形式主義,那是唯一能攔住這個按鈕被濫用的東西。

不論選哪一個,一定要在留言欄寫下判斷理由。一個夠格的理由要能回答三件事:這段程式碼實際在做什麼、為什麼在這個用途下無害、誰在什麼時候做的判斷。像這樣:

此 MD5 僅用於產生本地快取的鍵值,不參與身分驗證、完整性校驗或任何安全決策,輸入也不受外部使用者控制。經團隊評估後接受此風險。確認日期:2026-xx-xx

反過來說,下面這幾種寫法等於沒寫:「不影響」「這個沒問題」「之後再處理」「PM 說先上線」。它們共同的問題是沒有描述用途,而用途正是這整個判斷唯一的依據。

🛑 這一步不能省。 一個沒有理由的 Accept,對三個月後的你或接手的同事來說,跟沒審查過完全一樣,他無從判斷你是真的想過,還是只是想把數字清成零。

而 Day 06 說過的那個判準在這裡依然適用:你是真的解決了風險,還是只是讓工具閉嘴?

📊 順帶一提,SonarQube 有幫你盯著這件事。 儀表板上有一個獨立指標叫 Accepted issues,說明文字寫著「Valid issues that were not fixed(有效但未修復的問題)」。

這個數字如果一路往上長,代表你的團隊正在累積技術債而不自知。它被單獨拉出來顯示,就是為了不讓「標記完就忘記」變成常態。


那我們手上這一筆,該選哪一個?

一個都不要選。

我們的 checksum.js 是拿 MD5 去做檔案完整性校驗,這正好就是 MD5 碰撞會讓防線直接失效的場景。它不是誤判,也不是可以接受的風險,它就是一個該修的問題。所以我們走路徑 A:改成 SHA-256、重新掃描,讓它自己從清單上消失。

上面那張下拉選單的截圖,是點開來看、然後關掉的。

認識這個選單長什麼樣子是必要的,但認識它真正的目的,是為了知道自己什麼時候不該用它。在你伸手去按 Accept 之前,先問一次:我是沒辦法修,還是不想修?


🎯 今日重點小結與防守心法

  • 🔹 心法 1:棄用過時的弱演算法。在任何與安全沾上邊的場景中,堅決不用 MD5 與 SHA-1。
  • 🔹 心法 2:一筆資安問題只有兩個結局:修掉,或寫下理由。修好的會自己消失;標記為已接受的,一定要留下判斷依據給下一個人。
  • 🔹 心法 3:把審查當成免費的資安課。每一筆問題的「Why is this an issue?」都是一份寫得很好的教材,你每讀一篇,功力就長一分。

🚀 階段二完結:從「檢查」走向「製造」

恭喜!到今天為止,我們完成了 階段二:資安是檢查出來的(Day 06 - Day 11)。

這六天,我們一路走過:

  1. 搞懂了 Code Review 與 SAST 的分工,以及污點分析的四個核心概念。
  2. 認識了 SonarQube 家族,也誠實劃清了免費版的能力邊界,而昨天 SonarQube 自己也在儀表板上印出了同一句話。
  3. 親手裝上 IDE 外掛、用 Docker 架起檢測站、跑完第一次掃描、讀懂評分報告,並完成了第一次安全重構。

你現在擁有的,是一條實實在在、一毛錢沒花的檢查防線。

但別忘了 Day 03 戴明說的那句話:「檢查已經太遲了。品質的好壞,早就已經在產品裡面了。」

我們今天修掉的這個問題,其實是幾天前就已經寫進去的。如果在敲下那行程式碼的當下就寫對,這些重掃、重構、重新審查的時間,通通可以省下來。

接下來,我們即將進入:階段三 — 資安是「製造」出來的

從 Day 12 開始,我們要往品質演進的下一階前進,探討卡內基梅隆大學 SEI 的 Secure Coding 十大法則,以及 OWASP Top 10 的逐項攻防實戰,讓漏洞從一開始就沒有機會被寫出來。


💬 明日預告:【Day 12】既然事後要修,為什麼不一開始就寫好?SEI Secure Coding 核心哲學
明天我們將進入階段三,學習 SEI 提出著名的 Secure Coding 哲學與法則!


上一篇
【Day 10】【動手做】把程式碼丟進去掃描!解讀 SonarQube 的資安評分與漏洞報告
下一篇
【Day 12】既然事後要修,為什麼不一開始就寫好?SEI Secure Coding 核心哲學
系列文
槍林彈雨下的資安防守:從品質觀念切入,帶開發者從零動手作資安 30 天 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
AndyAWD
iT邦研究生 5 級 ‧ 2026-08-30 21:58:24

原來現代標準是 SHA-256

我要留言

立即登入留言