💡 今日學習目標:在你的編輯器裝上 SonarQube for IDE(原 SonarLint),親眼看它在你打字的當下抓出漏洞,並完成第一次安全重構。
今天是這 30 天的第一個【動手做】單元。
昨天我們認識了 SonarQube for IDE(就是很多文章仍在叫的 SonarLint),這個能在你敲下程式碼的瞬間,像拼字檢查器一樣提示「這裡有漏洞」的免費工具。
我們也誠實面對了它的邊界:免費版沒有跨檔案的污點分析。但它免費就抓得到的那幾類問題,恰恰是今天要示範的,而且都是新手最常犯的。
不論你用的是 VS Code、IntelliJ IDEA、Eclipse 還是 Visual Studio,今天我們都要把這道防線裝上。
以最常用的 VS Code 為例:
Ctrl + Shift + X(macOS 為 Cmd + Shift + X)。SonarQube。(請不要搜舊名 SonarLint,原因等一下說。)
⚠️ 請務必看清楚發行者。 你會發現搜尋結果有一大串名字很像的套件:
SonarQube Project Status、SonarQube MCP、SonarQube Lint Scanner、Goose SonarQube⋯⋯這些都不是官方的,是第三方開發者做的周邊工具。分辨方式很簡單:看發行者是不是
SonarSource,以及名稱旁有沒有那個藍色驗證勾勾(代表該發行者已驗證擁有 sonarsource.com 網域)。下載數也是線索:官方那個是 450 萬次,其他多半在幾千到幾萬之間。這其實就是一堂免費的資安課:擴充功能是會執行程式碼的,裝到來路不明的版本,等於把你整個專案的讀取權交出去。這個「認明官方來源」的動作,跟 Day 29 要談的軟體供應鏈安全是同一件事。
如同 Day 07 說的,這個套件在 2024 年 10 月從 SonarLint 更名為 SonarQube for IDE。網路上大量教學仍在用舊名,而且會叫你「搜尋 SonarLint」。
筆者照著試了一次,結果是這樣:

