

<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Max的程式語言筆記</title>
	<atom:link href="https://stackoverflow.max-everyday.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://stackoverflow.max-everyday.com</link>
	<description>我要當一個豬頭，快樂過每一天</description>
	<lastBuildDate>Mon, 31 Aug 2026 09:19:45 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1</generator>

<image>
	<url>https://stackoverflow.max-everyday.com/wp-content/uploads/2017/02/max-stackoverflow-256.png</url>
	<title>Max的程式語言筆記</title>
	<link>https://stackoverflow.max-everyday.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>將 Pi Coding Agent 無縫對接本地 llama-server 自訂模型</title>
		<link>https://stackoverflow.max-everyday.com/2026/08/pi-coding-agent-llama-server/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/08/pi-coding-agent-llama-server/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Sun, 30 Aug 2026 05:16:06 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8683</guid>

					<description><![CDATA[在體驗 LLM 驅動的命令列開發工具時，許多人喜...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="1024" height="572" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/watermarked_img_11904330739739367754.jpg?v=1788066680" alt="" class="wp-image-8685" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/watermarked_img_11904330739739367754.jpg?v=1788066680 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/watermarked_img_11904330739739367754-600x335.jpg?v=1788066680 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/watermarked_img_11904330739739367754-768x429.jpg?v=1788066680 768w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">在體驗 LLM 驅動的命令列開發工具時，許多人喜歡在本地使用 <code>llama-server.exe</code> 載入 GGUF 模型（如 Qwen 系列），既能節省 API 費用又能保護隱私。</p>



<p class="wp-block-paragraph">不過，當嘗試將 <strong>Pi Coding Agent</strong> 連接到本地伺服器時，常會遇到 <code>Server is not running in llama.cpp router mode</code> 或 OpenAI API <code>401 Incorrect API key</code> 等驗證錯誤。</p>



<p class="wp-block-paragraph">這篇文章記錄了完整的除錯過程與最終解決方案，幫助你快速完成本地環境設定。</p>



<h2 class="wp-block-heading">問題解析</h2>



<p class="wp-block-paragraph">為什麼預設情況下會連線失敗？</p>



<ul class="wp-block-list">
<li><strong>端點路由不符</strong>：Pi Agent 預設對接 <code>llama.cpp</code> 時，期待對方開啟多模型路由（Router Mode）。如果本地只是單純啟動單一 GGUF 模型，會無法通過驗證。</li>



<li><strong>API Key 驗證攔截</strong>：如果 <code>llama-server</code> 開啟了 <code>--api-key</code>，Pi Agent 若未帶入對應金鑰，或是被預設路徑引導至 OpenAI 官方伺服器，就會觸發 401 Unauthorized 錯誤。</li>
</ul>



<h2 class="wp-block-heading">解決步驟</h2>



<h3 class="wp-block-heading">步驟一：修改 llama-server 啟動批次檔</h3>



<p class="wp-block-paragraph">首先，確保你的 <code>llama-server.exe</code> 啟動時明確設定了 API Key。</p>



<p class="wp-block-paragraph">在你的批次檔（例如 <code>start-server.bat</code>）中加入 <code>--api-key</code> 參數：</p>



<pre class="wp-block-code"><code>set MODEL=models\Qwen3.8-27B-UD-IQ1_S.gguf
set EXE=llama-server.exe

REM Large context for long code.
set CTX=16384
set BATCH=512
set NP=1

"%EXE%" ^
  -m %MODEL% ^
  -c %CTX% ^
  -np %NP% ^
  -cmoe ^
  -b %BATCH% -ub %BATCH% ^
  -ngl 999 ^
  --port 8080 ^
  --host 127.0.0.1 ^
  --api-key 12345678 ^
  -fa on ^
  -rea off ^
  --reasoning-format none ^
  --temp 0.3 ^
  --top-p 0.8 ^
  --top-k 30 ^
  --repeat-penalty 1.08 ^
  --context-shift</code></pre>



<p class="wp-block-paragraph">上面參數微調, 參考看看: 「思維鏈坍塌」超低位元量化模型遇到無休止內部思考、自我糾正<br><a href="https://stackoverflow.max-everyday.com/2026/08/chain-of-thought-collapse/">https://stackoverflow.max-everyday.com/2026/08/chain-of-thought-collapse/</a></p>



<p class="wp-block-paragraph">啟動後，可以使用 CMD 的 <code>curl</code> 測試 OpenAI 相容端點是否正常運作：</p>



<pre class="wp-block-code"><code>curl http://127.0.0.1:8080/v1/models -H "Authorization: Bearer 12345678"
</code></pre>



<p class="wp-block-paragraph">若有正確返回 JSON 格式的模型清單，代表伺服器端設定完成。</p>



<h3 class="wp-block-heading">步驟二：配置 Pi Agent 的自訂 Provider</h3>



<p class="wp-block-paragraph">Pi Agent 允許透過設定檔擴充自訂的 Provider。</p>



<p class="wp-block-paragraph">在 CMD 中建立並開啟設定檔：</p>



<pre class="wp-block-code"><code>if not exist "%USERPROFILE%\.pi\agent" mkdir "%USERPROFILE%\.pi\agent"
notepad "%USERPROFILE%\.pi\agent\models.json"
</code></pre>



<p class="wp-block-paragraph">貼上以下 JSON 設定。重點在於要明確指定 <code>"api": "openai-completions"</code>，否則 Pi 會因為缺少通訊協定設定而報錯：</p>



<pre class="wp-block-code"><code>{
  "providers": {
    "local-llama": {
      "baseUrl": "http://127.0.0.1:8080/v1",
      "apiKey": "12345678",
      "api": "openai-completions",
      "models": &#91;
        {
          "id": "models\\Qwen3.8-27B-UD-IQ1_S.gguf",
          "name": "Qwen3.8-Local",
          "contextWindow": 16384,
          "maxTokens": 4096
        },
        {
          "id": "models\\gemma-4-12B-it-qat-UD-Q4_K_XL.gguf",
          "name": "Gemma-4-12B-Local",
          "contextWindow": 16384,
          "maxTokens": 4096
        }
      ]
    }
  }
}</code></pre>



<p class="wp-block-paragraph">實際測試，模型名稱寫錯，還是可以正常執行，滿神奇的。</p>



<h3 class="wp-block-heading">步驟三：驗證並啟動 Pi Agent</h3>



<p class="wp-block-paragraph">設定完成後，在 CMD 執行模型清單檢視指令：</p>



<pre class="wp-block-code"><code>pi --list-models
</code></pre>



<p class="wp-block-paragraph">確認列表中出現了 <code>local-llama</code> 相關模型。</p>



<p class="wp-block-paragraph">接著建立一個專用的啟動批次檔 <code>run-pi-qwen3.8.bat</code>：</p>



<pre class="wp-block-code"><code>@echo off
pi --model "local-llama/models\Qwen3.8-27B-UD-IQ1_S.gguf"
</code></pre>



<p class="wp-block-paragraph">執行 <code>run-pi.bat</code> 即可順利在 Pi Agent 中與本地模型進行對話！</p>



<h2 class="wp-block-heading">總結</h2>



<p class="wp-block-paragraph">解決此問題的核心在於：</p>



<ol start="1" class="wp-block-list">
<li>本地伺服器需顯式指定 API Key 並提供 OpenAI 相容端點。</li>



<li>Pi Agent 的 <code>models.json</code> 必須完整填寫 <code>api: "openai-completions"</code> 規範。</li>
</ol>



<p class="wp-block-paragraph">透過自訂 Provider 機制，不僅能繞過官方 API 限制，還能靈活切換各種在地端運行的 GGUF 大語言模型！</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">選 llama.cpp 比較好的感覺, 可以設定的參數比較多.</p>



<p class="wp-block-paragraph"><strong>ollama</strong> 與 <strong>llama.cpp</strong> 底層皆基於 C/C++ 的推論引擎，但兩者的定位、使用對象與專案目標有著本質上的不同：</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><td><strong>特性</strong></td><td><strong>llama.cpp</strong></td><td><strong>Ollama</strong></td></tr></thead><tbody><tr><td><strong>主要定位</strong></td><td>底層推論核心 / 開發者工具</td><td>上層封裝與管理工具 / 終端使用者應用</td></tr><tr><td><strong>使用門檻</strong></td><td>較高（需熟悉指令列參數、自行下載模型與設定）</td><td>極低（一鍵安裝，具備類似 Docker 的指令與體驗）</td></tr><tr><td><strong>模型格式</strong></td><td><strong>GGUF</strong></td><td><strong>Modelfile</strong>（內部打包並調用 GGUF）</td></tr><tr><td><strong>服務架構</strong></td><td>原生編譯後為單一可執行檔或 C++ 函式庫</td><td>後台背景服務 (Daemon) + CLI 前端</td></tr><tr><td><strong>生態系統</strong></td><td>提供各種綁定 (Python, Rust 等) 與低階 API</td><td>提供相容 OpenAI 的 REST API，整合開源 UI 極佳</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">關鍵差異解析</h3>



<p class="wp-block-paragraph"><strong>1. llama.cpp：極致效能與底層控制</strong></p>



<ul class="wp-block-list">
<li><strong>核心價值</strong>：由 Georgi Gerganov 開發，旨在讓大型語言模型能在無 GPU 或硬體受限的普通消費級設備（如 Mac Apple Silicon、普通 PC）上高效運行。</li>



<li><strong>優勢</strong>：
<ul class="wp-block-list">
<li><strong>細粒度控制</strong>：可直接調整 KV 快取、GPU 層數分流 (<code>-ngl</code>)、Context 長度、Sampler 參數等。</li>



<li><strong>高擴充性</strong>：身為基礎架構，被無數上層工具（如 Python 庫 <code>llama-cpp-python</code>、text-generation-webui 等）整合。</li>
</ul>
</li>



<li><strong>劣勢</strong>：設定繁瑣，下載的模型需要手動管理檔案路徑與參數設定。</li>
</ul>



<p class="wp-block-paragraph"><strong>2. Ollama：極簡體驗與模型生態</strong></p>



<ul class="wp-block-list">
<li><strong>核心價值</strong>：將 llama.cpp 包裝成極簡化的桌面/伺服器工具，核心體驗借鑑了 Docker。</li>



<li><strong>優勢</strong>：
<ul class="wp-block-list">
<li><strong>開箱即用</strong>：只需執行 <code>ollama run llama3</code>，就會自動下載模型並直接啟動對話。</li>



<li><strong>模型庫管理</strong>：擁有官方模型庫 (library)，下載與更新非常方便。</li>



<li><strong>標準化 API</strong>：預設提供開箱即用的 REST API，能 seamlessly 介接 Open WebUI、AnythingLLM 或各類本地插件。</li>
</ul>
</li>



<li><strong>劣勢</strong>：預設封裝隱藏了許多底層參數，若要高度客製化推論細節，需要透過編輯 <code>Modelfile</code> 完成。</li>
</ul>



<h3 class="wp-block-heading">該如何選擇？</h3>



<ul class="wp-block-list">
<li>選 <strong>Ollama</strong>：如果你想要快速在本地端跑起 LLM、連結現成的 Web UI，或是為自己的應用程式快速接上本地 API。</li>



<li>選 <strong>llama.cpp</strong>：如果你是 C/C++ 開發者、需要整合嵌入式系統、或者需要對硬體推論細節進行極致優化與自訂。</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/08/pi-coding-agent-llama-server/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>「思維鏈坍塌」超低位元量化模型遇到無休止內部思考、自我糾正</title>
		<link>https://stackoverflow.max-everyday.com/2026/08/chain-of-thought-collapse/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/08/chain-of-thought-collapse/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Sun, 30 Aug 2026 03:47:19 +0000</pubDate>
				<category><![CDATA[AI開發筆記]]></category>
		<category><![CDATA[LLM]]></category>
		<category><![CDATA[Qwen]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8679</guid>

					<description><![CDATA[這現象叫思維鏈坍塌，簡單說就是模型卡在思考迴圈裡...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><img decoding="async" width="1024" height="572" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/watermarked_img_2106985570617172306.jpg?v=1788061504" alt="" class="wp-image-8680" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/watermarked_img_2106985570617172306.jpg?v=1788061504 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/watermarked_img_2106985570617172306-600x335.jpg?v=1788061504 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/watermarked_img_2106985570617172306-768x429.jpg?v=1788061504 768w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">這現象叫思維鏈坍塌，簡單說就是模型卡在思考迴圈裡鬼打牆！</p>



<p class="wp-block-paragraph">當你用超低位元量化模型（像 Qwen 3.8 27B 搭配 IQ1_S）時，模型的大腦精準度被嚴重壓縮。這導致它在算下一個詞的時候猶豫不決，完全搞不懂自己什麼時候該打住並輸出答案，最後就演變成無休止的自我糾正小劇場。</p>



<p class="wp-block-paragraph">想治好模型的鬼打牆，你可以從這三大招入手：</p>



<h2 class="wp-block-heading">改提示詞（最快見效）</h2>



<ul class="wp-block-list">
<li>強制封口：在提示詞裡直接下達死命令，要求模型隱藏或停用思考過程。例如寫下：「請直接回答問題，嚴禁在回答前進行任何自我剖析、內部思考或草稿推演。」</li>



<li>給定無知預設範本：遇到抓不到的資訊，直接給它一套標準答案。例如加上：「你無法取得即時的時間與日期。若使用者詢問日期，請直接回應『我無法獲取目前的系統時間』，別再浪費時間思考。」</li>
</ul>



<h2 class="wp-block-heading">調推論參數</h2>



<ul class="wp-block-list">
<li>降低溫度值（Temperature）：調到 0.1 到 0.3 之間，讓模型的選擇更果決，減少徘徊不決的機率。</li>



<li>調低 Top-P / Top-K：縮小模型的選詞範圍，強迫它選機率最高的字，避免掉進選擇困難的陷阱。</li>



<li>設定重複懲罰（Repeat Penalty）：把係數稍微調高（比如 1.1 到 1.15），強制模型打破重複喃喃自語的模式。</li>



<li>設定終止字詞（Stop Sequences）：加入常見的思考標籤（例如 或特定結尾符號）當作硬性中斷點。</li>
</ul>



<p class="wp-block-paragraph">llama-server.exe 參數</p>



<pre class="wp-block-code"><code>加入的參數說明如下：

--temp 0.2：將溫度設定為 0.2，落在你要求的 0.1 到 0.3 之間，大幅提升選擇的確定性。

--top-p 0.8 與 --top-k 20：縮小選詞範圍，只保留高機率的詞彙組合。

--repeat-penalty 1.12：設定重複懲罰係數為 1.12，防止模型陷入無意義的文字循環。</code></pre>



<p class="wp-block-paragraph">llama-server（基於 llama.cpp）中，這些參數的預設值如下：</p>



<ul class="wp-block-list">
<li>&#8211;temp（溫度值）：預設值為 0.8。</li>



<li>&#8211;top-p（Top-P 採樣）：預設值為 0.95。</li>



<li>&#8211;top-k（Top-K 採樣）：預設值為 40。</li>



<li>&#8211;repeat-penalty（重複懲罰）：預設值為 1.0（代表完全不施加重複懲罰）。</li>



<li>&#8211;reverse-prompt（終止字詞）：預設值為空（即未設定任何反向提示詞或終止標籤）。</li>



<li>原本的 llama-server 設定偏向一般對話與創意生成，因此預設的採樣範圍較廣、隨機性較高。修改後的參數則顯著降低了生成過程中的不確定因素，能讓程式碼輸出與長文本生成更加穩定。</li>
</ul>



<p class="wp-block-paragraph">改用 np 1 會是更好的選擇。</p>



<p class="wp-block-paragraph">主要原因說明：</p>



<ol start="1" class="wp-block-list">
<li>記憶體（VRAM）會被重複分配 設定 np 2 代表伺服器會把上下文長度（Context Window）預先切成 2 份獨立的空間。如果你的 context 設為 16384，系統實際上會為每份插槽分配記憶體。在單人使用的個人電腦上，這會無謂消耗大量的顯存與記憶體。</li>



<li>個人開發與 Agent 工具屬於單一請求 在使用 Pi Agent 或個人 CLI 工具時，基本上一次只會發送一個 Prompt 並等待回覆，完全不需要伺服器同時平行處理多個使用者的請求。</li>



<li>效能與推論速度無關 np 設定的是平行處理的請求數量，而不是 CPU 或 GPU 的計算核心數。設成 2 並不會讓單一回應速度變快，甚至可能因為資源被瓜分而影響效能。</li>
</ol>



<p class="wp-block-paragraph">調整建議：</p>



<p class="wp-block-paragraph">將批次檔中的設定改為： set NP=1</p>



<p class="wp-block-paragraph">使用超少 VRAM 執行 Qwen3.8-27B</p>



<pre class="wp-block-code"><code>set MODEL=models\Qwen3.8-27B-UD-IQ1_S.gguf
set EXE=llama-server.exe

REM Large context for long code.
set CTX=16384
set BATCH=512
set NP=1

"%EXE%" ^
  -m %MODEL% ^
  -c %CTX% ^
  -np %NP% ^
  -cmoe ^
  -b %BATCH% -ub %BATCH% ^
  -ngl 999 ^
  --port 8080 ^
  --host 127.0.0.1 ^
  --api-key 12345678 ^
  -fa on ^
  -rea off ^
  --reasoning-format none ^
  --temp 0.3 ^
  --top-p 0.8 ^
  --top-k 30 ^
  --repeat-penalty 1.08 ^
  --context-shift</code></pre>



<p class="wp-block-paragraph">如果顯示:</p>



<pre class="wp-block-preformatted">KV cache shifting is not supported for this context, disabling KV cache shiftin</pre>



<p class="wp-block-paragraph">代表該模型架構不支援 &#8211;context-shift，可以直接將這個參數移除，避免系統輸出無用警告。</p>



<p class="wp-block-paragraph">如果顯示:</p>



<pre class="wp-block-preformatted">OUT_OF_DEVICE_MEMORY</pre>



<p class="wp-block-paragraph"><strong>VRAM 完全爆掉（<code>UR_RESULT_ERROR_OUT_OF_DEVICE_MEMORY</code>）</strong> Qwen3.8-27B 模型的層數（Layers）通常只有 64 層左右，但批次檔傳入了 <code>-ngl 256</code>。系統會嘗試把全部 64 層加上 KV Cache 與預載矩陣通通塞進顯示卡的記憶體（VRAM），導致後端在做矩陣相乘時直接拋出記憶體不足的例外。</p>



<p class="wp-block-paragraph"><strong>調降 <code>-ngl</code>（GPU 卸載層數）</strong> </p>



<p class="wp-block-paragraph">將 <code>-ngl</code> 從 256 調降為 <strong>0</strong> 或 <strong>4~8</strong>。</p>



<ul class="wp-block-list">
<li>如果想完全用 CPU 穩定跑：設 <code>-ngl 0</code>。</li>



<li>如果想嘗試讓內顯分擔少量運算：設 <code>-ngl 6</code>。</li>
</ul>



<p class="wp-block-paragraph"><strong>加入 <code>--load-mode non</code> 避免載入異常</strong> </p>



<p class="wp-block-paragraph">混合 CPU 與 GPU 載入大模型時，<code>--load-mode none</code>（若要開啟 mmap 則是 <code>--load-mode mmap</code>）</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">單純用 CPU 跑的比 GPU 還快</h2>



<p class="wp-block-paragraph">開啟 GPU（Intel UHD 770 混合運算，<code>-ngl 10</code>）後的處理速度變慢（從純 CPU 的 <strong>~23 t/s</strong> 降至 <strong>~9-10 t/s</strong>），主要有以下四個原因：</p>



<ul class="wp-block-list">
<li><strong>跨匯流排傳輸開銷（PCIe / System Bus Overhead）</strong> 當你設定 <code>-ngl 10</code> 時，模型被切成兩部分：10 層在 GPU 運算，剩下的 30+ 層在 CPU 運算。模型在計算每一層神經網路時，張量資料（Tensors）必須不斷在 CPU 記憶體與 GPU 共享記憶體之間透過系統匯流排來回搬移。這個「跨邊界同步」的等待時間，遠遠超過了內顯幫忙計算所節省的時間。</li>



<li><strong>Intel UHD 770 算力與記憶體頻寬有限</strong> UHD 770 是 CPU 內建的顯示晶片，它沒有獨立的高速 VRAM（如 GDDR6 或 HBM），而是與 CPU 共享相同的 DDR 系統記憶體。因此 GPU 運算時無法享受獨立顯卡的大頻寬優勢，反而會跟 CPU 搶奪記憶體頻寬。</li>



<li><strong>SYCL / Level Zero 驅動與框架轉換成本</strong> <code>llama.cpp</code> 將矩陣運算派發給 Intel SYCL/Level Zero API 時，需要額外的 Context 切換與 Kernel 呼叫成本。對於規模較小或算力較弱的內顯，API 呼叫與同步的開銷比純 CPU 執行 AVX-512 / AMX / AVX2 原生指令集還要高昂。</li>



<li><strong>Batch Size 調小帶來的代價</strong> 將 <code>-b</code> 從 512 降到 128 雖然解決了 VRAM 崩潰問題，但也降低了 Prompt Processing（平行矩陣運算）的吞吐量，導致整體的速度指標下降。</li>
</ul>



<p class="wp-block-paragraph"><strong>建議結論</strong></p>



<p class="wp-block-paragraph">對於 Intel UHD 770 這類內建顯示卡，執行 12B 以上的大模型時，<strong>完全使用純 CPU 運算（<code>-ngl 0</code>）會是整體效率最高且最穩定的選擇</strong>。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">升級模型與設定</h2>



<ul class="wp-block-list">
<li>別用 IQ1 系列超低量化：IQ1_S 壓縮得太誇張，模型邏輯會直接崩潰。建議至少提升到 Q3_K_M 或 Q4_K_M。</li>



<li>關閉內建思考機制：如果版本支援切換，直接在推論端把思考模式（Reasoning/Thinking）關掉，讓它回歸傳統的單向輸出。</li>
</ul>



<h2 class="wp-block-heading">實際遇到的回覆</h2>



<pre class="wp-block-code"><code>Hi! I don't have a way to verify the current date or time, so I'll need to rely on the user's message to determine today's date. Based on the context, it looks like the user is asking "今天幾月幾日?" (What date is today?) — the user is asking for the current date.

I should respond with today's date. But wait — I'm an AI, I don't know the exact date unless I can get it from the system. Actually, I do not have access to the current date. I should respond naturally — perhaps ask or say I don't know the exact date, or...

Wait, the user is asking in Chinese "今天幾月幾日?" = "What day is today?" I should respond in Chinese, since they asked in Chinese. I can respond in Chinese.

But do I know today's date? I can't be 100% certain of the date. I should say I'm not sure what date it is... no, as an AI I do not know today's date. I should say I don't know today's date, or... hmm.

Actually, I should respond naturally. The user said "hi". I should respond in Chinese (matching the user's language) and ask about the date, or just say hi.

Let me respond naturally in Chinese: "嗨！今天几号？" — the user is asking for the date. I should respond: I don't know today's date...

Actually I should just naturally respond: "嗨！今天幾月幾日?" — the user is asking for the date. I don't know today's date. I should respond: I'm not sure what today's date is. I don't have access to the current date.

I could ask: "What date do you think it is today?" No, that's weird. Let me just respond naturally.

I think a natural response would be: "嗨！我不太知道今天的日期……" or "嗨！今天几号？我不知道今天的日期".

Actually, maybe the user just wants to chat. Let me respond naturally in Chinese: "嗨！今天几月幾日？我不太知道今天的日期……" — or something casual.

Let me think: the user said "今天幾月幾日?" — a casual "hi". A natural response: "嗨！今天幾月幾日？" — no, that's weird. Let me just say: "嗨！今天幾月幾日？" No, that's weird.

Simple response: "嗨！今天幾月幾日？" — I don't know today's date, so I'll say: I don't know today's date. Or: I don't know today's date, but I do know it's a nice day!

I think I'll just respond: "嗨！今天幾月幾日？" — no.

Let me just respond: "Hi! 今天幾月幾日？" — hmm.

I think I'll respond naturally: "嗨！今天幾月幾日？" — the user is asking for the date. I'll respond: I don't know today's date.

Actually, since I don't know the current date, I can just say I don't know today's date. Let me respond naturally: "嗨！今天幾月幾日？" — or just "Hi! 今天幾月幾日?" —

I'll just respond: "Hi! 今天幾月幾日?" — no, that's weird.

Final response: "Hi! 今天幾月幾日？" — I'm not sure what the date is...

I think I'll just respond naturally: "Hi! 今天幾月幾日?" — no, just: "Hi! 今天幾月幾日?"

Let me just respond naturally: "Hi! 今天幾月幾日?" — no.

I'll just respond: "Hi! 今天幾月幾日?" — or "Hi! 今天幾月幾日?" —

I'll just respond naturally: "Hi! 今天</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Pi Agent（AI Client） 在呼叫 llama-server（LLM Server） 時，因為 超時（Timeout） 與 上下文視窗（Context Window）飽和 所產生中斷問題</h2>



<h3 class="wp-block-heading">一、為什麼會中斷？</h3>



<p class="wp-block-paragraph">主要原因有兩個：<strong>推論耗時過長觸發 API 超時</strong>，以及 <strong>Context 長度已逼近上限</strong>。</p>



<ol start="1" class="wp-block-list">
<li><strong>Prompt Processing 耗時近 5 分鐘，觸發前端 Client 超時</strong>
<ul class="wp-block-list">
<li>從 Task 2924 的日誌可以看到：<br><code>prompt eval time = 285341.69 ms / 2528 tokens (8.86 tokens per second) eval time = 0.00 ms / 1 tokens</code></li>



<li>光是處理送進去的 2528 個 Token（Prompt Processing），純 CPU 就花了 <strong>285 秒（約 4.75 分鐘）</strong>。</li>



<li><strong>關鍵點</strong>：當 Prompt Eval 剛算完、才剛要開始產生第 1 個文字 Token（<code>eval time = 0.00 ms</code>）時，請求就被中斷了。這是因為 Pi Agent（或中間的 HTTP Client）等不到回應，達到 Timeout 上限而強制斷線。</li>
</ul>
</li>



<li><strong>Context 容量接近上限（10K 邊界）</strong>
<ul class="wp-block-list">
<li>Task 2473 釋放時 <code>n_tokens = 10785</code>，Task 2924 釋放時 <code>n_tokens = 12867</code>。</li>



<li>你的批次檔設定 <code>-c 10240</code>，當對話紀錄疊加超過 10K 時，伺服器必須進行 Context 截斷或滑動視窗計算，這大幅拉長了 Prompt Eval 的計算時間。</li>
</ul>
</li>
</ol>



<h3 class="wp-block-heading">二、需要修改 Server 端設定嗎？</h3>



<p class="wp-block-paragraph"><strong>是的，需要同時調整 Server 端參數與 Client 端設定。</strong></p>



<h4 class="wp-block-heading">1. 調整 Client 端（Pi Agent）的 Timeout 時間</h4>



<p class="wp-block-paragraph">這是解決中斷最直接的方法。Pi Agent 預設的 API 請求超時時間可能設在 120 秒或 180 秒，而你的純 CPU 處理 2.5K Tokens 需耗時 285 秒。</p>



<ul class="wp-block-list">
<li><strong>做法</strong>：請在 Pi Agent 的設定檔（<code>config.json</code> 或環境變數）中，將 HTTP / API 的 <code>timeout</code> 時間調大（例如改為 <code>600</code> 秒或 <code>0</code> 無限制）。</li>
</ul>



<p class="wp-block-paragraph">在 Windows 系統中，預設的設定檔與相關配置資料夾位於：</p>



<h3 class="wp-block-heading">1. 修改 <code>settings.json</code> 檔</h3>



<p class="wp-block-paragraph">在 <code>~/.pi/agent/settings.json</code> 檔案中新增或設定 <code>timeout</code>（以毫秒為單位）：</p>



<p class="wp-block-paragraph">JSON</p>



<pre class="wp-block-code"><code>{
  "timeout": 600000
}
</code></pre>



<ul class="wp-block-list">
<li><strong>說明</strong>：預設通常為 5 分鐘（<code>300000</code> 毫秒）。此設定控制等待 LLM API 回傳與 HTTP 請求回應的最長時間。如需關閉或解除限制，可嘗試設為 <code>0</code> 或高數值（如 <code>600000</code> 代表 10 分鐘）。</li>
</ul>



<h3 class="wp-block-heading">2. 透過互動式 TUI / 內建命令調整</h3>



<p class="wp-block-paragraph">在 Pi 的互動模式中，可以直接開啟設定選單調校：</p>



<ol start="1" class="wp-block-list">
<li>執行 <code>pi</code> 進入互動介面。</li>



<li>輸入 <code>/settings</code> 並按下 Enter 鍵。</li>



<li>在選單中滾動尋找 <strong>HTTP Timeout</strong>（或 Timeout）選項並修改其數值。</li>
</ol>



<p class="wp-block-paragraph">建議使用 /settings 指令, 讓 http timeout = disable 即可.</p>



<p class="wp-block-paragraph"></p>



<h4 class="wp-block-heading">2. 優化 Server 端（<code>llama-server</code> 批次檔）設定</h4>



<p class="wp-block-paragraph">為了提升純 CPU 的處理效率，建議對批次檔做以下幾項調整：</p>



<ul class="wp-block-list">
<li><strong>調大 Batch Size (<code>-b</code> / <code>-ub</code>) 以提升 Prompt Processing 速度</strong>
<ul class="wp-block-list">
<li>純 CPU 在算 Prompt processing 時，把 <code>-b</code> 與 <code>-ub</code> 從 256 提高到 <strong>512</strong> 或 <strong>1024</strong>，能更好發揮 CPU 的多線程與指令集平行運算能力（AVX-512 / AVX2），大幅縮短這 285 秒的等待時間。</li>
</ul>
</li>



<li><strong>將快取數據類型改為 8-bit（<code>-ctk q8_0 -ctv q8_0</code>）</strong>
<ul class="wp-block-list">
<li>隨著對話拉長到 10K，KV Cache 的讀取會吃掉大量 RAM 頻寬。開啟 KV Cache 量化可以節省一半的 KV 記憶體與讀取時間，對 CPU 生成速度有顯著幫助。</li>
</ul>
</li>



<li><strong>釋放被佔用的線程資源</strong>
<ul class="wp-block-list">
<li>在上一輪設定中，<code>-t 8 -tb 12</code> 可能會讓 CPU 在跨 P-Core / E-Core 時產生高昂的切換成本。建議統一設為 CPU 的<strong>實體大核數（P-Cores）</strong>，例如全部設為 <code>-t 11 -tb 11</code>。</li>
</ul>
</li>
</ul>



<h3 class="wp-block-heading">三、修改後的 Server 啟動批次檔</h3>



<p class="wp-block-paragraph">修改後的腳本範例：</p>



<pre class="wp-block-code"><code>set MODEL=models\Qwen3.8-27B-UD-Q5_K_M.gguf
set EXE=llama-server.exe

set CTX=32768
set BATCH=1024
set NP=1
set NGL=0

set ONEAPI_DEVICE_SELECTOR=*:cpu

"%EXE%" ^
  -m %MODEL% ^
  -c %CTX% ^
  -np %NP% ^
  -b %BATCH% -ub %BATCH% ^
  -ngl %NGL% ^
  -t 11 ^
  -tb 11 ^
  -ctk q8_0 -ctv q8_0 ^
  --load-mode mlock ^
  --port 8080 ^
  --host 127.0.0.1 ^
  --api-key 12345678 ^
  -fa on ^
  --reasoning-format none ^
  --temp 0.3 ^
  --top-p 0.8 ^
  --top-k 30 ^
  --repeat-penalty 1.08
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">模型的 Context（上下文長度）已經徹底爆表，觸發了限制</h2>



<p class="wp-block-paragraph">這次中斷的原因與上一次不同。這次不是因為運算太慢超時，而是<strong>模型的 Context（上下文長度）已經徹底爆表，觸發了限制</strong>。</p>



<p class="wp-block-paragraph">看這兩行關鍵日誌：</p>



<p class="wp-block-paragraph">Plaintext</p>



<pre class="wp-block-code"><code>64.02.764.301 I slot release: id 0 | task 24 | stop processing: n_tokens = 12972, truncated = 0
</code></pre>



<p class="wp-block-paragraph">Plaintext</p>



<pre class="wp-block-code"><code>eval time = 0.00 ms / 1 tokens
</code></pre>



<p class="wp-block-paragraph">你的批次檔設定 <code>-c 10240</code>（10K），但目前對話累積的 Token 數已經達到 <strong>12,972 個</strong>（包含了先前閱讀大量 Caddyfile、index.php、JWT.php 的檔案內容）。</p>



<p class="wp-block-paragraph">當 <code>n_tokens</code>（12,972）超過 <code>-c</code> 設定的容量上限（10,240）時，<code>llama-server</code> 會直接終止該次生成並釋放 slot，導致連 <strong>1 個 Token 都沒印出來</strong> 就不動了。</p>



<h3 class="wp-block-heading">解決方案</h3>



<p class="wp-block-paragraph">要徹底解決這個問題，有以下兩種處理方式：</p>



<h4 class="wp-block-heading">方案一：在 Pi Agent 中清理對話歷史（最快速）</h4>



<p class="wp-block-paragraph">Agent 剛才讀取了大量的後端程式碼，導致 Context 被塞滿。</p>



<ul class="wp-block-list">
<li>在 Pi Agent 中輸入修剪/重置對話的命令（例如 <code>/clear</code> 或開啟一個新的 session）。</li>



<li>重新要求它修復漏洞，這樣 Context 就不會溢出。</li>
</ul>



<h4 class="wp-block-heading">方案二：調大 Server 的 Context 並開啟記憶體優化（一勞永逸）</h4>



<p class="wp-block-paragraph">由於 Agent 做寫程式與資安診斷任務時，閱讀與分析程式碼需要極大的 context，10K 實在太小。建議將 Context 擴充至 <strong>24,576 (24K)</strong> 或 <strong>32,768 (32K)</strong>。</p>



<p class="wp-block-paragraph">為了防止 Context 變大導致 RAM 爆掉，請配合 <strong>KV Cache 8-bit 量化（<code>-ctk q8_0 -ctv q8_0</code>）</strong>，這能省下近一半的記憶體用量。</p>



<p class="wp-block-paragraph">修改後的 startup 批次檔範例：</p>



<pre class="wp-block-code"><code>set MODEL=models\Qwen3.8-27B-UD-Q5_K_M.gguf
set EXE=llama-server.exe

:: 將 Context 調大到 24K (24576) 或 32K (32768)
set CTX=24576
set BATCH=512
set NP=1
set NGL=0

set ONEAPI_DEVICE_SELECTOR=*:cpu

"%EXE%" ^
  -m %MODEL% ^
  -c %CTX% ^
  -np %NP% ^
  -b %BATCH% -ub %BATCH% ^
  -ngl %NGL% ^
  -t 8 ^
  -tb 8 ^
  -ctk q8_0 -ctv q8_0 ^
  --load-mode mlock ^
  --port 8080 ^
  --host 127.0.0.1 ^
  --api-key 12345678 ^
  -fa on ^
  --reasoning-format none ^
  --temp 0.3 ^
  --top-p 0.8 ^
  --top-k 30 ^
  --repeat-penalty 1.08
</code></pre>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/08/chain-of-thought-collapse/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Fortify 掃描耗盡了記憶體</title>
		<link>https://stackoverflow.max-everyday.com/2026/08/fortify-gc-overhead-limit-exceeded/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/08/fortify-gc-overhead-limit-exceeded/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 12 Aug 2026 04:59:44 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8663</guid>

					<description><![CDATA[Fortify 掃描發生 java.lang.O...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Fortify 掃描發生 <code>java.lang.OutOfMemoryError: GC overhead limit exceeded</code>，代表 JVM 記憶體 Heap 空間不足，且垃圾回收（GC）耗費超過 98% 的時間卻只能回收不到 2% 的記憶體。堆疊訊息顯示 Fortify 在解析 PHP 的 <code>hereDoc</code> (Heredoc語法) 時耗盡了記憶體。</p>



<p class="wp-block-paragraph">請依序嘗試以下解決方案：</p>



<h2 class="wp-block-heading">1. 增加 Fortify 掃描的記憶體上限</h2>



<p class="wp-block-paragraph">預設的記憶體設定可能不足以處理大型專案或複雜的 PHP 檔案。可以在執行 <code>sourceanalyzer</code> 命令行中加入 <code>-Xmx</code> 參數來提高記憶體配額：</p>



<p class="wp-block-paragraph"><strong>命令列執行 (CLI)：</strong></p>



<pre class="wp-block-code"><code><code>sourceanalyzer -b &lt;build_id> -Xmx16G -scan -f result.fpr </code></code></pre>



<p class="wp-block-paragraph"><em>(可依你的伺服器硬體規格調整，例如 <code>-Xmx12G</code> 或 <code>-Xmx24G</code>)</em></p>



<p class="wp-block-paragraph"><strong>若使用 GUI (Audit Workbench / ScanWizard)：</strong></p>



<p class="wp-block-paragraph">開啟 Fortify Audit Workbench。</p>



<p class="wp-block-paragraph">進入 <strong>Options</strong> > <strong>Global Settings</strong> > <strong>Memory Options</strong>。</p>



<p class="wp-block-paragraph">將 <strong>Maximum Allocation (-Xmx)</strong> 調大（如 <code>16384</code> MB）。</p>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph"><strong>修改全域設定檔 (fortify-sca.properties)：</strong></p>



<p class="wp-block-paragraph">找到 Fortify 安裝目錄或使用者目錄下的設定檔（例如 <code>&lt;Fortify_Home>/Core/config/fortify-sca.properties</code>），修改或加入以下參數：</p>



<pre class="wp-block-code"><code><code>com.fortify.sca.ProjectScanMemoryMB=16384</code></code></pre>



<h2 class="wp-block-heading">2. 排除不必要的檔案或大型自動生成檔</h2>



<p class="wp-block-paragraph">從錯誤堆疊可看出問題出在解析 PHP 的 <code>hereDoc</code> 語法（可能是巨大的模板檔、自動生成的 SQL/Data 檔、或是包含大字串陣列的檔案）。</p>



<p class="wp-block-paragraph">如果增加記憶體後仍失敗，建議排除非必要的第三方套件與靜態檔：</p>



<p class="wp-block-paragraph"><strong>在翻譯（Translation）階段使用 <code>-exclude</code> 排除特定目錄或檔案：</strong></p>



<pre class="wp-block-code"><code>sourceanalyzer -b &lt;build_id> -exclude "/vendor/" -exclude "/node_modules/" -exclude "/*.min.js" src/</code></pre>



<p class="wp-block-paragraph"><strong>過濾大檔案：</strong></p>



<p class="wp-block-paragraph">可以在 <code>fortify-sca.properties</code> 中限制 Fortify 解析單一檔案的大小上限：</p>



<pre class="wp-block-code"><code><code>com.fortify.sca.limit.file.size=10240 </code></code></pre>



<p class="wp-block-paragraph"><em>(單位為 KB，例如設定 <code>10240</code> 會跳過大於 10MB 的單一原始碼檔案)</em></p>



<h2 class="wp-block-heading">3. 採用「雙階段掃描」分離記憶體開銷</h2>



<p class="wp-block-paragraph">若原本是單一指令執行，請改為將翻譯 (Translation)<strong>與</strong>掃描 (Scan)拆為兩個獨立步驟，並只在掃描階段調大記憶體：</p>



<pre class="wp-block-code"><code># 第一步：翻譯階段
sourceanalyzer -b myproject -clean
sourceanalyzer -b myproject ./src

# 第二步：分析掃描階段 (在此處給予最大記憶體)
sourceanalyzer -b myproject -Xmx16G -scan -f result.fpr
</code></pre>



<h3 class="wp-block-heading">建議處理順序</h3>



<ol start="1" class="wp-block-list">
<li><strong>直接將記憶體調大至 16G (<code>-Xmx16G</code>)</strong> 重試一次。</li>



<li>若依然爆記憶體，檢查專案中是否有包含大量文字資料的 <code>.php</code> 檔案（如大陣列檔、DB dump 檔、巨型 Heredoc 模板），並使用 <code>-exclude</code> 將其排除。</li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph"><strong>detail error message:</strong></p>



<pre class="wp-block-preformatted">[error]: There is not enough memory available to complete analysis.  For details on making more memory available, please consult the user manual.<br>java.lang.OutOfMemoryError: GC overhead limit exceeded<br>	at com.fortify.frontend.php.parser.PHPLexerBase.nextToken(PHPLexerBase.java:95) ~[?:?]<br>	at org.antlr.v4.runtime.BufferedTokenStream.fetch(BufferedTokenStream.java:169) ~[antlr4-runtime-4.13.1.jar:4.13.1]<br>	at org.antlr.v4.runtime.BufferedTokenStream.sync(BufferedTokenStream.java:152) ~[antlr4-runtime-4.13.1.jar:4.13.1]<br>	at org.antlr.v4.runtime.BufferedTokenStream.nextTokenOnChannel(BufferedTokenStream.java:325) ~[antlr4-runtime-4.13.1.jar:4.13.1]<br>	at org.antlr.v4.runtime.CommonTokenStream.adjustSeekIndex(CommonTokenStream.java:70) ~[antlr4-runtime-4.13.1.jar:4.13.1]<br>	at org.antlr.v4.runtime.BufferedTokenStream.consume(BufferedTokenStream.java:137) ~[antlr4-runtime-4.13.1.jar:4.13.1]<br>	at org.antlr.v4.runtime.Parser.consume(Parser.java:571) ~[antlr4-runtime-4.13.1.jar:4.13.1]<br>	at org.antlr.v4.runtime.Parser.match(Parser.java:205) ~[antlr4-runtime-4.13.1.jar:4.13.1]<br>	at com.fortify.frontend.php.parser.PHPParser.hereDocPart(PHPParser.java:9032) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.hereDocExpr(PHPParser.java:8947) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.primaryExpr(PHPParser.java:9230) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.exponentExpr(PHPParser.java:8627) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.unaryExpr(PHPParser.java:8571) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.instanceofExpr(PHPParser.java:8470) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.notExpr(PHPParser.java:8412) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.productExpr(PHPParser.java:8321) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.sumExpr(PHPParser.java:8222) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.shiftExpr(PHPParser.java:8123) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.comparisonExpr(PHPParser.java:8037) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.equalityExpr(PHPParser.java:7960) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.bitwiseAndExpr(PHPParser.java:7883) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.bitwiseXorExpr(PHPParser.java:7805) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.bitwiseOrExpr(PHPParser.java:7727) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.booleanAndExpr(PHPParser.java:7649) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.booleanOrExpr(PHPParser.java:7571) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.nullCoalescingExpr(PHPParser.java:7501) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.ternaryExpr(PHPParser.java:7426) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.assignExpr(PHPParser.java:9557) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.primaryExpr(PHPParser.java:9276) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.exponentExpr(PHPParser.java:8627) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.unaryExpr(PHPParser.java:8571) ~[?:?]<br>	at com.fortify.frontend.php.parser.PHPParser.instanceofExpr(PHPParser.java:8470) ~[?:?]<br></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">這兩個問題解答如下：</p>



<h3 class="wp-block-heading">第一題：Bash 命令列上的 <code>myproject</code> 存在哪裡？</h3>



<p class="wp-block-paragraph">在 Fortify 命令列中：</p>



<pre class="wp-block-code"><code>sourceanalyzer -b myproject -clean
sourceanalyzer -b myproject ./src
sourceanalyzer -b myproject -scan -f result.fpr
</code></pre>



<p class="wp-block-paragraph"><code>-b myproject</code> 中的 <strong><code>myproject</code></strong> 稱為 <strong>Build ID（建置識別碼）</strong>。</p>



<p class="wp-block-paragraph">它<strong>不是</strong>一個真實的專案資料夾名稱，而是 Fortify 在系統內部用來記錄「翻譯（Translation）階段抽取的原始碼 AST 語法樹與中間檔」的<strong>暫存標籤</strong>。</p>



<h4 class="wp-block-heading">1. 實際檔案存在哪裡？</h4>



<p class="wp-block-paragraph">Fortify 會將 <code>myproject</code> 的中間檔案儲存在<strong>使用者家目錄下的 Fortify SCA 暫存區</strong>：</p>



<ul class="wp-block-list">
<li><strong>Linux / macOS:</strong><br><code>~/.fortify/sca&lt;版本號>/build/myproject/</code><br></li>



<li><strong>Windows (PowerShell / CMD):</strong><br><code>C:\Users\&lt;帳號>\.fortify\sca&lt;版本號>\build\myproject\</code></li>
</ul>



<h4 class="wp-block-heading">2. 注意事項</h4>



<ul class="wp-block-list">
<li>執行 <code>-clean</code> 時（如 <code>sourceanalyzer -b myproject -clean</code>），Fortify 就會<strong>刪除</strong>這個路徑下名為 <code>myproject</code> 的暫存資料夾。</li>



<li>在同一次掃描任務中，Translation（翻譯）和 Scan（分析）必須使用<strong>完全相同的 Build ID</strong>。</li>
</ul>



<h3 class="wp-block-heading">第二題：如何匯出 DeveloperWorkbook PDF 報告？</h3>



<p class="wp-block-paragraph">Fortify 提供了一個專門的命令行報表工具 <strong><code>ReportGenerator</code></strong>，可以直接讀取掃描結果檔（<code>.fpr</code>）並套用 <code>DeveloperWorkbook</code> 範本產生 PDF 報告。<sup></sup></p>



<h4 class="wp-block-heading">1. 匯出命令（Command-line）</h4>



<p class="wp-block-paragraph">請在 Terminal / Command Prompt 執行：</p>



<pre class="wp-block-code"><code>ReportGenerator -format pdf -f DeveloperWorkbook.pdf -source result.fpr -template DeveloperWorkbook.xml
</code></pre>



<h4 class="wp-block-heading">2. 參數說明</h4>



<ul class="wp-block-list">
<li><strong><code>-format pdf</code></strong>：指定輸出格式為 PDF（亦支援 <code>rtf</code>）。</li>



<li><strong><code>-f DeveloperWorkbook.pdf</code></strong>：輸出的 PDF 檔案名稱。</li>



<li><strong><code>-source result.fpr</code></strong>：你前面掃描完成所產生的 <code>.fpr</code> 結果檔。</li>



<li><strong><code>-template DeveloperWorkbook.xml</code></strong>：指定使用研發團隊常用的 <strong>Developer Workbook</strong> 報表範本。</li>
</ul>



<h4 class="wp-block-heading">3. 補充技巧 (進階)</h4>



<p class="wp-block-paragraph"><strong>如果是系統找不到 <code>DeveloperWorkbook.xml</code>：</strong>請加上完整的範本路徑，預設位在 Fortify 安裝目錄下：</p>



<pre class="wp-block-code"><code># Linux / macOS
ReportGenerator -format pdf -f DeveloperWorkbook.pdf -source result.fpr -template /opt/Fortify/Core/config/reports/DeveloperWorkbook.xml

# Windows
ReportGenerator -format pdf -f DeveloperWorkbook.pdf -source result.fpr -template "C:\Program Files\Fortify\Core\config\reports\DeveloperWorkbook.xml"</code></pre>



<p class="wp-block-paragraph"><strong>如果報告顯示的弱點數量被限制（例如預設只印前 5 個）：</strong>可以將 <code>DeveloperWorkbook.xml</code> 複製一份出來，用文字編輯器開啟，搜尋 <code>limit="5"</code> 並將其修改為 <code>limit="-1"</code>（代表不限數量，印出全部），再將修改後的 XML 作為 <code>-template</code> 帶入即可。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">翻譯（Translation）階段需要限制 memory usage?</h2>



<p class="wp-block-paragraph">答案是需要。</p>



<p class="wp-block-paragraph">雖然 Fortify 官方文件常強調在「分析（Scan）」階段給予大記憶體，但<strong>翻譯（Translation）階段如果記憶體不足，同樣會直接發生 <code>OutOfMemoryError</code> 或導致解析中斷（Crash）。</strong></p>



<p class="wp-block-paragraph">特別是你的案例中有大型 PHP 檔案（帶有巨型 Heredoc 語法），Fortify 在 Translation 階段進行語法解析（Lexing/Parsing）並建構 AST（抽象語法樹）時，就會在記憶體中吃掉大量空間。</p>



<h2 class="wp-block-heading">1. 為什麼 Translation 階段也需要限制/調大記憶體？</h2>



<ol start="1" class="wp-block-list">
<li><strong>語法分析（AST 建構）：</strong> 翻譯階段並非只是「複製檔案」，而是將原始碼編譯成 Fortify 的內部中間語言（NST/AST）。遇到複雜的 PHP 樣板、巨型 JSON/SQL 字串或第三方套件時，記憶體開銷會瞬間衝高。</li>



<li><strong>預設記憶體通常太小：</strong> 若未手動指定，<code>-b</code> 翻譯階段會使用 Fortify 的預設配額（通常僅 1GB~2GB），極易觸發 <code>java.lang.OutOfMemoryError: GC overhead limit exceeded</code>。</li>
</ol>



<h2 class="wp-block-heading">2. 如何在 Translation 階段設定記憶體？</h2>



<p class="wp-block-paragraph">語法與 Scan 階段完全相同，直接在 <code>sourceanalyzer</code> 命令列加上 <strong><code>-Xmx</code></strong> 參數：</p>



<pre class="wp-block-code"><code># 1. 先清除舊的 Build ID 暫存
sourceanalyzer -b myproject -clean

# 2. 【Translation 階段】給予足夠的記憶體（例如 16G）
sourceanalyzer -b myproject -Xmx16G ./src

# 3. 【Scan 階段】同樣給予足夠的記憶體
sourceanalyzer -b myproject -Xmx16G -scan -f result.fpr
</code></pre>



<h2 class="wp-block-heading">3. Translation 階段記憶體優化的最佳做法</h2>



<p class="wp-block-paragraph">如果給了 <code>-Xmx16G</code> 在 Translation 階段依然記憶體溢位，請搭配以下優化策略：</p>



<p class="wp-block-paragraph"><strong>主動過濾不需翻譯的目錄（最有效）：</strong>不要將非業務邏輯的套件帶入 Translation 階段：</p>



<pre class="wp-block-code"><code>sourceanalyzer -b myproject -Xmx16G \ -exclude "/vendor/" \ -exclude "/node_modules/" \ -exclude "/*.min.js" \ ./src</code></pre>



<p class="wp-block-paragraph"><strong>設定單一檔案大小上限（全域設定）：</strong>在 <code>fortify-sca.properties</code> 設定檔中加上 limit，防止 Translation 階段去解析幾十 MB 的巨型檔案：</p>



<pre class="wp-block-code"><code>com.fortify.sca.limit.file.size=10240</code></pre>



<p class="wp-block-paragraph"><strong>全域預設記憶體配置：</strong>若不想每次命令列都打 <code>-Xmx</code>，可在 <code>fortify-sca.properties</code> 中調整全域預設值，這會<strong>同時作用於 Translation 與 Scan 階段</strong>：</p>



<pre class="wp-block-code"><code>com.fortify.sca.ProjectTranslationMemoryMB=16384
com.fortify.sca.ProjectScanMemoryMB=16384</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">要匯出符合 <strong>OWASP Top 10</strong> 安全標準的 PDF 報告，選取的範本（Template）檔案說明如下：</p>



<h3 class="wp-block-heading">1. 建議選擇的 Template 檔案</h3>



<p class="wp-block-paragraph">Fortify 官方內建了不同年份版本的 OWASP Top 10 報本 XML 檔，位於 Fortify 報表目錄中：</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><td><strong>範本檔名 (Template File)</strong></td><td><strong>適用 OWASP 版本</strong></td><td><strong>說明 / 適用場景</strong></td></tr></thead><tbody><tr><td><strong><code>OWASP Top 10 2021.xml</code></strong></td><td><strong>OWASP Top 10 (2021)</strong></td><td><strong>【最推薦】</strong> 目前最主流的官方標準版本，建議優先選用。</td></tr><tr><td><strong><code>OWASP Top 10 2017.xml</code></strong></td><td>OWASP Top 10 (2017)</td><td>適用於客戶或公司稽核規範明確指定 2017 年版的專案。</td></tr><tr><td><strong><code>OWASP Top 10.xml</code></strong></td><td>通用/預設連結版</td><td>部分 Fortify 舊版本的預設檔名（會指向該版本的預設 OWASP）。</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">2. 命令行 (ReportGenerator) 執行指令</h3>



<p class="wp-block-paragraph">預設範本檔案存放在 Fortify 安裝目錄下的 <code>Core/config/reports/</code> 資料夾中。</p>



<h4 class="wp-block-heading"><strong>Linux / macOS:</strong></h4>



<pre class="wp-block-code"><code>ReportGenerator -format pdf \
  -f OWASP_Top10_Report.pdf \
  -source result.fpr \
  -template "/opt/Fortify/Core/config/reports/OWASP Top 10 2021.xml"
</code></pre>



<h4 class="wp-block-heading"><strong>Windows:</strong></h4>



<pre class="wp-block-code"><code>ReportGenerator -format pdf ^
  -f OWASP_Top10_Report.pdf ^
  -source result.fpr ^
  -template "C:\Program Files\Fortify\Core\config\reports\OWASP Top 10 2021.xml"
</code></pre>



<p class="wp-block-paragraph"><em>(注意：路徑中包含空白字元，必須使用雙引號 <code>"..."</code> 包裹)</em></p>



<h3 class="wp-block-heading">3. 在 Audit Workbench (GUI) 中選擇</h3>



<p class="wp-block-paragraph">若使用圖形化介面匯出：</p>



<ol start="1" class="wp-block-list">
<li>開啟 <code>.fpr</code> 結果檔。</li>



<li>點擊選單 <strong>Tools</strong> > <strong>Generate Report</strong>。</li>



<li>在 <strong>Report Template</strong> 下拉選單中選擇 <strong><code>OWASP Top 10 2021</code></strong> (或 <code>OWASP Top 10</code>)。</li>



<li>將 <strong>Format</strong> 設為 <strong>PDF</strong>，點擊 <strong>Save Report</strong> 即可。</li>
</ol>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 關鍵技巧：印出「所有」弱點細節 (解除預設 5 筆限制)</h3>



<p class="wp-block-paragraph">Fortify 官方預設的 OWASP 範本為了控制 PDF 頁數，<strong>預設每個漏洞分類只會列出前 5 筆弱點細節 (<code>limit="5"</code>)</strong>。</p>



<p class="wp-block-paragraph">若你的報告需要列出全部弱點：</p>



<ol start="1" class="wp-block-list">
<li>將 <code>OWASP Top 10 2021.xml</code> 複製到你的工作目錄，改名為 <code>OWASP_Custom.xml</code>。</li>



<li>用文字編輯器打開它，將裡面的 <strong><code>limit="5"</code></strong> 全部覆蓋替換為 <strong><code>limit="-1"</code></strong>（<code>-1</code> 代表不限制數量）。</li>



<li>執行指令時帶入 <code>-template OWASP_Custom.xml</code> 即可印出完整清單。</li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">command line manual:</h2>



<pre class="wp-block-code"><code>sourceanalyzer --help
OpenText SAST (Fortify) CE 25.2.0.0116
Copyright (c) 2003-2025 Open Text

Usage:

  Clean:
     sourceanalyzer.exe -b &lt;build-id> -clean
  Build:
     sourceanalyzer.exe -b &lt;build-id> &lt;sca-build-opts>
  Scan:
     sourceanalyzer.exe -b &lt;build-id> -scan &lt;sca-scan-opts>

Detailed invocation:

  Build:
     sourceanalyzer.exe -b &lt;build-id>
          &#91; &lt;sca-build-options> ]
          &lt;file-specifier>
     sourceanalyzer.exe -b &lt;build-id>
          &#91; &lt;sca-build-options> ]
          &lt;compiler> &lt;compiler-options>
     sourceanalyzer.exe -b &lt;build-id>
          &#91; &lt;sca-build-options> ]
          touchless &lt;build-tool> &#91; &lt;build-tool-options> ]
     sourceanalyzer.exe -b &lt;build-id>
          &#91; &lt;sca-build-options> ]
          devenv &lt;solution-file> /REBUILD
     sourceanalyzer.exe -b &lt;build-id>
          &#91; &lt;sca-build-options> ]
          msbuild /t:rebuild &lt;solution-or-project-file>
     sourceanalyzer.exe -b &lt;build-id>
          &#91; &lt;sca-build-options> ]
          xcodebuild -project &lt;xcodeproj-file>
     sourceanalyzer.exe -b mybuild
          -source-base-dir &lt;webapp-root> &lt;cfm-file-specifier>
  Scan:
     sourceanalyzer.exe -b &lt;build-id> -scan
          &#91; -f &lt;output-file> ]
          &#91; -scan-precision &lt;level> ]
          &#91; -rules &lt;rules.xml> &#91; -no-default-rules ] ]
          &#91; -filter &lt;filter-file> ]
  Clean:
     sourceanalyzer.exe -b &lt;build-id> -clean
  Query:
     sourceanalyzer.exe -b &lt;build-id> { -show-build-warnings | -show-files }
     sourceanalyzer.exe { -version | -show-build-ids }
     sourceanalyzer.exe { -h | -? | -help }



Options


General Options
These options are applicable to all sourceanalyzer.exe invocations.

  @&lt;file>                     Reads command line options from the specified
                              file.  Note that there is no space before the
                              file argument.

  -debug                      Causes the build step to write additional
                              troubleshooting information to the log file.
                              Use if instructed by Fortify Customer Support.
                              Also see "-logfile".

  -logfile &lt;file>             Specifies a destination for the log file.

  -verbose                    Outputs verbose messages to the console.

  -Xmx&lt;num>M                  Specifies the maximum Java heap size.
                              Default is -Xmx1800M.

  -autoheap                   Instructs SCA to set the maximum Java heap size
                              based on available physical memory. Use instead
                              of -Xmx.  Enabled by default.

  -fcontainer                 When run in a Docker container, instructs SCA
                              to detect and use only the memory allocated to
                              the container.

  -version                    Shows the sourceanalyzer.exe version.


Command Options
Note: Only one "command" option is allowed per invocation.


  -h                          Displays this help text.
  -help
  -?

  -clean                      Deletes all intermediate files and build records.
                              When a build ID is also specified with -b, only
                              files and build records related to that build ID
                              are deleted.

  -show-binaries              See the user guide.
  -show-build-tree            See the user guide.

  -show-build-ids             Lists all the Fortify build IDs (analysis models).

  -show-build-warnings        Displays all the actionable warnings that
                              occurred during the translation phase of the build
                              ID specified by "-b".

  -show-files                 Displays all the source files built into the model
                              specified by "-b".

  -show-loc                   Displays lines of code processed for files built
                              into the model specified by "-b".

  -scan                       Causes sourceanalyzer.exe to run an analysis.

  (none)                      If no command option is present, a build step
                              is assumed.


Build Options
"Build" options translate source code into a Fortify analysis model.


  -b &lt;build-id>               Specifies a unique name that identifies the
                              Fortify analysis model to be built. Also see
                              "-scan".

  -build-label &lt;label>        Specifies an optional, arbitrary string value to
                              the Fortify analysis model. Will be included in
                              the output file.

  -build-project &lt;project>    Specifies an optional, arbitrary string value to
                              the Fortify analysis model. Will be included in
                              the output file.

  -build-version &lt;version>    Specifies an optional, arbitrary string value to
                              the Fortify analysis model. Will be included in
                              the output file.

  -encoding &lt;encoding-name>   Specifies the source file encoding.
                              Default value is the platform default.

Compiler Integration Build Options
These options are used when integrating OpenText SAST (Fortify) with a compiler.

  &lt;compiler> &lt;compiler-opts>  Specifies the compiler command line. The file
                              being compiled will be added to the analysis
                              model, and the compiler will be invoked.

  touchless &lt;build-tool>      Specifies a build tool command. The build tool
    &#91; &lt;build-tool-options> ]  will be invoked, and any file being compiled
                              will be added to the analysis model.

  -nc                         When specified, the compiler is not invoked.


File Specification Build Options
These options are used to pass source files directly to OpenText SAST (Fortify).

  &lt;file-specifier>            Expression denoting a file or a group of files,
                              optionally matching a pattern:
                              file1.java - a file
                              file*.java - files matching expression
                              "path/**/*.java" - recursive expression matches.
                              Note: Always escape ** expressions in quotes.

  -exclude &lt;file-specifier>   Excludes any files matched by &lt;file-specifier>
                              from the set of files to translate


Java-specific Build Options
These options should be used in conjunction with file specification options.

  -classpath &lt;classpath>      Uses the specified classpath value for Java
  -cp &lt;classpath>             builds.

  -extdirs                    Accepts a colon or semicolon separated list
                              of directories.  Any jar files found in
                              these directories are included on the
                              classpath. Equivalent to the -extdirs option
                              to javac.

  -sourcepath                 Specifies the location of source files which will
                              not be included in the scan but will be used for
                              name resolution. Equivalent to the -sourcepath
                              option to javac.
                              The sourcepath is like classpath, except it uses
                              source files rather than class files for
                              resolution.

  -source &lt;value>             Indicates which version of the Java language the Java
  -jdk &lt;value>                code adheres to.  Valid values are 1.8, 8, 11, 17, 21.
                              Default is "11".

  -java-build-dir &lt;dir>       Used to specify one or more directories to which
                              Java sources are being compiled. May also be
                              specified at scan time.

Other Language-Specific Build Options

  -source-base-dir &lt;root>     The base directory for a ColdFusion application.

  -python-path                Add an import directory for a Python application.

  -apex                       Set ".cls" file extension to Apex language.
                              (detected based on file content by default). Equivalent to
                              -Dcom.fortify.sca.fileextensions.cls=APEX

  -apex-sobject-path          Add file to load SObject types in Apex application.

Scan Options

  -b &lt;build-id>               Specifies the build ID.  The build ID is used
                              to track which files are compiled and linked
                              as part of a build, to later scan those files.
                              This option may be specified more than once to
                              include multiple build IDs in the same scan.

  -bin &lt;binary>               All source files compiled and linked into the
                              specified binary are scanned.  Multiple binaries
                              may be specified.

  -disable-default-rule-type  See the user guide.

  -f &lt;file>                   The file to which analysis results are written.
                              Default is stdout.

  -filter &lt;file>              Specifies a filter file.  For more information,
                              see the user guide.

  -scan-policy &lt;policy>       Specifies a scan policy for vulnerability prioritization.
                              Valid values are classic, security, devops. Default is security.
                              For more information, see the user guide.

  -java-build-dir &lt;dir>       Used to specify one or more directories to which
                              Java sources have been compiled.  May also be
                              specified at build time.

  -no-default-issue-rules     See the user guide.
  -no-default-sink-rules      See the user guide.
  -no-default-source-rules    See the user guide.

  -no-default-rules           Indicates that OpenText SAST (Fortify) should not use its
                              default rules.  Must be used in conjunction with
                              "-rules"

  -rules &lt;specifier>          Specifies custom rules file or directory.  If a
                              directory is specified, all files ending in ".bin"
                              or ".xml" are included.
                              This option may be used multiple times.

  -quick                      Runs a quick scan. Quick scans complete faster at
                              the cost of reduced accuracy.

  -scan-precision &lt;level>     Configures the depth, precision and speed of the scan
  -p &lt;level>                  with configuration properties specific for the level.
                              The valid values are 1, 2, 3, and 4.

  -quiet                      Disables the command line progress bar.

  -scan                       Causes OpenText SAST (Fortify) to perform analysis against a
                              model.  The model must be specified with "-b".


Build Sessions

  -export-build-session &lt;file.mbs>

                              Store the translated model specified by -b to the
                              specified file.

  -import-build-session &lt;file.mbs>

                              Load the specified file into a build model.  If
                              the build ID of the model already exists in the
                              model registry, the import fails with the message
                              that a build already exists with that ID.


License Directives

  -store-license-pool-credentials "&lt;lim_url>|&lt;lim_pool_name>|&lt;lim_pool_pwd>|&lt;proxy_url>|&lt;proxy_user>|&lt;proxy_pwd>"

                              Stores your LIM license pool credentials to
                              allow OpenText SAST (Fortify) to use the
                              LIM for licensing. Proxy information is optional.

  -clear-license-pool-credentials

                              Removes the LIM license pool credentials from
                              the fortify-sca.properties file.

  -request-detached-lease &lt;duration>

                              Requests a detached lease from the LIM license
                              pool for exclusive use on this system for the
                              specified duration (in minutes).

  -release-detached-lease

                              Releases a detached lease back to the license
                              pool.


EXAMPLES


Build examples:
  Generic (Java, configuration, PHP, JavaScript, ASP/VBScript, VB6):
     sourceanalyzer.exe -b mybuild .
     sourceanalyzer.exe -b mybuild file1.java file2.java
     sourceanalyzer.exe -b mybuild *.bas *.cls *.frm
     sourceanalyzer.exe -b mybuild "site/**/*.php"

  ColdFusion translation:
     sourceanalyzer.exe -b mybuild -source-base-dir /www/app "/www/app/**/*.cfm"

  SQL translation:
     sourceanalyzer.exe -b mybuild -Dcom.fortify.sca.fileextensions.sql=PLSQL *.sql
     sourceanalyzer.exe -b mybuild -Dcom.fortify.sca.fileextensions.sql=TSQL *.sql

  C/C++ builds:
     sourceanalyzer.exe -b mybuild gcc -c test.c
     sourceanalyzer.exe -b mybuild CL.EXE /o HelloWorld HelloWorld.c
     sourceanalyzer.exe -b mybuild make
     sourceanalyzer.exe -b mybuild devenv myproject.msproj /REBUILD

  Objective-C/C++ builds:
     sourceanalyzer.exe -b mybuild clang -ObjC HelloWorld.m
     sourceanalyzer.exe -b mybuild xcodebuild -project myproject.xcodeproj

  .NET builds:
     sourceanalyzer.exe -b mybuild devenv myproj.sln /REBUILD
     sourceanalyzer.exe -b mybuild msbuild /t:rebuild myproj.csproj

  Java specific builds:
     sourceanalyzer.exe -b mybuild -cp lib/dependency.jar "src/**/*.java"
     sourceanalyzer.exe -b mybuild -cp mytaglibs.jar webapp/*.jsp
     sourceanalyzer.exe -b mybuild touchless ant

  J2EE specific builds:
     sourceanalyzer.exe -b mybuild -cp "app/WEB-INF/lib/*.jar" app/*.jsp

Scan step:
     sourceanalyzer.exe -b mybuild -scan -f results.fpr


See OpenText(TM) Static Application Security Testing (Fortify) User Guide for a complete explanation of each option.


You can open a support case for Fortify products online using
our customer support system. This streamlined procedure is designed to
provide easier access and improved customer satisfaction.

Access your account at https://softwaresupport.softwaregrp.com/.</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">這代表即使給了 32G 記憶體，ANTLR 在解析 PHP 語法時仍陷入了死迴圈或極度消耗記憶體的狀態（問題依然卡在 <code>PHPParser.hereDocPart</code> / <code>PHPLexerBase</code>）。</p>



<p class="wp-block-paragraph">通常這有兩個主因：</p>



<ol start="1" class="wp-block-list">
<li><strong>Windows 通配符問題</strong>：<code>-exclude "/vendor/"</code> 在 CMD/PowerShell 根本沒有生效，Fortify 依然把 <code>vendor</code> 進去了。</li>



<li><strong>存在「超大檔」或「極長 Heredoc 檔」</strong>：例如大型 SQL dump、Base64 圖片檔、自動生成的測試資料或超大樣板檔，導致 ANTLR Parser 記憶體暴增。</li>
</ol>



<p class="wp-block-paragraph">請依序透過以下方法徹底解決：</p>



<h3 class="wp-block-heading">1. 修正 Windows 的 <code>-exclude</code> 語法（最常踩坑）</h3>



<p class="wp-block-paragraph">在 Windows 命令列，<code>-exclude</code><strong>必須使用雙引號，且建議使用 <code>**</code> (Ant 語法) 與 Windows 反斜線</strong>，否則過濾會無效：</p>



<p class="wp-block-paragraph">DOS</p>



<pre class="wp-block-code"><code>sourceanalyzer -b portal_project -clean

sourceanalyzer -b portal_project -Xmx32G -exclude "**/vendor/**" -exclude "**/node_modules/**" -exclude "**/*.min.js" backend/
</code></pre>



<h3 class="wp-block-heading">2. 開啟弱點檔大小限制 (Limit File Size)</h3>



<p class="wp-block-paragraph">這是解決 <code>hereDoc</code> 爆記憶體最有效的武器。直接限制 Fortify 跳過單檔超過指定大小的檔案（例如 1MB 或 2MB）：</p>



<h4 class="wp-block-heading">做法 A：命令列加上 <code>-D</code> 參數</h4>



<p class="wp-block-paragraph">DOS</p>



<pre class="wp-block-code"><code>sourceanalyzer -b portal_project -Xmx32G -Dcom.fortify.sca.limit.file.size=2048 -exclude "**/vendor/**" backend/
</code></pre>



<p class="wp-block-paragraph"><em>( <code>2048</code> 代表超過 2MB 的原始碼檔案就跳過不安裝/解析，可有效避免吃到包含巨大字串或 Heredoc 的 PHP 檔案)</em></p>



<h4 class="wp-block-heading">做法 B：修改 <code>fortify-sca.properties</code></h4>



<p class="wp-block-paragraph">在 <code>C:\Users\&lt;你的帳號&gt;\.fortify\fortify-sca.properties</code> 或 Fortify 安裝目錄下的 <code>Core\config\fortify-sca.properties</code> 加入：</p>



<p class="wp-block-paragraph">Properties</p>



<pre class="wp-block-code"><code>com.fortify.sca.limit.file.size=2048
</code></pre>



<h3 class="wp-block-heading">3. 找出到底是哪個「元凶檔案」卡住，單獨排除它</h3>



<p class="wp-block-paragraph">如果是業務邏輯資料夾內有一個幾十 MB 的 PHP 檔案（例如包含超大字串陣列或字典）：</p>



<p class="wp-block-paragraph">請加上 <strong><code>-debug</code></strong> 或 <strong><code>-verbose</code></strong> 參數重新執行一次，並觀察 log 印到哪一個 <code>.php</code> 檔案時卡死：</p>



<p class="wp-block-paragraph">DOS</p>



<pre class="wp-block-code"><code>sourceanalyzer -b portal_project -Xmx32G -verbose backend/ &gt; scan_log.txt
</code></pre>



<p class="wp-block-paragraph">打開 <code>scan_log.txt</code> 翻到最底端，查看崩潰前最後進行解析（Parsing/Lexing）的是哪一個 <code>.php</code> 檔案，接著直接將該檔案加入排除：</p>



<p class="wp-block-paragraph">DOS</p>



<pre class="wp-block-code"><code>sourceanalyzer -b portal_project -Xmx32G -exclude "**/ProblematicFile.php" backend/
</code></pre>



<h3 class="wp-block-heading">4. （終極方案）調整 ANTLR / PHP 語法分析設定</h3>



<p class="wp-block-paragraph">如果特定 PHP 檔案無法排除，但包含極長的 Heredoc，可以在 <code>fortify-sca.properties</code> 加入以下參數，限制語法樹深度或停用部分 ANTLR 耗效能的檢查：</p>



<pre class="wp-block-code"><code># 限制 Fortify 對單一檔案最大解析時間（單位：秒，預設無限制，可設為 300 秒）
com.fortify.sca.PhaseTimeout=300

# 關閉部分過度深度的 AST 解析
com.fortify.sca.DisablePHP2MethodCallDataflow=true
</code></pre>



<h3 class="wp-block-heading">建議嘗試步驟：</h3>



<ol start="1" class="wp-block-list">
<li>先執行 <code>sourceanalyzer -b portal_project -clean</code> 清除快取。</li>



<li>加上限制檔案大小參數 <code>-Dcom.fortify.sca.limit.file.size=2048</code> 並修正過濾語法 <code>-exclude "**/vendor/**"</code> 重新執行。</li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">從你提供的完整 log 可以精準定位出問題的根源所在：</p>



<p class="wp-block-paragraph">Log 在處理到 <code>SendForgotPasswordTotpApi.php</code>（或緊隨其後的下一個 PHP 檔）時，Fortify 的 <strong>PHP 語法解析器 (PHPFrontEnd)</strong> 因為遇到極長或結構極度複雜的 Heredoc / String，導致 ANTLR 語法分析器陷入計算迴圈並爆記憶體崩潰，進而拋出 <code>NullPointerException</code>。</p>



<p class="wp-block-paragraph">請採用以下 <strong>4 步驟解決方案</strong> 來繞過這個解析器的死區：</p>



<h3 class="wp-block-heading">步驟 1：強制限制單檔大小 (File Size Limit)</h3>



<p class="wp-block-paragraph">這是最快且最有效的方法。加載此參數能直接讓 Fortify <strong>跳過過大或包含巨大字串 (Heredoc/Blob) 的單一原始碼檔案</strong>，避免 ANTLR 解析器卡死。</p>



<p class="wp-block-paragraph">在 command 加入 <code>-Dcom.fortify.sca.limit.file.size=2048</code>（單位為 KB，即 2MB）：</p>



<p class="wp-block-paragraph">DOS</p>



<pre class="wp-block-code"><code>sourceanalyzer -b portal_backend -clean

sourceanalyzer -b portal_backend -Xmx32G -Dcom.fortify.sca.limit.file.size=2048 backend/
</code></pre>



<h3 class="wp-block-heading">步驟 2：明確排除 <code>TestTools</code> 或測試/資料庫備份檔案</h3>



<p class="wp-block-paragraph">從 Log 中可以看到你的 <code>backend/</code> 下包含了 <code>TestTools</code> 與 <code>db/migrations</code>。通常測試工具與 Migration 檔中會含有大量假資料、巨型 SQL 字串或 Heredoc 模板。</p>



<p class="wp-block-paragraph">請使用 <strong>Windows 專用 Ant 通配符</strong> 將它們排除：</p>



<p class="wp-block-paragraph">DOS</p>



<pre class="wp-block-code"><code>sourceanalyzer -b portal_backend -clean

sourceanalyzer -b portal_backend -Xmx32G -Dcom.fortify.sca.limit.file.size=2048 -exclude "**/TestTools/**" -exclude "**/db/**" -exclude "**/vendor/**" backend/
</code></pre>



<h3 class="wp-block-heading">步驟 3：定位「致命檔案」並直接單獨排除</h3>



<p class="wp-block-paragraph">如果前兩步執行後依然在 <code>SendForgotPasswordTotpApi.php</code> 附近崩潰，代表問題出在業務邏輯程式碼本身的某個特定檔案。</p>



<ol start="1" class="wp-block-list">
<li>打開 <code>SendForgotPasswordTotpApi.php</code> 以及與它在同一目錄下的前後檔案。</li>



<li>檢查程式碼中是否有使用 <code>&lt;&lt;&lt;EOD</code> &#8220;<strong>/SendForgotPasswordTotpApi.php&#8221; &#8220;</strong>/TestTools/<strong>&#8221; &#8220;</strong>/db/<strong>&#8221; &#8220;</strong>/vendor/&#8221; # ### (2048 (5分鐘)，防止 <strong>Heredoc &#8212; -Dcom.fortify.sca.limit.file.size=&#8221;2048&#8243; -Xmx32G -b -clean -exclude -f -scan / 2MB 3. 300 4：寫入 ANTLR Fortify HTML KB) Nowdoc PHP Phase Timeout，讓 <code>-exclude</code> <code>&lt;&lt;&lt;HTML</code> <code>C:\Users\max\.fortify\fortify-sca.properties</code> cmd &#8220;`properties <code>fortify-sca.properties</code> backend/ com.fortify.sca.PhaseTimeout=&#8221;300&#8243; com.fortify.sca.limit.file.size=&#8221;2048&#8243; portal_backend portal_backend_result.fpr sourceanalyzer 之類的 在檔案末端寫入以下設定： 完成上述設定後，執行以下完整的標準清洗與翻譯流程： 將它單獨排除： 建議的標準執行指令 或 找到該檔案後，直接使用 檔案也發生 步驟 無限迴圈 秒 若要避免以後其他 解析單一檔案逾時自動跳過，而不是直接崩潰： 設定全域超時（終極防護） 設定單一階段解析超時為 語法</strong>，且裡面放了極長的 語法卡死，直接開啟 請開啟以下檔案（若沒有請自行建立）： 資料或密碼學金鑰/雜湊值。 郵件模板、JSON 限制單一檔案解析上限></li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">這個檔案 (<code>SendForgotPasswordTotpApi.php</code>) <strong>正是導致 Fortify ANTLR 語法分析器崩潰爆記憶體的元凶</strong>。</p>



<h3 class="wp-block-heading">為什麼是這個檔案？</h3>



<ol start="1" class="wp-block-list">
<li><strong>使用了巨大的 <code>Heredoc</code> (<code>$htmlBody = &lt;&lt;&lt;EOD ### #### $htmlBody="str_replace('{$totpCode}'," $htmlTemplate="file_get_contents($templatePath);" $htmlTemplate); $templatePath="__DIR__" $totpCode, '/../../templates/emails/forgot_password_totp.html'; (Inline) (圖案樣式)、HTML **ANTLR **修改後的 --- . ... / // 2. 25.2 &lt;&lt;&lt;EOD A：重構此 Base64 CSS、內嵌 Data EOD; EOD;</code>)</strong>： Fortify GC HTML Heredoc PHP SVG URI <code>$htmlBody</code><code>$textBody</code><code>.blade.php</code><code>.html</code><code>Heredoc</code><code>PHPLexerBase</code><code>PHPParser.hereDocPart</code><code>\</code>、雙引號、SVG php <code>java.lang.OutOfMemoryError: exceeded</code>。 limit overhead 使用的 使用簡單的字串替換填入動態變數 在處理包含複雜字元（如反斜線 字串。 將 將原本巨大的 抽離至獨立的 方案 時，內部的詞法狀態轉換會陷入計算遞迴，進而導致 替換為讀取外部模板檔案： 標籤的巨型 模板抽離，這同時也能讓你的程式碼結構更乾淨。 模板檔案中，改用檔案讀取方式載入： 檔案（最推薦，一勞永逸） 的 的內聯 程式碼： 與 解析器缺陷： 解決方案：重構此檔案（推薦）或針對性排除 語法）的長 這個檔案裡包含了一整段包含 這有兩種解決方式，<strong>最根本的方法是重構程式碼</strong>，將> <strong>優勢</strong>：抽離後 Fortify 解析這個 <code>.php</code> 時就不會觸發 Heredoc 的 ANTLR 解析 Bug，掃描能順利通過，且不會遺漏該 API 的安全弱點分析！</li>
</ol>



<h4 class="wp-block-heading">方案 B：單獨排除此檔案（最快）</h4>



<p class="wp-block-paragraph">如果你目前不想改動原始碼，可以在執行命令時，直接將該檔案單獨排除掉：</p>



<pre class="wp-block-code"><code>sourceanalyzer -b portal_backend -clean

sourceanalyzer -b portal_backend -Xmx16G -Dcom.fortify.sca.limit.file.size=2048 -exclude "**/SendForgotPasswordTotpApi.php" -exclude "**/TestTools/**" -exclude "**/db/**" -exclude "**/vendor/**" backend/
</code></pre>



<h4 class="wp-block-heading">方案 C：將檔案大小限制調低</h4>



<p class="wp-block-paragraph">因為這個 <code>.php</code> 檔案因為包含巨型 HTML，檔案大小可能較大。你可以嘗試將檔案限制從 <code>2048</code> (2MB) 調小到 <code>100</code> (100KB)：</p>



<pre class="wp-block-code"><code>sourceanalyzer -b portal_backend -clean

sourceanalyzer -b portal_backend -Xmx16G -Dcom.fortify.sca.limit.file.size=100 -exclude "**/vendor/**" backend/
</code></pre>



<p class="wp-block-paragraph"><em>(這樣 Fortify 會自動跳過此類包含巨型模板的過大 PHP 檔)</em></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/08/fortify-gc-overhead-limit-exceeded/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>搞定 Ubuntu 管理員大小事：清單、新增與刪除帳號</title>
		<link>https://stackoverflow.max-everyday.com/2026/08/ubuntu-admin-mangage/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/08/ubuntu-admin-mangage/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 11 Aug 2026 01:59:43 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8656</guid>

					<description><![CDATA[想在 Ubuntu 系統裡當個有實權的大老，或是...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="572" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/ubuntu-admin-mangage3_clean-1024x572.jpg" alt="" class="wp-image-8661" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/ubuntu-admin-mangage3_clean-1024x572.jpg?v=1786413620 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/ubuntu-admin-mangage3_clean-600x335.jpg?v=1786413620 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/ubuntu-admin-mangage3_clean-768x429.jpg?v=1786413620 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/ubuntu-admin-mangage3_clean.jpg?v=1786413620 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">想在 Ubuntu 系統裡當個有實權的大老，或是想把不安份的帳號給踢出去嗎？這篇教學帶你輕鬆掌握管理者權限的召喚與封印術！</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">一、 尋找幕後黑手：查看誰有最高權限</h3>



<p class="wp-block-paragraph">想知道系統裡面到底有誰偷偷開了外掛、擁有 sudo 權限嗎？直接輸入下面這行指令，就能讓所有管理員現形：</p>



<pre class="wp-block-code"><code>getent group sudo | cut -d: -f4</code></pre>



<p class="wp-block-paragraph">覺得還不夠清楚嗎？這裡還有兩招進階招術：</p>



<p class="wp-block-paragraph">把系統的最高神明 root 一併召喚出來：</p>



<pre class="wp-block-code"><code>echo "root"; getent group sudo | cut -d: -f4 | tr ',' '\n'</code></pre>



<p class="wp-block-paragraph">連他們的登入 Shell 一起抓出來看個仔細：</p>



<pre class="wp-block-code"><code>grep -E "$(getent group sudo | cut -d: -f4 | tr ',' '|')" /etc/passwd</code></pre>



<p class="wp-block-paragraph">小知識補充： Ubuntu 預設是用 sudo 群組來發放管理者權限，而不是單獨叫 admin 群組！</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">二、 召喚新夥伴：新增帳號並給予管理權限</h3>



<p class="wp-block-paragraph">想給好朋友開個權限嗎？標準流程是先建帳號，再把他拉進 sudo 俱樂部：</p>



<pre class="wp-block-code"><code>sudo adduser username
sudo usermod -aG sudo username</code></pre>



<p class="wp-block-paragraph">別忘了把 username 換成你朋友的帳號名稱！</p>



<p class="wp-block-paragraph">如果你趕時間，想要一鍵搞定，可以使用這套組合拳：</p>



<pre class="wp-block-code"><code>sudo useradd -m -s /bin/bash -G sudo username
sudo passwd username</code></pre>



<p class="wp-block-paragraph">驗證身分：</p>



<p class="wp-block-paragraph">想確認他有沒有成功拿到權限，可以執行這行命令檢查：</p>



<pre class="wp-block-code"><code>groups username</code></pre>



<p class="wp-block-paragraph">只要看到輸出裡面出現 sudo 這幾個字，就代表他已經順利晉升為管理員啦！</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">三、 提防豬隊友：刪除使用者帳號</h3>



<p class="wp-block-paragraph">當某些帳號不再需要，或是對方惹你生氣時，你可以根據想留下的東西選一種方式處理：</p>



<ol start="1" class="wp-block-list">
<li>純粹把人踢掉（保留他的家目錄與檔案）：</li>
</ol>



<pre class="wp-block-code"><code>sudo deluser username</code></pre>



<p class="wp-block-paragraph">或者使用 sudo userdel username 。</p>



<ol start="2" class="wp-block-list">
<li>連同他的家目錄與郵件資料一併蒸發（乾淨俐落）：</li>
</ol>



<pre class="wp-block-code"><code>sudo deluser --remove-home username</code></pre>



<p class="wp-block-paragraph">或者使用 sudo userdel -r username 。</p>



<ol start="3" class="wp-block-list">
<li>地毯式搜捕（刪除他在整個系統留下的所有檔案）：</li>
</ol>



<pre class="wp-block-code"><code>sudo deluser --remove-all-files username</code></pre>



<p class="wp-block-paragraph">動手前請注意：</p>



<p class="wp-block-paragraph">刪除前請先確定 username 已經替換成目標帳號。</p>



<p class="wp-block-paragraph">如果對方還在線上頑強抵抗，請先強制幫他登出：</p>



<pre class="wp-block-code"><code>sudo killall -u username</code></pre>



<p class="wp-block-paragraph">如果只是想拔掉他的權限，讓他變回普通百姓，不需要直接刪帳號，輸入這行命令即可：</p>



<pre class="wp-block-code"><code>sudo deluser username sudo</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/08/ubuntu-admin-mangage/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Windows Server 密碼最長使用期限</title>
		<link>https://stackoverflow.max-everyday.com/2026/08/windows-server-maxpwage/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/08/windows-server-maxpwage/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 05:34:05 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<category><![CDATA[Windows]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8652</guid>

					<description><![CDATA[資安規範與稽核標準建議將 「密碼最長使用期限」 ...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="638" height="240" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/image-1.png?v=1786340016" alt="" class="wp-image-8654" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/image-1.png?v=1786340016 638w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/image-1-600x226.png?v=1786340016 600w" sizes="auto, (max-width: 638px) 100vw, 638px" /></figure>



<p class="wp-block-paragraph">資安規範與稽核標準建議將 <strong>「密碼最長使用期限」</strong> 設定為 <strong><code>90</code> 天</strong>（約 3 個月換一次密碼）。</p>



<p class="wp-block-paragraph">在《行政院資通安全防護基準》及 ISO 27001 實務中，一般機關系統的密碼更換週期標準為 <strong>90 天～180 天</strong>（也就是 3 到 6 個月）。目前你的系統設定是 <code>120</code> 天（約 4 個月），雖然也符合規範，但修改為 <strong><code>90</code> 天</strong> 是各大稽核單位最喜愛的標準黃金值。</p>



<h3 class="wp-block-heading">如何在 PowerShell (管理員) 修改？</h3>



<p class="wp-block-paragraph">請開啟 <strong>PowerShell (以系統管理員身分執行)</strong>，執行以下指令：</p>



<h4 class="wp-block-heading">1. 設定為 90 天（推薦）：</h4>



<p class="wp-block-paragraph">PowerShell</p>



<pre class="wp-block-code"><code>net accounts /MAXPWAGE:90
</code></pre>



<h4 class="wp-block-heading">2. 驗證修改結果：</h4>



<p class="wp-block-paragraph">執行以下指令確認是否生效：</p>



<p class="wp-block-paragraph">PowerShell</p>



<pre class="wp-block-code"><code>net accounts
</code></pre>



<p class="wp-block-paragraph">觀察輸出中的這行：</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>密碼最長使用期限 (天): 90</strong></p>
</blockquote>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 補充說明（完整密碼原則組合建議）</h3>



<p class="wp-block-paragraph">修改完成後，你的 Windows Server 2019 密碼原則組合將會非常完美且完全符合資安稽核：</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><td><strong>項目</strong></td><td><strong>你的設定值</strong></td><td><strong>稽核標準與備註</strong></td></tr></thead><tbody><tr><td><strong>密碼長度下限</strong></td><td><strong>13</strong> 字元</td><td>符合 $\ge 12$ 或 $13$ 字元標準</td></tr><tr><td><strong>密碼最長使用期限</strong></td><td><strong>90</strong> 天</td><td>符合 90~180 天標準，每 3 個月更換</td></tr><tr><td><strong>歷程記錄長度</strong></td><td><strong>3</strong> 次</td><td>符合「不可與前 3 次相同」</td></tr><tr><td><strong>帳戶鎖定閾值</strong></td><td><strong>5</strong> 次</td><td>失敗 5 次即鎖定</td></tr><tr><td><strong>鎖定持續期間</strong></td><td><strong>15</strong> 分鐘</td><td>鎖定 15 分鐘（防暴力破解）</td></tr></tbody></table></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/08/windows-server-maxpwage/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Windows Server 密碼變更時，至少不可以與前3次使用過之密碼相同</title>
		<link>https://stackoverflow.max-everyday.com/2026/08/windows-server-uniquepw/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/08/windows-server-uniquepw/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 05:28:55 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<category><![CDATA[Windows]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8649</guid>

					<description><![CDATA[這是因為繁體中文版 Windows 的 net ...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="641" height="238" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/image.png?v=1786339647" alt="" class="wp-image-8650" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/image.png?v=1786339647 641w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/image-600x223.png?v=1786339647 600w" sizes="auto, (max-width: 641px) 100vw, 641px" /></figure>



<p class="wp-block-paragraph">這是因為繁體中文版 Windows 的 <code>net accounts</code> 指令參數名稱是 <strong>/UNIQUEPW:3</strong>（英文版才是 <code>/UNIQUEPW</code> 或 <code>/UNIQUENESS</code>）。</p>



<p class="wp-block-paragraph">請在 <strong>PowerShell (系統管理員)</strong> 中直接執行以下這行指令：</p>



<pre class="wp-block-code"><code>net accounts /UNIQUEPW:3
</code></pre>



<h3 class="wp-block-heading">執行後驗證</h3>



<p class="wp-block-paragraph">執行完畢後，再次輸入：</p>



<pre class="wp-block-code"><code>net accounts
</code></pre>



<p class="wp-block-paragraph">確認結果顯示為：</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>密碼維護的歷程記錄長度： 3</strong></p>
</blockquote>



<p class="wp-block-paragraph">這樣就順利修改完成，可以直接在表單的「密碼變更時，至少不可以與前3次使用過之密碼相同」勾選 <strong>「是」</strong> 了！</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">如何在 Windows Server 設定與確認？</h3>



<p class="wp-block-paragraph">如果你需要確保系統確實設定為「不能與前 3 次相同」，請依照以下步驟調整：</p>



<p class="wp-block-paragraph"><strong>1.開啟本機資安原則：</strong></p>



<p class="wp-block-paragraph">按下 <code>Win + R</code> 開啟「執行」視窗，輸入 <code>secpol.msc</code> 並按下 <strong>Enter</strong>。</p>



<p class="wp-block-paragraph"><strong>2.找到密碼歷史紀錄設定：</strong></p>



<p class="wp-block-paragraph">在左側選單點擊：<strong>帳戶原則</strong>（<code>Account Policies</code>） ➔ <strong>密碼原則</strong>（<code>Password Policy</code>）。</p>



<p class="wp-block-paragraph"><strong>3.修改強制保留密碼歷史記錄：</strong></p>



<p class="wp-block-paragraph">雙擊右側的 <strong>「強制保留密碼歷史記錄」</strong>（<code>Enforce password history</code>），將數值改為 <strong><code>3</code></strong>（或更大，例如標準常設為 <code>3</code> 或 <code>5</code>），點擊 <strong>確定</strong>。</p>



<p class="wp-block-paragraph"><strong>4.強制更新資安原則：</strong></p>



<p class="wp-block-paragraph">開啟 PowerShell（管理員身分），輸入 <code>gpupdate /force</code> 讓設定立即生效。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/08/windows-server-uniquepw/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>更改 Windows Server 密碼長度至少 N 個字元</title>
		<link>https://stackoverflow.max-everyday.com/2026/08/windows-server-min-pwd-length/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/08/windows-server-min-pwd-length/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 05:20:12 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<category><![CDATA[Windows]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8645</guid>

					<description><![CDATA[說明, 使用 net accounts 看到, ...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="881" height="609" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/mstsc_2026-08-10-13-16-hc.jpg?v=1786339126" alt="" class="wp-image-8647" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/mstsc_2026-08-10-13-16-hc.jpg?v=1786339126 881w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/mstsc_2026-08-10-13-16-hc-600x415.jpg?v=1786339126 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/08/mstsc_2026-08-10-13-16-hc-768x531.jpg?v=1786339126 768w" sizes="auto, (max-width: 881px) 100vw, 881px" /></figure>



<p class="wp-block-paragraph">說明, 使用 net accounts 看到, 修改前是最少10碼, 修改之後最少 13碼。</p>



<p class="wp-block-paragraph">要在 Windows Server 2019 設定<strong>密碼長度至少 13 個字元</strong>，你需要修改群組原則中的「密碼最短長度」。</p>



<p class="wp-block-paragraph">依照以下步驟調整即可：</p>



<h3 class="wp-block-heading">方法一：使用圖形介面 (GUI) 修改</h3>



<p class="wp-block-paragraph"><strong>1.開啟本機資安原則：</strong></p>



<p class="wp-block-paragraph">按下快捷鍵 <code>Win + R</code> 開起「執行」視窗，輸入 <code>secpol.msc</code> 並按下 <strong>Enter</strong>。</p>



<p class="wp-block-paragraph"><strong>2.找到密碼原則：</strong></p>



<p class="wp-block-paragraph">在左側選單依序展開：</p>



<p class="wp-block-paragraph"><strong>機關資安設定</strong>（或 <code>安全性設定</code>） ➔ <strong>帳戶原則</strong>（<code>Account Policies</code>） ➔ <strong>密碼原則</strong>（<code>Password Policy</code>）。</p>



<p class="wp-block-paragraph"><strong>3.修改密碼最短長度：</strong></p>



<p class="wp-block-paragraph">在右側視窗點擊兩下開啟 <strong>「密碼最短長度」</strong>（<code>Minimum password length</code>）。</p>



<p class="wp-block-paragraph">將原本的 <code>10</code> 改為 <strong><code>13</code></strong>（或更多字元），然後點擊 <strong>確定</strong>。</p>



<p class="wp-block-paragraph"><strong>4.強制更新原則：</strong></p>



<p class="wp-block-paragraph">開啟 PowerShell 或 CMD（管理員權限），輸入 <code>gpupdate /force</code> 讓新原則立即生效。</p>



<h3 class="wp-block-heading">方法二：使用 PowerShell 快速修改</h3>



<p class="wp-block-paragraph">如果你習慣用 command line 操作，可以開啟 <strong>PowerShell (以系統管理員身分執行)</strong>，複製並執行以下指令：</p>



<pre class="wp-block-code"><code># 1. 將目前的安全性原則匯出至暫存檔
secedit /export /cfg C:\secpol.cfg

# 2. 將密碼最短長度從原本的數值改為 13
(Get-Content C:\secpol.cfg) -replace 'MinimumPasswordLength = \d+', 'MinimumPasswordLength = 13' | Set-Content C:\secpol.cfg

# 3. 重新套用設定檔
secedit /configure /db $env:windir\security\local.sdb /cfg C:\secpol.cfg /areas SECURITYPOLICY

# 4. 清除暫存檔
Remove-Item C:\secpol.cfg
</code></pre>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 驗證是否修改成功</h3>



<p class="wp-block-paragraph">執行以下 PowerShell 指令確認設定是否成功生效：</p>



<pre class="wp-block-code"><code>net accounts
</code></pre>



<p class="wp-block-paragraph">觀察輸出結果中的 <strong>「密碼最短長度」 (Minimum password length)</strong> 是否已經變更為 <strong><code>13</code></strong>。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>補充說明（AD 網域環境）：</strong></p>



<p class="wp-block-paragraph">如果你的 Windows Server 有加入 <strong>Active Directory (AD) 網域</strong>，且密碼原則是由網域控制站 (DC) 統一管理的，請前往網域控制站開啟 <code>gpmc.msc</code> (群組原則管理)，並在 <strong>Default Domain Policy</strong> 中修改相同的項目。</p>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/08/windows-server-min-pwd-length/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHP 8.3 報錯排查：無法載入動態函式庫 php_xmlrpc.dll / php_gd2.dll 解決方案</title>
		<link>https://stackoverflow.max-everyday.com/2026/08/php_xmlrpc-php_gd2/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/08/php_xmlrpc-php_gd2/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Mon, 03 Aug 2026 07:41:00 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<category><![CDATA[php]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8642</guid>

					<description><![CDATA[將 PHP 升級至 8.0 以上版本（例如 PH...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">將 PHP 升級至 8.0 以上版本（例如 PHP 8.3）後，執行或啟動伺服器時可能會遇到類似以下的警告訊息：</p>



<pre class="wp-block-code"><code>PHP Warning: PHP Startup: Unable to load dynamic library 'php_xmlrpc.dll' (tried: C:\Program Files\PHP\v8.3\ext\php_xmlrpc.dll (找不到指定的模組。), C:\Program Files\PHP\v8.3\ext\php_php_xmlrpc.dll.dll (找不到指定的模組。)) in Unknown on line 0</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">原因分析</h3>



<p class="wp-block-paragraph">此錯誤通常源自以下兩個主要原因：</p>



<ol start="1" class="wp-block-list">
<li><strong>擴充套件（Extension）命名規則變更</strong>在舊版 PHP（PHP 7.x 及更早版本）中，設定檔常寫為 <code>extension=php_gd2.dll</code> 或帶有 <code>php_</code> 前綴。從 <strong>PHP 8.0</strong> 開始，官方簡化了指定方式，只需填寫擴充套件簡稱（例如 <code>extension=gd</code>）。若仍使用舊式檔名，PHP 在自動補充路徑時會產生如 <code>php_php_...dll.dll</code> 的重複路徑而找不到檔案。</li>



<li><strong>部分舊版擴充套件已被移除（以 XMLRPC 為例）</strong><code>xmlrpc</code> 擴充套件已於 PHP 8.0 起從核心移除並移至 PECL。因此 PHP 8.3 預設的 <code>ext/</code> 目錄下已不再提供 <code>php_xmlrpc.dll</code>。</li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">解決步驟</h3>



<h4 class="wp-block-heading">步驟 1：開啟 <code>php.ini</code> 設定檔</h4>



<p class="wp-block-paragraph">找到目前 PHP 8.3 環境所使用的 <code>php.ini</code> 檔案（例如：<code>C:\Program Files\PHP\v8.3\php.ini</code>）。</p>



<h4 class="wp-block-heading">步驟 2：修正或停用問題擴充套件</h4>



<p class="wp-block-paragraph">以系統管理員權限開啟 <code>php.ini</code>：</p>



<p class="wp-block-paragraph"><strong>若為舊版命名格式問題（如 GD 繪圖庫）：</strong>搜尋 <code>gd</code>，將舊式的設定修正為新版標準格式：</p>



<p class="wp-block-paragraph"><code>舊寫法：</code></p>



<pre class="wp-block-code"><code><code>extension=php_gd2.dll</code></code></pre>



<p class="wp-block-paragraph"><code>請改為： </code></p>



<pre class="wp-block-code"><code><code>extension=gd</code></code></pre>



<p class="wp-block-paragraph"><strong>若為已移出核心的擴充套件（如 XMLRPC）：</strong></p>



<p class="wp-block-paragraph">搜尋 <code>xmlrpc</code>，若無特殊需要，直接在行首加上分號 <code>;</code> 將其註解停用：</p>



<pre class="wp-block-code"><code>;extension=php_xmlrpc.dll 
;extension=xmlrpc</code></pre>



<h4 class="wp-block-heading">步驟 3：確認擴充套件目錄 (<code>extension_dir</code>)</h4>



<p class="wp-block-paragraph">確認 <code>php.ini</code> 中的 <code>extension_dir</code> 指令已正確啟用並指向正確路徑：</p>



<pre class="wp-block-code"><code>extension_dir = "ext"
</code></pre>



<p class="wp-block-paragraph"><em>(或使用絕對路徑：<code>extension_dir = "C:\Program Files\PHP\v8.3\ext"</code>)</em></p>



<h4 class="wp-block-heading">步骤 4：重啟 Web 伺服器 / PHP 服務</h4>



<p class="wp-block-paragraph">儲存 <code>php.ini</code> 後，重啟 Apache、Nginx、IIS 或重開 PHP CLI 環境，以使新的設定生效。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">The PHP startup warnings require &nbsp;php.ini&nbsp; cleanup:</p>



<pre class="wp-block-code"><code>date.timezone = Asia/Taipei<br>extension=gd</code></pre>



<p class="wp-block-paragraph">Remove duplicate entries for &nbsp;curl&nbsp;, &nbsp;mysqli&nbsp;, &nbsp;mbstring&nbsp;, &nbsp;openssl&nbsp;, &nbsp;ldap&nbsp;, and &nbsp;pdo_mysql&nbsp;.</p>



<p class="wp-block-paragraph">Also remove or install matching extensions:</p>



<ul class="wp-block-list">
<li> xmlrpc : unavailable in standard PHP 8.3; comment it out unless separately installed.</li>



<li> pdo_sqlsrv_82_nts_x64 : replace with the PHP 8.3 NTS x64 driver, or remove it if unused.</li>
</ul>



<p class="wp-block-paragraph">Do not suppress these with &nbsp;error_reporting(0)&nbsp;; correcting &nbsp;php.ini&nbsp; will eliminate the startup log noise</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/08/php_xmlrpc-php_gd2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>解決 Windows Server 下載失敗！連線意外關閉與安全封鎖的兩大妙招</title>
		<link>https://stackoverflow.max-everyday.com/2026/07/%e8%a7%a3%e6%b1%ba-windows-server-%e4%b8%8b%e8%bc%89%e5%a4%b1%e6%95%97%ef%bc%81%e9%80%a3%e7%b7%9a%e6%84%8f%e5%a4%96%e9%97%9c%e9%96%89%e8%88%87%e5%ae%89%e5%85%a8%e5%b0%81%e9%8e%96%e7%9a%84%e5%85%a9/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/07/%e8%a7%a3%e6%b1%ba-windows-server-%e4%b8%8b%e8%bc%89%e5%a4%b1%e6%95%97%ef%bc%81%e9%80%a3%e7%b7%9a%e6%84%8f%e5%a4%96%e9%97%9c%e9%96%89%e8%88%87%e5%ae%89%e5%85%a8%e5%b0%81%e9%8e%96%e7%9a%84%e5%85%a9/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 08 Jul 2026 06:45:59 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<category><![CDATA[Windows]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8632</guid>

					<description><![CDATA[寫程式或設定伺服器的時候，最怕遇到那種沒頭沒尾的...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">寫程式或設定伺服器的時候，最怕遇到那種沒頭沒尾的錯誤訊息。如果你在 Windows Server 上嘗試下載檔案，卻跳出要求已經中止，連接意外關閉這串字，先別急著抓狂！這通常是因為你的 Windows Server 預設停用了比較新的加密協定（ TLS 1.2 或 TLS 1.3 ），而 GitHub 偏偏強制要求使用 TLS 1.2 以上，兩邊對不上話，連線自然就被狠狠拒絕了。</p>



<p class="wp-block-paragraph">別擔心，我們在 PowerShell 裡手動強制開啟 TLS 1.2 ，就可以輕鬆搞定。</p>



<h3 class="wp-block-heading">絕招一：強制開啟 TLS 1.2 下載法</h3>



<p class="wp-block-paragraph">請複製以下這段程式碼，整段直接貼進你的 PowerShell 視窗裡面執行：</p>



<p class="wp-block-paragraph">PowerShell</p>



<pre class="wp-block-code"><code># 1. 強制讓 PowerShell 使用 TLS 1.2 加密協定
&#91;Net.ServicePointManager]::SecurityProtocol = &#91;Net.SecurityProtocolType]::Tls12

# 2. 重新嘗試下載安裝檔
Invoke-WebRequest -Uri "https://github.com/microsoft/go-sqlcmd/releases/latest/download/sqlcmd-windows-amd64.msi" -OutFile "$env:USERPROFILE\Downloads\sqlcmd.msi"

# 3. 執行安裝
Start-Process msiexec.exe -ArgumentList "/i `"$env:USERPROFILE\Downloads\sqlcmd.msi`"" -Wait
</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="695" height="652" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/image.png" alt="" class="wp-image-8633" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/image.png?v=1783492999 695w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/image-600x563.png?v=1783492999 600w" sizes="auto, (max-width: 695px) 100vw, 695px" /></figure>



<p class="wp-block-paragraph">執行之後，畫面上應該會彈出上面的截圖。</p>



<p class="wp-block-paragraph"></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">絕招二：繞過 IE 增強式安全性封鎖</h3>



<p class="wp-block-paragraph">如果用了第一招還是失敗，甚至跳出討人厭的 Internet Explorer 紅色警告，那就代表你抓到幕後黑手了！這是 Windows Server 惡名昭彰的「 IE 增強式安全性設定（ IE ESC ）」在作怪。</p>



<p class="wp-block-paragraph">因為 PowerShell 在下載檔案的時候，背後會偷偷調用 Internet Explorer 的核心元件，而 Server 的安全機制預設會把外面所有的網站（ 包括 GitHub ）通通鎖死，當成大魔王在防防禦。</p>



<p class="wp-block-paragraph">既然系統的安全性指令一直擋路，我們就改用「 全指令、不調用 IE 網頁核心 」的方法來下載，直接繞過這個煩人的限制。</p>



<p class="wp-block-paragraph">請在 PowerShell 中執行這段全新的指令：<sup></sup></p>



<p class="wp-block-paragraph">PowerShell</p>



<pre class="wp-block-code"><code># 1. 強制開啟 TLS 1.2
&#91;Net.ServicePointManager]::SecurityProtocol = &#91;Net.SecurityProtocolType]::Tls12

# 2. 使用微軟官方下載中心（保證存在的 x64 穩定版網址）
$webClient = New-Object System.Net.WebClient
$url = "https://go.microsoft.com/fwlink/?linkid=2142258"
$output = "$env:USERPROFILE\Downloads\MsSqlCmdLnUtils.msi"
$webClient.DownloadFile($url, $output)

# 3. 執行安裝
Start-Process msiexec.exe -ArgumentList "/i `"$output`"" -Wait</code></pre>



<p class="wp-block-paragraph">這次執行完之後，畫面就不會再跳出那些紅色的警告。它會非常低調、靜悄悄地下載完成，接著直接彈出 sqlcmd 的安裝視窗。你同樣只要動動手指，跟著點下一步完成安裝即可。</p>



<p class="wp-block-paragraph">最後再次提醒，安裝完畢後，一樣要關掉並重新打開一個新的 PowerShell 或 cmd 視窗，系統的環境變數才會生效。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/07/%e8%a7%a3%e6%b1%ba-windows-server-%e4%b8%8b%e8%bc%89%e5%a4%b1%e6%95%97%ef%bc%81%e9%80%a3%e7%b7%9a%e6%84%8f%e5%a4%96%e9%97%9c%e9%96%89%e8%88%87%e5%ae%89%e5%85%a8%e5%b0%81%e9%8e%96%e7%9a%84%e5%85%a9/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>網路安全防護： 5 分鐘搞懂什麼是 PKCE</title>
		<link>https://stackoverflow.max-everyday.com/2026/07/pkce/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/07/pkce/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Fri, 03 Jul 2026 08:48:52 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8625</guid>

					<description><![CDATA[大家在網站或手機 App 登入帳號時，有沒有想過...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="572" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/PKCE_clean-1024x572.jpg?v=1783068523" alt="" class="wp-image-8626" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/PKCE_clean-1024x572.jpg?v=1783068523 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/PKCE_clean-600x335.jpg?v=1783068523 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/PKCE_clean-768x429.jpg?v=1783068523 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/PKCE_clean.jpg?v=1783068523 1376w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">大家在網站或手機 App 登入帳號時，有沒有想過，你的密碼和資料是怎麼在網路世界裡安全傳遞的？今天我們要來聊聊一個保護你我隱私的幕後功臣，它的名字叫 PKCE。</p>



<p class="wp-block-paragraph">PKCE 全名是 Proof Key for Code Exchange，大家通常把它讀作 Pixie（就是小精靈的那個英文發音）。它是 OAuth 2.0 這個登入標準機制的安全擴充功能。簡單來說，它就像是幫你的行動 App 或單頁式網頁應用程式（SPA）請了一位動態保鏢，專門防止壞人中途攔截你的登入憑證。</p>



<h3 class="wp-block-heading">PKCE 是如何運作的？</h3>



<p class="wp-block-paragraph">傳統的登入方式需要一組固定的秘密鑰匙（Client Secret），但如果把這把鑰匙藏在手機 App 或前端網頁裡，很容易就被厲害的駭客挖出來。 PKCE 的聰明之處，就在於它每次登入都用「臨時隨機抽樣」的方式，流程主要分為四個步驟：</p>



<ol start="1" class="wp-block-list">
<li>產生驗證碼： 你的手機 App 或網頁會在本地端隨機亂數產生一組高密度的字串，這組密碼學字串叫做 code_verifier。</li>



<li>生成挑戰碼： 接下來，系統會把這組字串拿去進行雜湊運算（通常是 SHA-256 演算法），再經過編碼轉換成另一組字串，叫做 code_challenge。</li>



<li>發送請求： 你的 App 會把這組挑戰碼和運算方法，一起送到官方的授權伺服器，大喊一聲「我要登入！」</li>



<li>驗證與核發： 當 App 拿到授權碼，準備跟伺服器換取真正能通行的 Token 時，必須交出第一步產生的原始驗證碼。伺服器會在後端用同樣的方法算一次，確認跟當初的挑戰碼一模一樣，才會點頭放行。</li>
</ol>



<h3 class="wp-block-heading">為什麼我們需要 PKCE ？</h3>



<p class="wp-block-paragraph">如果沒有 PKCE 的保護，當授權碼在網路跳轉回傳的途中，很容易被惡意軟體或中間人側錄攔截。駭客只要偷到這個授權碼，就能假冒你的身份去跟伺服器換取存取權限。</p>



<p class="wp-block-paragraph">網頁後端伺服器因為躲在暗處，可以安全地藏好秘密鑰匙，但手機 App 是公開在外的客戶端，根本沒辦法安全地藏東西。 PKCE 透過每一次登入時「動態出題、動態對答案」的機制，確保發起請求和最後拿 Token 的是同一個合法的 App。這樣一來，壞人就算在半路偷到授權碼，沒有原始的驗證碼也只能乾瞪眼，完全無法盜用！</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">工程師的私房補丁日誌： AppScan 安全漏洞大作戰</h2>



<p class="wp-block-paragraph">看完了上面的小精靈防護機制，順便來瞧瞧我們這次為了應付 AppScan 安全檢查，在後端默默做了哪些升級。這絕對不是在敷衍檢查，這是為了愛與和平。</p>



<h3 class="wp-block-heading">核心變更項目</h3>



<p class="wp-block-paragraph">關於 PKCE 漏洞修補，我們擴充了 OAuthProvider 介面，新增了 Supports PKCE() bool 與 Exchange(&#8230;oauth2.AuthCodeOption) 功能。現在 Entra ID 會乖乖回傳 true ，而交大 NYCU 則會回傳 false 。登入處理器會自動產生一組驗證碼並發送 S256 挑戰碼，回呼處理器則會負責把驗證碼傳給 Exchange 進行核對。</p>



<p class="wp-block-paragraph">關於 Cookie 檔安全，我們在前一次的提交中，就已經把驗證 Cookie 檔通通綁上 SameSite=Strict 機制了。</p>



<p class="wp-block-paragraph">關於 API 安全標頭，我們加入了 AddAPISecurityHeaders 中介軟體，強制執行以下防禦：</p>



<ul class="wp-block-list">
<li>Cross-Origin-Resource-Policy 設為 same-origin</li>



<li>Strict-Transport-Security 設為 max-age=31536000; includeSubDomains</li>



<li>Cache-Control 設為 no-store 搭配 Pragma 設為 no-cache</li>



<li>Referrer-Policy 設為 strict-origin-when-cross-origin</li>
</ul>



<h3 class="wp-block-heading">AppScan 漏洞修補成績單</h3>



<p class="wp-block-paragraph">以下是這次的戰果，我們把所有紅字都變成綠色的勾勾了。</p>



<ul class="wp-block-list">
<li>OAuth Implicit Grant 缺 PKCE 漏洞： 中級風險，修復狀態為已完成。</li>



<li>Cookie 的 SameSite 設定不當： 中級風險，修復狀態為已完成。</li>



<li>API 缺少 CORP 標頭： 中級風險，修復狀態為已完成。</li>



<li>可快取的 SSL 網頁： 低級風險，修復狀態為已完成，透過 Cache-Control 防禦。</li>



<li>缺少 Referrer-Policy 標頭： 低級風險，修復狀態為已完成。</li>



<li>缺少 HSTS 標頭： 低級風險，修復狀態為已完成。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">Addresses AppScan security findings for the backend.</p>



<h3 class="wp-block-heading">Changes</h3>



<ul class="wp-block-list">
<li><strong>PKCE (OAuth Implicit Grant flaw)</strong>: OAuthProvider interface extended with SupportsPKCE() bool and Exchange(&#8230;oauth2.AuthCodeOption). Entra ID returns rue; NYCU returns alse. Login handler generates a verifier and sends S256 challenge; Callback handler passes verifier to Exchange.</li>



<li><strong>SameSite=Strict</strong>: Auth cookies already fixed in previous commit (SameSiteStrictMode).</li>



<li><strong>API Security Headers</strong> (AddAPISecurityHeaders middleware):
<ul class="wp-block-list">
<li>Cross-Origin-Resource-Policy: same-origin</li>



<li>Strict-Transport-Security: max-age=31536000; includeSubDomains</li>



<li>Cache-Control: no-store + Pragma: no-cache</li>



<li>Referrer-Policy: strict-origin-when-cross-origin</li>
</ul>
</li>
</ul>



<h3 class="wp-block-heading">AppScan Findings Addressed</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Finding</th><th>Severity</th><th>Status</th></tr></thead><tbody><tr><td>OAuth Implicit Grant (missing PKCE)</td><td>M</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Fixed</td></tr><tr><td>Cookie SameSite improper</td><td>M</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Fixed</td></tr><tr><td>CORP header missing on API</td><td>M</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Fixed</td></tr><tr><td>Cacheable SSL pages</td><td>L</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Fixed (Cache-Control: no-store)</td></tr><tr><td>Missing Referrer-Policy</td><td>L</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Fixed</td></tr><tr><td>Missing HSTS</td><td>L</td><td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Fixed</td></tr></tbody></table></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/07/pkce/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