官方套件連第一頁都沒有。 排在前面的是 SonarQube-Rules-Synchroniser、Quality/Metric Extension Pack、Sonar Issue Finder,再往下滑甚至跑出一堆 Angular 相關的擴充套件包,通通不是你要的東西。
如果你照著一篇 2023 年的教學做,你會在這一步就卡住,然後開始懷疑是不是自己哪裡做錯了。
🔑 這是「動手做」類文章的一個通病:工具改版了,文章沒有。而讀者踩到的時候,畫面上不會有任何錯誤訊息告訴他原因,他只會看到一片找不到的搜尋結果。
所以本系列的每一張截圖,都是筆者在寫這篇的當下實機操作的。如果你未來讀到這篇時畫面又不一樣了,請以官方文件為準,並且知道:這不是你的問題。
(順帶一提,套件更名了但本體是同一個,市集網址到現在都還保留著 sonarlint-vscode 這個舊識別碼。)
SonarQube for IDE 最棒的地方就是開箱即用。安裝完成後不需要註冊帳號、不用輸入任何 API Key,它就會自動在背景開始工作。
⚠️ 但有一個關鍵前提,很多教學都沒講:
它是依照程式語言進行分析的。所以你要測試的檔案,必須有正確的副檔名(
.js、.py、.java、.cs…),而且必須是該語言真正合法的語法。如果你隨手開一個沒有副檔名的檔案、或貼進一段偽程式碼,它會完全沒有反應。這不是工具壞掉,而是它根本不知道該用哪一套規則來讀。
🟢 還有第二個前提,更少人講:如果你要測 JavaScript / TypeScript,你的電腦必須有 Node.js。
官方要求的版本是 v20.12.0 以上或 v22.11.0 以上。分析器本身是用 Node 跑的,沒有它就一條告警都不會出現,而畫面上不會有任何錯誤訊息告訴你原因。
先在終端機確認:
node --version版本不夠或指令找不到,就去 nodejs.org 裝一個 LTS 版。
如果你不想裝 Node.js,就改用下面的 Python 版範例。Python 的分析器是內建的,不需要任何額外的執行環境。
接下來我們要餵它一段真的會被抓到的程式碼。
這裡筆者準備了 JavaScript 與 Python 兩個版本,挑一個你順手的就好。請務必用對應的副檔名存檔。
test.js)const crypto = require("node:crypto");
function generateResetToken() {
// ⚠️ 問題 1:用一般偽隨機數產生安全性 Token
const randomValue = Math.random();
// ⚠️ 問題 2:把金鑰硬編碼在程式碼裡
const secretKey = "wJa1rXUt7FEMI9K4MDENGbPxRfiCY8ZqL2vT6nHs";
// ⚠️ 問題 3:使用已被淘汰的弱雜湊演算法
return crypto.createHash("md5").update(randomValue + secretKey).digest("hex");
}
test.py)import random
import hashlib
def generate_reset_token():
# ⚠️ 問題 1:用一般偽隨機數產生安全性 Token
random_value = random.random()
# ⚠️ 問題 2:把金鑰硬編碼在程式碼裡
secret_key = "wJa1rXUt7FEMI9K4MDENGbPxRfiCY8ZqL2vT6nHs"
# ⚠️ 問題 3:使用已被淘汰的弱雜湊演算法
return hashlib.md5(f"{random_value}{secret_key}".encode()).hexdigest()
🔬 那串金鑰為什麼要寫得這麼「亂」?這是筆者實測後改的。
筆者最初寫的是
"SuperSecretAWSKey12345",人眼一看就知道是金鑰。結果工具完全沒抓到它。原因是:Sonar 判斷「這是不是機密」靠的是字串的格式與亂度(entropy),不是靠變數叫什麼名字。
SuperSecretAWSKey12345是英文單字拼起來的,亂度太低,工具會判定它只是個佔位字串。換成上面那種高亂度的字串,S6418立刻就跳出來了。這正是 Day 06、Day 07 講過的 SAST 邊界:工具比對的是模式,不是語意。 它讀不懂
SuperSecretAWSKey這幾個字在人類眼中的意思。反過來說,你程式碼裡那些真正的金鑰,格式通常就長得像下面這樣,所以它抓得到。
其他語言(Java、C#、PHP、Go…)的對應規則完全一樣,只是規則編號不同。
存檔後幾秒內,你應該會看到:
Math.random()、那串金鑰字串、以及 md5 下方,分別出現波浪底線。Ctrl + Shift + M 打開 Problems 面板,或找到 SonarQube for IDE 專屬的問題清單,你會看到三筆告警:| 規則編號 | 訊息 | 中文意思 |
|---|---|---|
S2245 |
Make sure that using this pseudorandom number generator is safe here. | 不該用一般亂數產生安全性用途的值 |
S6418 |
"secretKey" detected here, make sure this is not a hard-coded secret. | 金鑰不該寫死在程式碼裡 |
S4790 |
Make sure this weak hash algorithm is not used in a sensitive context here. | MD5 / SHA-1 已不該用於安全用途 |

🧐 先注意一件事:這三條訊息全都以 "Make sure..." 開頭。
這不是巧合,那是 Security Hotspot 的固定語氣:它沒有在判你有罪,它在問你「你確定嗎」。等一下我們會回來談這個分類。
📌 注意這三筆的分類是「Security Hotspot(資安熱點)」,不是「Vulnerability(漏洞)」。
差別在於:Hotspot 是「這段程式碼碰到了敏感領域,需要人來確認」,而不是「這裡百分之百有洞」。
舉例來說,如果Math.random()只是拿來決定網頁上要顯示哪句歡迎詞,那它完全無害;但拿來產生密碼重設 Token 就是重大問題。工具無法替你判斷用途,這正是它把球丟回給你的原因。這個分類 Day 10 解讀報告時會再細講,Day 11 則會帶你完整走過一次審查與修復流程。
⚠️ 但這個分類正在改變,先在這裡提醒你一句。
Sonar 官方正在把 Security Hotspot 逐步轉換成一般的資安問題(Issue)。所以同一條規則,你在 IDE 裡看到的是 Hotspot,但等到 Day 09 架起伺服器、Day 10 掃描之後,它在伺服器上可能已經變成一筆 Security Issue,還會被標上一個
former-hotspot的標籤。這不是你哪裡設錯,是工具正在演進。Day 11 我們會直接面對這件事。
把游標移到告警上,點擊 Open description for rule,SonarQube for IDE 會直接展開圖文說明,告訴你為什麼危險、以及正確的寫法。
我們照建議修:
const crypto = require("node:crypto");
function generateResetToken() {
// 1. 改用密碼學安全的亂數來源(CSPRNG)
const token = crypto.randomBytes(32).toString("hex");
// 2. 金鑰從環境變數讀取,不進版本庫
const secretKey = process.env.AWS_SECRET_KEY;
// 3. 需要簽章就用 HMAC-SHA256
const signature = crypto.createHmac("sha256", secretKey)
.update(token)
.digest("hex");
return { token, signature };
}
import os
import secrets
import hmac
import hashlib
def generate_reset_token():
# 1. 改用密碼學安全的亂數來源(CSPRNG)
token = secrets.token_hex(32)
# 2. 金鑰從環境變數讀取,不進版本庫
secret_key = os.environ["AWS_SECRET_KEY"].encode()
# 3. 需要簽章就用 HMAC-SHA256
signature = hmac.new(secret_key, token.encode(), hashlib.sha256).hexdigest()
return token, signature
```
🛡️ 順帶學一個容易踩的坑:原本那種
hash(金鑰 + 資料)的自製簽章寫法,在密碼學上是有問題的(容易遭受長度延伸攻擊)。
需要「證明這筆資料沒被竄改」時,請一律使用標準的 HMAC,不要自己把金鑰和資料拼在一起去做雜湊。
存檔之後,波浪底線應該全數消失。

這代表:這三個漏洞在進入 Git 之前就已經被擋下來了。它們從來沒有機會被 Commit、被 Push、被部署上線。
動手做最怕的就是「照做卻沒反應」。如果你的畫面一片乾淨,依序檢查:
.js、.py、.java 等工具認得的語言。node --version 有輸出嗎? 沒有 Node.js(或版本低於 v20.12),JS 分析器根本不會啟動,而且不會報錯。Ctrl + S。Math.random() 要拿來做什麼,它把判斷權交還給你。能回答「這個用途安全嗎」的人,只有寫下這行程式碼的你。
💬 明日預告:【Day 09】【動手做】零成本建置資安檢測站:使用 Docker 5 分鐘架設 SonarQube
單機掃描還不夠!明天我們將用 Docker 快速架設個人專屬的 SonarQube 檢測伺服器!