

<?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-stackoverflow &#8211; Max的程式語言筆記</title>
	<atom:link href="https://stackoverflow.max-everyday.com/author/max-stackoverflow/feed/" rel="self" type="application/rss+xml" />
	<link>https://stackoverflow.max-everyday.com</link>
	<description>我要當一個豬頭，快樂過每一天</description>
	<lastBuildDate>Wed, 08 Jul 2026 06:51:33 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.1</generator>

<image>
	<url>https://stackoverflow.max-everyday.com/wp-content/uploads/2017/02/max-stackoverflow-256.png</url>
	<title>max-stackoverflow &#8211; Max的程式語言筆記</title>
	<link>https://stackoverflow.max-everyday.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<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 fetchpriority="high" 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="(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 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="(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>
		<item>
		<title>讓 ＺＡＰ掃瞄登入管理後台的指南</title>
		<link>https://stackoverflow.max-everyday.com/2026/07/zap-backend-gui/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/07/zap-backend-gui/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Thu, 02 Jul 2026 10:39:47 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8621</guid>

					<description><![CDATA[要讓 ＯＷＡＳＰ ＺＡＰ 成功掃描登入後的管理介...]]></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/07/zap-backend-gui_clean-1024x572.jpg?v=1782988780" alt="" class="wp-image-8622" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/zap-backend-gui_clean-1024x572.jpg?v=1782988780 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/zap-backend-gui_clean-600x335.jpg?v=1782988780 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/zap-backend-gui_clean-768x429.jpg?v=1782988780 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/07/zap-backend-gui_clean.jpg?v=1782988780 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">要讓 ＯＷＡＳＰ ＺＡＰ 成功掃描登入後的管理介面，最關鍵的任務就是讓 ＺＡＰ 掌握身分驗證機制，不然它爬到一半被系統踢登出，掃描就等於白做了。</p>



<p class="wp-block-paragraph">在 Ｄｏｃｋｅｒ 環境中執行 ＺＡＰ，主要有兩種方式，一種是透過瀏覽器操作圖形介面，另一種是直接用指令跑自動化掃描。既然需要設定登入資訊這麼複雜的操作，強烈建議使用第一種圖形介面，操作起來就跟在本機裝軟體一模一樣。</p>



<p class="wp-block-paragraph">以下是完整的設定與執行步驟。</p>



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



<h2 class="wp-block-heading">方法一：啟動 Web Swing 圖形介面（推薦，方便設定登入）</h2>



<p class="wp-block-paragraph">Docker 版本的 ZAP 內建了 Web Swing 功能，可以把 GUI 畫面渲染到網頁上。</p>



<h3 class="wp-block-heading">1. 執行 Docker 指令</h3>



<p class="wp-block-paragraph">打開你的終端機（Terminal 或 PowerShell），輸入以下指令：</p>



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



<pre class="wp-block-code"><code>docker run -u zap -p 8080:8080 -p 8090:8090 -i owasp/zap2twig:latest zap-webswing.sh
</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4cc.png" alt="📌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>參數解析：</strong></p>



<ul class="wp-block-list">
<li><code>-p 8080:8080</code>：ZAP Web 界面的連接埠（透過瀏覽器訪問）。</li>



<li><code>-p 8090:8090</code>：ZAP 本身的 Proxy 連接埠。</li>



<li><code>owasp/zap2twig:latest</code>：這是包含完整週邊工具的 ZAP 映像檔。</li>
</ul>
</blockquote>



<h3 class="wp-block-heading">2. 開啟瀏覽器操作</h3>



<p class="wp-block-paragraph">指令跑起來後，打開你本機的瀏覽器，輸入以下網址：</p>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f449.png" alt="👉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <code>http://localhost:8080/zap/</code></p>



<p class="wp-block-paragraph">你就會在網頁裡看到完整的 ZAP 操作視窗。接下來，你就可以按照前面教的步驟去設定 Context、Authentication 和 Users。</p>



<h2 class="wp-block-heading">方法二：使用命令列 (CLI) 進行自動化掃描</h2>



<p class="wp-block-paragraph">如果你已經把登入的設定檔匯出成 <code>.context</code> 檔案，或者管理介面可以單靠 Token 驗證，你也可以直接用指令讓 Docker 裡的 ZAP 跑完就結束。</p>



<p class="wp-block-paragraph">執行 Baseline Scan（基本弱點掃描）指令：</p>



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



<pre class="wp-block-code"><code>docker run -t owasp/zap2twig:latest zap-baseline.py -t https://your-target-site.com
</code></pre>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Docker 環境下的關鍵注意事項（非常重要）</h2>



<p class="wp-block-paragraph">當 ZAP 住在 Docker 容器裡時，有兩個常見的「坑」需要注意：</p>



<h3 class="wp-block-heading">1. 目標網站也在你的本機 (Localhost) 嗎？</h3>



<p class="wp-block-paragraph">如果你的管理介面網站也是用 Docker 跑在本機，或是跑在本機的 <code>http://localhost:3000</code>：</p>



<ul class="wp-block-list">
<li><strong>不能</strong>在 ZAP 裡面填 <code>localhost</code>，因為對 ZAP 容器來說，<code>localhost</code> 指的是 ZAP 自己。</li>



<li>解決方法：將網址改為 <code>http://host.docker.internal:3000/</code>，這樣 ZAP 才能穿透容器存取到你本機的服務。</li>
</ul>



<h3 class="wp-block-heading">2. 資料如何儲存與匯出？</h3>



<p class="wp-block-paragraph">Docker 容器一旦關閉，裡面的設定和掃描報告就會消失。如果你想把掃描報告存回本機電腦，必須掛載資料夾（Volume）：</p>



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



<pre class="wp-block-code"><code># 在指令中加入 -v 參數，將本機的資料夾與容器內同步
docker run -u zap -v /path/to/your/local/dir:/home/zap/reports:rw -p 8080:8080 -i owasp/zap2twig:latest zap-webswing.sh
</code></pre>



<p class="wp-block-paragraph">你的管理介面網站目前是部署在雲端測試機，還是也同樣跑在你的本機 Docker 環境中呢？</p>



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



<p class="wp-block-paragraph">要讓 OWASP ZAMP (Zed Attack Proxy) 成功掃描「登入後」的管理介面，核心關鍵在於<strong>讓 ZAP 掌握身分驗證（Authentication）機制</strong>，這樣它才不會在掃描到一半時因 Session 過期而被踢登出。</p>



<p class="wp-block-paragraph">以下是實現這個目標最完整、最推崇的設定流程：</p>



<h2 class="wp-block-heading">步驟一：使用 Context（上下文）鎖定目標</h2>



<ol start="1" class="wp-block-list">
<li>打開 ZAP，在左側的 <strong>Sites</strong> 樹狀目錄中，找到你的網站。</li>



<li>右鍵點擊網站根目錄 -&gt; <strong>Include in Context</strong> -&gt; 選擇 <strong>New Context</strong>（或預設的 Default Context）。</li>



<li>這會打開 Context 設定視窗。在 <strong>In Scope</strong> 頁面中，確保你的網站 URL 已被勾選。</li>
</ol>



<h2 class="wp-block-heading">步驟二：錄製登入步驟並設定登入指標</h2>



<p class="wp-block-paragraph">ZAP 需要知道「怎麼登入」以及「怎麼判斷目前是登入還是登出狀態」。</p>



<h3 class="wp-block-heading">1. 錄製登入請求</h3>



<ul class="wp-block-list">
<li>建議使用 ZAP 內建的瀏覽器（點擊右上角的瀏覽器圖示，如 Chrome/Firefox）手動操作一次登入。</li>



<li>登入成功後，回到 ZAP 的 <strong>History</strong> 標籤頁。</li>



<li>找到那筆發送帳號密碼的 <code>POST</code> 請求（通常是 <code>/login</code> 或 <code>/auth</code>）。</li>



<li>右鍵點擊該請求 -&gt; <strong>Flag as Context</strong> -&gt; 選擇你的 Context -&gt; <strong>Form-based Auth Login Request</strong>。</li>
</ul>



<h3 class="wp-block-heading">2. 設定登出指標（Logged Out Indicator）</h3>



<p class="wp-block-paragraph">ZAP 在掃描時如果看到特定的字眼，就知道自己被踢登出了（必須重新登入）。</p>



<ul class="wp-block-list">
<li>在剛剛內建瀏覽器的畫面上，找到「登出後才會出現的文字」（例如：<code>請先登入</code>、<code>Login</code>、<code>帳號或密碼錯誤</code>）。</li>



<li>在 ZAP 的 Context 設定視窗中，切換到 <strong>Authentication</strong> 頁面。</li>



<li>將驗證方法改為 <strong>Form-based Authentication</strong>（如果剛剛有正確 Flag，這裡會自動帶入登入 URL 和參數）。</li>



<li>在下方 <strong>Logged Out Indicator</strong> 欄位中，填入剛剛找到的登出關鍵字（支援正則表達式，例如：<code>.*請先登入.*</code>）。</li>
</ul>



<h2 class="wp-block-heading">步驟三：設定使用者憑證（Users）</h2>



<p class="wp-block-paragraph">讓 ZAP 知道要用哪組帳密去登入管理介面。</p>



<ol start="1" class="wp-block-list">
<li>在 Context 設定視窗中，點選 <strong>Users</strong> 頁面。</li>



<li>點擊 <strong>Add</strong> 新增一個使用者（例如：<code>admin</code>）。</li>



<li>勾選 <strong>Enabled</strong>，並輸入管理員的 <code>Username</code> 與 <code>Password</code>。</li>
</ol>



<h2 class="wp-block-heading">步驟四：設定強制使用者模式（Forced User Mode）</h2>



<p class="wp-block-paragraph">這是最關鍵的一步，強迫 ZAP 的所有掃描工具（包含 Spider 和 Active Scan）都帶著這個身分去跑。</p>



<ol start="1" class="wp-block-list">
<li>在 Context 設定視窗中，點選 <strong>Forced User</strong> 頁面。</li>



<li>在下拉選單中，選擇你剛剛在 步驟三 建立的那個使用者（<code>admin</code>）。</li>



<li>點擊 <strong>OK</strong> 儲存並關閉 Context 設定視窗。</li>



<li><strong>啟用強制模式</strong>：在 ZAP 主介面上方的工具列，找到一個「鎖頭搭配一個小人」的圖示（Forced User Mode），把它點擊下去（讓它呈現鎖定/啟用的狀態）。</li>
</ol>



<h2 class="wp-block-heading">步驟五：開始安全掃描</h2>



<p class="wp-block-paragraph">現在 ZAP 已經具備自動登入的能力了，你可以開始掃描管理介面：</p>



<ol start="1" class="wp-block-list">
<li><strong>Spider（蜘蛛爬蟲）</strong>：
<ul class="wp-block-list">
<li>在左側 Sites 樹狀目錄，右鍵點擊管理介面的資料夾（例如 <code>/admin</code>）。</li>



<li>選擇 <strong>Attack</strong> -&gt; <strong>Spider&#8230;</strong>。</li>



<li>在彈出的視窗中，<strong>User</strong> 欄位記得選擇你設定的 <code>admin</code>，然後點擊 <strong>Start Scan</strong>。這時 ZAP 就會帶著登入身分去爬取所有管理員才能看到的頁面。</li>
</ul>
</li>



<li><strong>Active Scan（主動掃描/弱點掃描）</strong>：
<ul class="wp-block-list">
<li>爬蟲結束後，一樣右鍵點擊該目錄 -&gt; <strong>Attack</strong> -&gt; <strong>Active Scan&#8230;</strong>。</li>



<li>同樣確保 <strong>User</strong> 選擇 <code>admin</code>，點擊 <strong>Start Scan</strong>，ZAP 就會開始對管理介面進行漏洞狂轟猛炸。</li>
</ul>
</li>
</ol>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><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;" /> <strong>進階小撇步 (如果管理介面有驗證碼/雙因子認證 CAPTCHA / 2FA)：</strong></p>



<p class="wp-block-paragraph">自動化工具通常無法破解驗證碼。如果遇到這種情況，建議改用 <strong>Replacer 功能</strong> 或 <strong>Scripting</strong> 直接把手動登入後取得的 <code>Cookie</code> 或 <code>Bearer Token</code> 塞給 ZAP 瀏覽器，但缺點是 Token 過期就必須手動更新。</p>
</blockquote>



<p class="wp-block-paragraph">你在設定 Form-based Auth 的過程中，有遇到特定的登入阻礙（例如前端加密、Token 驗證）嗎？</p>



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



<p class="wp-block-paragraph">GUI 很麻煩, 改用 terminal 比較方便</p>



<p class="wp-block-paragraph">Shell Tail second dry-run log 35 lines<br>Found Java version 17.0.19<br>Available memory: 15863 MB<br>Using JVM args: -Xmx3965m<br>307 [main] INFO org.parosproxy.paros.Constant &#8211; Copying default configuration to /home/zap/.ZAP/config.xml<br>381 [main] INFO org.parosproxy.paros.Constant &#8211; Creating directory /home/zap/.ZAP/session<br>382 [main] INFO org.parosproxy.paros.Constant &#8211; Creating directory /home/zap/.ZAP/dirbuster<br>382 [main] INFO org.parosproxy.paros.Constant &#8211; Creating directory /home/zap/.ZAP/fuzzers<br>383 [main] INFO org.parosproxy.paros.Constant &#8211; Creating directory /home/zap/.ZAP/plugin<br>Jul 06, 2026 3:33:16 AM java.util.prefs.FileSystemPreferences$1 run<br>INFO: Created user preferences directory.<br>Downloading add-on from:<br>https://github.com/zaproxy/zap-extensions/releases/download/commonlib-v1.42.0/commonlib-release-1.42.0.zap<br>Downloading add-on from:<br>https://github.com/zaproxy/zap-extensions/releases/download/authhelper-v0.40.0/authhelper-beta-0.40.0.zap<br>Downloading add-on from:<br>https://github.com/zaproxy/zap-extensions/releases/download/selenium-v15.51.0/selenium-release-15.51.0.zap<br>Add-on downloaded to: /home/zap/.ZAP/plugin/authhelper-beta-0.40.0.zap<br>Add-on downloaded to: /home/zap/.ZAP/plugin/commonlib-release-1.42.0.zap<br>Add-on downloaded to: /home/zap/.ZAP/plugin/selenium-release-15.51.0.zap</p>



<p class="wp-block-paragraph">Tasks<br>&#8212;<br>│ │ Subagents<br>│ No subagents<br>│<br>│ Shell Commands<br>│ ❯ ◎ Run ZAP full scan in background, logging output [detached background]</p>



<p class="wp-block-paragraph">改用 AI 代勞進行 scan 超方便, 完全不需要去學習如何使用.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/07/zap-backend-gui/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>門神阿福的點名簿： WAF 防火牆 IP 白名單維護指南</title>
		<link>https://stackoverflow.max-everyday.com/2026/06/waf-white-ip-list/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/06/waf-white-ip-list/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Fri, 26 Jun 2026 03:47:23 +0000</pubDate>
				<category><![CDATA[Azure 筆記]]></category>
		<category><![CDATA[azure]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8609</guid>

					<description><![CDATA[嗨，各位路過的探險家！今天來聊聊我們網站的超級門...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="559" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/waf-white-ip-list_clean-1024x559.jpg?v=1782445631" alt="" class="wp-image-8611" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/waf-white-ip-list_clean-1024x559.jpg?v=1782445631 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/waf-white-ip-list_clean-600x327.jpg?v=1782445631 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/waf-white-ip-list_clean-768x419.jpg?v=1782445631 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/waf-white-ip-list_clean.jpg?v=1782445631 1408w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">嗨，各位路過的探險家！今天來聊聊我們網站的超級門神 ── WAF 防火牆。</p>



<p class="wp-block-paragraph">這個系統就像是一個戒備森嚴的城堡，為了不讓壞人進來搗亂，門神阿福手上有一張點名簿，也就是所謂的白名單。只要你的 IP 位址不在這張點名簿上，阿福就會直接把你塞進小黑屋，連大門口都進不來。</p>



<p class="wp-block-paragraph">最厲害的是，阿福非常有個性，他只保護特定的城堡。就算隔壁的系統不小心被圍攻，我們 Project Name 這邊依然穩如泰山。</p>



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



<h2 class="wp-block-heading">阿福的點名簿現況</h2>



<p class="wp-block-paragraph">目前只有拿到特別通行證的網段才能順利通行：</p>



<ul class="wp-block-list">
<li>某神秘機房專區： 192.168.1.0/24</li>



<li>核心團隊的秘密基地： 192.168.2.0/24</li>



<li>測試專用通道： 192.168.3.0/24</li>



<li>管理員的個人特權 IP： 203.0.113.89</li>
</ul>



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



<h2 class="wp-block-heading">前置作業：請出管理員鑰匙</h2>



<p class="wp-block-paragraph">在指揮阿福修改點名簿之前，請先確認你已經切換到正確的管理帳號，不然阿福是絕對不會理你的。</p>



<p class="wp-block-paragraph">指令如下：</p>



<p class="wp-block-paragraph">az account set &#8211;subscription &#8220;請輸入你的雲端帳號萬用鑰匙代碼&#8221;</p>



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



<h2 class="wp-block-heading">新增特權 IP 的四大步驟</h2>



<p class="wp-block-paragraph">假設今天有兩組好朋友，一組是 192.168.99.0/24 的新同學，另一組是 198.51.100.6 的外包夥伴，想要加入點名簿。請跟著以下步驟做：</p>



<h3 class="wp-block-heading">步驟 1：偷看目前的點名簿</h3>



<p class="wp-block-paragraph">先把阿福現在手上到底登記了誰調查清楚。</p>



<p class="wp-block-paragraph">az network application-gateway waf-policy custom-rule show &#8211;policy-name 門神名稱 &#8211;resource-group 城堡名稱 &#8211;name 規則名稱 &#8211;query &#8220;matchConditions[0].matchValues&#8221; -o json</p>



<h3 class="wp-block-heading">步驟 2：大家排排站，準備新名單</h3>



<p class="wp-block-paragraph">請把剛剛查出來的舊名單複製出來，然後把新朋友的名字加進去，排成一列。</p>



<h3 class="wp-block-heading">步驟 3：大手一揮，整張蓋過去</h3>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>超級重要：</strong> 阿福很健忘，你不能只跟他說我要加兩個人，你要給他一張全新的完整名單，否則舊的人會被他通通忘光光。</p>
</blockquote>



<p class="wp-block-paragraph">我們會用一個腳本把新舊名單綁在一起，然後直接覆蓋：</p>



<p class="wp-block-paragraph">$allIPs = @(</p>



<p class="wp-block-paragraph">&#8220;192.168.1.0/24&#8221;,</p>



<p class="wp-block-paragraph">&#8220;192.168.2.0/24&#8221;,</p>



<p class="wp-block-paragraph">&#8220;192.168.3.0/24&#8221;,</p>



<p class="wp-block-paragraph">&#8220;203.0.113.89&#8221;,</p>



<p class="wp-block-paragraph">&#8220;192.168.99.0/24&#8221;, # 這是新朋友一號</p>



<p class="wp-block-paragraph">&#8220;198.51.100.6&#8221; # 這是新朋友二號</p>



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



<p class="wp-block-paragraph">（接下來把這串名單打包成 JSON 格式後，用雲端指令送給阿福更新。因為指令比較長，這裡就不贅述，重點是名單要帶齊。）</p>



<h3 class="wp-block-heading">步驟 4：檢查阿福有沒有認真工作</h3>



<p class="wp-block-paragraph">更新完之後，再次叫阿福把名單拿出來核對，看看剛才加的新朋友是不是都在上面了。</p>



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



<h2 class="wp-block-heading">如果想踢掉某個 IP 呢</h2>



<p class="wp-block-paragraph">方法完全一樣。先去步驟 1 看名單，在步驟 2 的時候拿橡皮擦把不想看到的人擦掉，最後用步驟 3 的覆蓋大法，這個人就被阿福除名了。</p>



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



<h2 class="wp-block-heading">實地測試：看看阿福有沒有偷懶</h2>



<p class="wp-block-paragraph">名單改完之後，我們一定要來測試看看阿福是不是真的有在認真抓壞人：</p>



<ul class="wp-block-list">
<li><strong>叫沒拿通行證的朋友點網址：</strong> 如果畫面跳出 403 錯誤，代表被阿福成功攔截，阿福好棒。</li>



<li><strong>自己人從白名單點網址：</strong> 如果正常顯示網頁，代表通行無阻。</li>
</ul>



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



<h2 class="wp-block-heading">筆記小重點</h2>



<ul class="wp-block-list">
<li><strong>隔壁鄰居很安全：</strong> 這個點名簿設定只針對我們 Project Name ，完全不會影響到同一個伺服器底下的其他系統。</li>



<li><strong>覆蓋完請稍等：</strong> 阿福收到新名單後，大概需要 1 到 2 分鐘的時間來認熟面孔，請給他一點時間。</li>



<li><strong>附贈隱形護盾：</strong> 除了不讓陌生人進來，阿福同時還開啟了國際防禦標準，一般的網路流氓就算換了白名單 IP 進來，只要手腳不乾淨一樣會被抓走喔。</li>
</ul>



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



<p class="wp-block-paragraph">如果你想使用 Azure Portal 網頁畫面來操作，步驟非常直覺，請跟著以下步驟點選。</p>



<p class="wp-block-paragraph"><strong>第一步、登入與尋找策略</strong></p>



<p class="wp-block-paragraph">首先打開瀏覽器登入 Azure Portal 網站。在網頁最上方的搜尋欄輸入 Web Application Firewall policies 或是 WAF 策略，然後點選進入。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="705" height="982" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-30-18-37-ca.jpg?v=1782815930" alt="" class="wp-image-8614" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-30-18-37-ca.jpg?v=1782815930 705w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-30-18-37-ca-431x600.jpg?v=1782815930 431w" sizes="auto, (max-width: 705px) 100vw, 705px" /></figure>



<p class="wp-block-paragraph"><strong>第二步、選擇特定的策略</strong></p>



<p class="wp-block-paragraph">在列表中找到並點選你正在使用的 WAF 策略名稱。</p>



<p class="wp-block-paragraph"><strong>第三步、進入自訂規則</strong></p>



<p class="wp-block-paragraph">進入該策略的頁面後，查看左側的選單，在設定區塊中，點選自訂規則。</p>



<p class="wp-block-paragraph"><strong>第四步、修改名單</strong></p>



<p class="wp-block-paragraph">在自訂規則的列表中，點選你用來管制 IP 的那一條規則。點開之後，你會看到比對條件，找到比對變數為 RemoteAddr 的那一項，你就可以直接在下方的數值列表中，新增或刪除你要異動的 IP 位址。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="453" height="1024" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-30-18-39-cb-453x1024.jpg?v=1782816010" alt="" class="wp-image-8617" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-30-18-39-cb-453x1024.jpg?v=1782816010 453w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-30-18-39-cb-266x600.jpg?v=1782816010 266w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-30-18-39-cb.jpg?v=1782816010 468w" sizes="auto, (max-width: 453px) 100vw, 453px" /></figure>



<p class="wp-block-paragraph"><strong>第五步、儲存更新</strong></p>



<p class="wp-block-paragraph">修改完成後，點選畫面最上方的儲存按鈕。系統大約需要 1 到 2 分鐘的時間將新名單同步到防火牆。</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/06/waf-white-ip-list/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>一目了然的 Azure NSG 設定：多 Port 阻擋與特定 IP 放行的完美組合</title>
		<link>https://stackoverflow.max-everyday.com/2026/06/azure-nsg-port-ip-list/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/06/azure-nsg-port-ip-list/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Fri, 26 Jun 2026 03:31:20 +0000</pubDate>
				<category><![CDATA[Azure 筆記]]></category>
		<category><![CDATA[azure]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8606</guid>

					<description><![CDATA[在 Azure 的網路安全性群組（NSG）世界裡...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1024" height="572" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/9251883175766280132_clean.jpg?v=1782444672" alt="" class="wp-image-8607" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/9251883175766280132_clean.jpg?v=1782444672 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/9251883175766280132_clean-600x335.jpg?v=1782444672 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/9251883175766280132_clean-768x429.jpg?v=1782444672 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">在 Azure 的網路安全性群組（NSG）世界裡，有時候我們會遇到一個很像「既要又要」的邊緣狀況：你想要同時把好幾個 Port 關起來，不讓壞人進來，但同時又希望特定幾位好朋友（白名單 IP）可以大搖大擺地走進去。</p>



<p class="wp-block-paragraph">要同時做到阻擋大家又放行自己人，最乾淨也最聰明的作法，就是利用兩條規則的「優先順序（Priority）」差來玩一場網路版的邏輯遊戲。</p>



<p class="wp-block-paragraph">因為 Azure NSG 這個門神在檢查規則時，是採取「由上到下」比對的。數字越小的規則越優先執行，而且只要一比對成功，它就會直接定案，懶得再看下面的規則了。</p>



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



<h2 class="wp-block-heading">核心邏輯大公開</h2>



<p class="wp-block-paragraph">簡單來說，你只需要左右開弓，建立以下兩條規則：</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><td><strong>目的連接埠</strong></td><td><strong>動作</strong></td><td><strong>門神心裡話</strong></td></tr></thead><tbody><tr><td>Allow-Whitelist-to-Ports</td><td>300</td><td>你的白名單 IP</td><td>80, 443, 8080</td><td>Allow（允許）</td><td>是好朋友拿著通行證來了！這幾個 Port 給你過！</td></tr><tr><td>Deny-All-to-Ports</td><td>301</td><td>Any（任何人）</td><td>80, 443, 8080</td><td>Deny（拒絕）</td><td>剩下的閒雜人等注意，只要想碰這幾個 Port，通通給我滾！</td></tr></tbody></table></figure>



<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 重要提醒：允許（Allow）規則的 Priority 數字（例如 300）必須小於阻擋（Deny）規則的 Priority 數字（例如 301）。如果把阻擋數字寫得太小，門神就會先把大家都趕走，你的好朋友就再也進不來了。</p>



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



<h2 class="wp-block-heading">詳細設定步驟（Azure Portal 傳送門操作）</h2>



<p class="wp-block-paragraph">請直接移駕到你的 NSG （azure vmptstgjpe001-nsg）的「輸入安全性規則（Inbound security rules）」頁面，深呼吸之後點選「新增（Add）」：</p>



<h3 class="wp-block-heading">步驟 1：建立白名單允許規則</h3>



<ol start="1" class="wp-block-list">
<li>Source（來源）：下拉選單請選 IP Addresses。</li>



<li>Source IP addresses（來源 IP 位址）：輸入你想放行的白名單 IP。如果好朋友有點多，可以用半形逗號隔開，例如： 1.1.1.1, 2.2.2.0/24。</li>



<li>Source port ranges（來源連接埠範圍）：維持預設的 * 號就好。</li>



<li>Destination（目的地）：選 Any 或是直接指定那一台 VM 的內網 IP。</li>



<li>Destination port ranges（目的連接埠範圍）：輸入你想控制的多個 Port，中間記得用半形逗號隔開，例如： 80, 443, 8080。</li>



<li>Protocol（通訊協定）：看你心情與需求，選 TCP、UDP 或是 Any。</li>



<li>Action（動作）：大方地選擇 Allow。</li>



<li>Priority（優先順序）：給它一個比較小的數字，例如 300。</li>



<li>Name（名稱）：取一個好辨識的名字，像是 Allow_Whitelist_MultiPorts。</li>
</ol>



<h3 class="wp-block-heading">步驟 2：建立其餘全部阻擋規則</h3>



<p class="wp-block-paragraph">再次點選「新增（Add）」來建立第二道關卡：</p>



<ol start="1" class="wp-block-list">
<li>Source（來源）：這次要選 Any，代表任何路人甲乙丙。</li>



<li>Source port ranges：一樣維持 * 號。</li>



<li>Destination：選 Any。</li>



<li>Destination port ranges：請輸入與步驟 1 完全一模一樣的多個 Port，例如： 80, 443, 8080。</li>



<li>Protocol：請與步驟 1 保持同步。</li>



<li>Action（動作）：這次要狠下心選擇 Deny。</li>



<li>Priority（優先順序）：數字必須比步驟 1 還要大，例如 301。</li>



<li>Name（名稱）：給它一個威武的名字，像是 Deny_All_MultiPorts。</li>
</ol>



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



<h2 class="wp-block-heading">進階管理小技巧</h2>



<p class="wp-block-paragraph">如果你的白名單 IP 常常像天氣一樣變來變去，或者你手底下的 VM 越來越多，每次都要手動來改 NSG 規則真的會讓人想砸鍵盤。</p>



<p class="wp-block-paragraph">這時候建議可以利用「應用程式安全性群組（Application Security Group, ASG）」或者 Azure 的「IP Groups」功能。這就像是把所有好朋友的名字直接打包進一個群組懶人包，以後 NSG 規則的來源直接指定這個群組就可以了，管理起來輕鬆到可以提早下班！</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/06/azure-nsg-port-ip-list/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>如何申請google oauth</title>
		<link>https://stackoverflow.max-everyday.com/2026/06/apply-google-oauth/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/06/apply-google-oauth/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Thu, 25 Jun 2026 09:34:38 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8601</guid>

					<description><![CDATA[申請 Google OAuth 其實不難，主要是...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">申請 Google OAuth 其實不難，主要是在 <strong>Google Cloud Console</strong> 中完成。不論你是要開發網站登入、串接 Google Drive 還是發送 Gmail，流程都大同小異。</p>



<p class="wp-block-paragraph">以下為你整理出最流暢的申請 4 大步驟：</p>



<h3 class="wp-block-heading">第一步：建立新專案</h3>



<ol start="1" class="wp-block-list">
<li>前往 <a href="https://console.cloud.google.com/" target="_blank" rel="noreferrer noopener">Google Cloud Console</a>。</li>



<li>登入你的 Google 帳號。</li>



<li>點擊左上角的專案下拉選單，選擇 <strong>「新建專案」 (New Project)</strong>。</li>



<li>輸入專案名稱（例如：<code>MyAwesomeApp</code>），然後點擊 <strong>「建立」</strong>。</li>
</ol>



<h3 class="wp-block-heading">第二步：設定 OAuth 同意畫面 (OAuth Consent Screen)</h3>



<p class="wp-block-paragraph">這是使用者在登入你的 App 時會看到的授權畫面，必須先設定它。</p>



<ol start="1" class="wp-block-list">
<li>點擊左側選單的 <strong>「API 和服務」 > 「OAuth 同意畫面」</strong>。</li>



<li>選擇 <strong>User Type</strong>：
<ul class="wp-block-list">
<li><strong>外部 (External)：</strong> 開放給所有 Google 帳號（一般應用程式選這個）。</li>



<li><strong>內部 (Internal)：</strong> 僅限你 Google Workspace 組織內的成員（企業內部工具選這個）。</li>
</ul>
</li>



<li>填寫基本資料：<strong>應用程式名稱</strong>、<strong>使用者支援電子郵件</strong> 以及 <strong>開發人員聯絡資訊</strong>，完成後點擊「儲存並繼續」。</li>



<li><strong>範圍 (Scopes)：</strong> 設定你想拿到的權限（如 <code>email</code>, <code>profile</code>）。初次測試可以先不選，直接跳過。</li>



<li><strong>測試使用者 (Test Users)：</strong> 如果你的專案還在「測試中」階段，<strong>必須</strong>在這裡新增你自己的測試 Email，否則其他帳號會無法登入。</li>
</ol>



<h3 class="wp-block-heading">第三步：建立憑證 (Credentials) 取得密鑰</h3>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="496" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-25-17-16-c1-1024x496.jpg?v=1782379301" alt="" class="wp-image-8602" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-25-17-16-c1-1024x496.jpg?v=1782379301 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-25-17-16-c1-600x291.jpg?v=1782379301 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-25-17-16-c1-768x372.jpg?v=1782379301 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-25-17-16-c1-1536x744.jpg?v=1782379301 1536w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/chrome_2026-06-25-17-16-c1.jpg?v=1782379301 1754w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">這一步會生成你的 <code>Client ID</code> 和 <code>Client Secret</code>。</p>



<ol start="1" class="wp-block-list">
<li>點擊左側選單的 <strong>「憑證」 (Credentials)</strong>。</li>



<li>點擊上方的 <strong>「+ 建立憑證」 > 「OAuth 用戶端 ID」</strong>。</li>



<li>選擇你的 <strong>應用程式類型</strong>（例如：網頁應用程式、Android、iOS 等）。</li>



<li><strong>填寫核心網址（以網頁應用程式為例）：</strong>
<ul class="wp-block-list">
<li><strong>已授權的 JavaScript 來源：</strong> 你的網站首頁（例如：<code>http://localhost:3000</code> 或 <code>https://yourdomain.com</code>）。</li>



<li><strong>已授權的重新導向 URI：</strong> 使用者登入成功後，Google 把 Code 送回你網站的後端路由（例如：<code>http://localhost:3000/auth/google/callback</code>）。<em>※ 注意：這裡一定要設定精準，否則會噴 <code>redirect_uri_mismatch</code> 錯誤。</em></li>
</ul>
</li>



<li>點擊 <strong>「建立」</strong>。</li>
</ol>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><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;" /> <strong>恭喜！</strong> 畫面上會跳出你的 <strong>用戶端 ID (Client ID)</strong> 和 <strong>用戶端密鑰 (Client Secret)</strong>。請立刻複製並妥善保管，<strong>密鑰絕對不能外流</strong>！</p>
</blockquote>



<h3 class="wp-block-heading">第四步：切換為「生產模式」（選填）</h3>



<p class="wp-block-paragraph">在開發測試階段，你的專案狀態會是「測試中 (Testing)」，只有你在第二步設定的測試小夥伴能登入，且可能會有不安全應用的警告。</p>



<ul class="wp-block-list">
<li>當你的 App 準備好上線時，回到 <strong>「OAuth 同意畫面」</strong>，點擊 <strong>「發布應用程式」 (Publish App)</strong> 切換為正式生產模式。</li>



<li><em>備註：如果你申請了敏感權限（如讀取使用者的 Gmail 私訊），Google 會需要人工審查，可能需要幾天到幾週的時間。</em></li>
</ul>



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



<p class="wp-block-paragraph">我有測試環境 domain name  與正式環境 domain name 要申請2組 OAuth 2.0 用戶端 ID 嗎? 還是同一組ID即可。</p>



<p class="wp-block-paragraph"><strong>建議申請「2 組獨立的 OAuth 2.0 用戶端 ID (Client ID)」。</strong></p>



<p class="wp-block-paragraph">雖然在技術上，Google 允許你在同一組 Client ID 的「已授權的重新導向 URI」中，同時填入測試環境與正式環境的網址，但<strong>非常強烈建議分開申請</strong>。原因如下：</p>



<h3 class="wp-block-heading">1. 安全性考量（最重要的原因）</h3>



<ul class="wp-block-list">
<li><strong>憑證外流風險：</strong> 測試環境通常防護較弱，可能會有較多開發人員、測試人員接觸到設定檔。如果測試環境的 Client Secret（用戶端密鑰）不小心外流，且你兩邊用同一組 ID，<strong>攻擊者就能利用外流的密鑰去染指或偽造正式環境的登入請求</strong>。</li>



<li><strong>網址白名單污染：</strong> 萬一測試環境網址被惡意劫持或存在漏洞，它與正式環境共享同一個 OAuth 信任圈，會帶來額外的安全隱患。</li>
</ul>



<h3 class="wp-block-heading">2. Google 審查與「發布狀態」限制</h3>



<p class="wp-block-paragraph">Google Cloud 專案的 OAuth 同意畫面有分兩種狀態：</p>



<ul class="wp-block-list">
<li><strong>測試中 (Testing)：</strong> 只有白名單內的「測試使用者」可以登入，且登入時會跳出「未經驗證的應用程式」警告。</li>



<li><strong>生產模式 (In Production)：</strong> 開放給所有 Google 帳號登入。如果使用了敏感權限（Scopes），還需要通過 Google 的人工驗證。</li>
</ul>



<p class="wp-block-paragraph"><strong>如果你只用一組：</strong></p>



<p class="wp-block-paragraph">當你的專案為了正式環境切換成「生產模式」並送交審查時，如果你的網址白名單裡還夾帶了 <code>project-name.test.example.com.tw</code>（而且通常測試環境在外網無法被 Google 審查官順利連上），<strong>這可能會導致你的 Google 審查被拒絕 (Rejected)</strong>，因為 Google 要求生產模式下的網址必須是公開、真實且符合規範的。</p>



<h3 class="wp-block-heading">最佳實作做法 (Best Practice)</h3>



<p class="wp-block-paragraph">為了架構清晰與安全，你可以選擇以下兩種配置方式：</p>



<h4 class="wp-block-heading">方案 A：在同一個 Google Cloud 專案下，建立 2 個 Client ID（推薦，最方便）</h4>



<p class="wp-block-paragraph">在同一個專案的「憑證」頁面中，點擊兩次「建立憑證 &gt; OAuth 用戶端 ID」：</p>



<ul class="wp-block-list">
<li><strong>第一組 ID（命名：Project Name &#8211; Test）：</strong>
<ul class="wp-block-list">
<li>重新導向 URI 填：<code>https://projectname.test.example.com.tw/oauth2/callback</code></li>
</ul>
</li>



<li><strong>第二組 ID（命名：Project Name &#8211; Prod）：</strong>
<ul class="wp-block-list">
<li>重新導向 URI 填：<code>https://projectname.example.com.tw/oauth2/callback</code></li>
</ul>
</li>
</ul>



<p class="wp-block-paragraph"><em>這樣你在程式碼的環境變數（<code>.env</code>）中，測試機就讀取測試的 ID/Secret，正式機就讀取正式的 ID/Secret，彼此互不干擾。</em></p>



<h4 class="wp-block-heading">方案 B：建立 2 個獨立的 Google Cloud 專案（最嚴謹）</h4>



<ul class="wp-block-list">
<li>專案一：<code>PorjectName-Test</code>（專案狀態保持在 Testing，方便開發隨時測試）</li>



<li>專案二：<code><code>PorjectName</code>-Prod</code>（專案狀態切換為 Production，專門用來跑正式上線與 Google 審查流程）</li>
</ul>



<p class="wp-block-paragraph"><strong>總結：</strong> 為了維護系統的安全防線與後續維護的便利性，請務必為測試與正式環境分開申請獨立的憑證！</p>



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



<p class="wp-block-paragraph">應用程式是哪種類型, Vue 3 + Vite</p>



<p class="wp-block-paragraph">既然你的前端技術棧是 <strong>Vue 3 + Vite</strong>，在架構設計上你首先需要釐清一個關鍵問題：<strong>你的應用程式有沒有後端（Backend，如 Node.js, Python, Go）？</strong></p>



<p class="wp-block-paragraph">這會直接決定你如何實作 Google OAuth。以下為你說明這兩種情境與實作方式：</p>



<h3 class="wp-block-heading">情境一：有後端（推薦，最安全）</h3>



<p class="wp-block-paragraph">這是最正統的做法。Vue 3 只負責顯示「登入按鈕」，使用者點擊後會引導到 Google 登入，登入成功後 Google 會把驗證碼（Authorization Code）傳給你的<strong>後端</strong>，由後端去跟 Google 交換 Token，並簽發你網站自己的 JWT 給 Vue 3。</p>



<p class="wp-block-paragraph">在這種模式下，你的 <strong>OAuth 2.0 用戶端 ID 類型</strong>要選擇：<strong>「網頁應用程式」 (Web Application)</strong>。</p>



<h4 class="wp-block-heading">Vite / Vue 3 前端實作步驟：</h4>



<p class="wp-block-paragraph">目前社群上最推崇、封裝最完整的套件是 <code>vue3-google-signin</code>。</p>



<p class="wp-block-paragraph"><strong>安裝套件：</strong>Bash</p>



<pre class="wp-block-code"><code><code>npm install vue3-google-signin</code></code></pre>



<p class="wp-block-paragraph"><strong>在 <code>main.js</code> 或 <code>main.ts</code> 中初始化：</strong></p>



<pre class="wp-block-code"><code>import { createApp } from 'vue'
import App from './App.vue'
import GoogleSignInPlugin from 'vue3-google-signin'

const app = createApp(App)

app.use(GoogleSignInPlugin, {
  // Vite 的環境變數必須以 VITE_ 開頭
  clientId: import.meta.env.VITE_GOOGLE_CLIENT_ID,
})

app.mount('#app')</code></pre>



<p class="wp-block-paragraph"><strong>在畫面上建立登入按鈕 (<code>Login.vue</code>)：</strong></p>



<pre class="wp-block-code"><code>&lt;script setup>
import { GoogleLogin } from 'vue3-google-signin'

const handleLoginSuccess = (response) => {
  // 這裡會拿到 Google 回傳的 code 或 credential
  console.log("Google 登入成功:", response);

  // 【關鍵】將這個 response (含 code) 用 axios 傳送給你的後端
  // axios.post('https://projectname.example.come.tw/api/auth/google', { code: response.code })
}

const handleLoginError = () => {
  console.error("Google 登入失敗")
}
&lt;/script>

&lt;template>
  &lt;div class="login-container">
    &lt;h2>入口網登入&lt;/h2>
    &lt;GoogleLogin :callback="handleLoginSuccess" :popup-type="'CODE'" @error="handleLoginError" />
  &lt;/div>
&lt;/template></code></pre>



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



<h3 class="wp-block-heading">情境二：純前端（SPA 單頁應用，無後端）</h3>



<p class="wp-block-paragraph">如果你的 Vue 3 是一個完全靜態的網站，沒有任何自己的後端資料庫，單純只想確認「他是誰」或者想在前端呼叫 Google API（例如讀取他的 Google Calendar）。</p>



<p class="wp-block-paragraph">在這種模式下，你的 <strong>OAuth 2.0 用戶端 ID 類型</strong>一樣選擇：<strong>「網頁應用程式」</strong>。但流程會走「隱含授權 (Implicit Flow)」，直接在前端取得 <code>access_token</code>。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>安全警告：</strong> 因為純前端沒有秘密可言，你的 <code>Client Secret</code> 絕對不能寫在 Vue 3 的原始碼或 <code>.env</code> 裡（因為打包後任何人都能看光）。在 Google Console 建立憑證時，這種模式通常不需要用到 Client Secret。</p>
</blockquote>



<h4 class="wp-block-heading">前端設定差異：</h4>



<p class="wp-block-paragraph">如果你是用上面提到的 <code>vue3-google-signin</code>，在按鈕上就不要加 <code>:popup-type="'CODE'"</code>，它預設會直接在前端回傳一串 JWT（身分憑證），你可以直接解開它來取得使用者的 Email 和姓名：</p>



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



<pre class="wp-block-code"><code>import { decodeCredential } from 'vue3-google-signin'

const handleLoginSuccess = (response) =&gt; {
  // 直接在前端解密 Google 回傳的使用者資訊
  const userData = decodeCredential(response.credential)
  console.log("使用者資料:", userData) // 包含 email, name, picture
}
</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;" /> 搭配你上一題的測試/正式環境，Vite 該如何設定？</h3>



<p class="wp-block-paragraph">因為你有兩個環境，請在 Vue 3 專案根目錄建立兩個環境變數檔案：</p>



<ol start="1" class="wp-block-list">
<li><strong><code>.env.development</code></strong> (測試環境用)程式碼片段<code>VITE_GOOGLE_CLIENT_ID=你的測試環境_CLIENT_ID.apps.googleusercontent.com</code></li>



<li><strong><code>.env.production</code></strong> (正式環境用)程式碼片段<code>VITE_GOOGLE_CLIENT_ID=你的正式環境_CLIENT_ID.apps.googleusercontent.com</code></li>
</ol>



<p class="wp-block-paragraph">當你執行 <code>vite preview</code> 或 <code>vite build</code> 時，Vite 就會自動根據環境抓取正確的 Client ID，完美對應到正式環境與測試環境了！</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/06/apply-google-oauth/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>預覽 PlantUML 的 .puml 檔案</title>
		<link>https://stackoverflow.max-everyday.com/2026/06/preview-plant-uml-puml/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/06/preview-plant-uml-puml/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Thu, 25 Jun 2026 09:20:48 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8596</guid>

					<description><![CDATA[這裡幫大家整理了 4 個超實用的方法，不管是想要...]]></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/06/preview-plant-uml-puml_clean-1024x572.jpg?v=1782379243" alt="" class="wp-image-8599" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/preview-plant-uml-puml_clean-1024x572.jpg?v=1782379243 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/preview-plant-uml-puml_clean-600x335.jpg?v=1782379243 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/preview-plant-uml-puml_clean-768x429.jpg?v=1782379243 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/preview-plant-uml-puml_clean.jpg?v=1782379243 1376w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">這裡幫大家整理了 4 個超實用的方法，不管是想要神級的操作體驗，還是懶得安裝任何軟體的速成法，這篇通通有。</p>



<h2 class="wp-block-heading">方法 1 ： VS Code 神級流暢體驗（強烈推薦）</h2>



<p class="wp-block-paragraph">如果你平常就在用 Visual Studio Code 寫程式，這絕對是最頂的選擇，可以享受一邊打字、旁邊畫面一邊即時更新的快感。</p>



<ol start="1" class="wp-block-list">
<li>打開 VS Code ，到擴充功能市場搜尋並安裝 PlantUML 這個套件。</li>



<li>注意，這裡有一個重要的小跟班需要安裝。因為 PlantUML 畫圖需要 Graphviz 的協助，請打開命令提示字元輸入 winget install Graphviz 來快速搞定它。</li>



<li>如果真的懶得在電腦安裝 Graphviz 怎麼辦？也可以直接到 VS Code 的設定裡搜尋 plantuml.server ，把它改成連到官方的線上伺服器來幫你畫圖。</li>



<li>萬事俱備後，打開 .puml 檔案，按下 Alt + D ，右邊就會跳出即時預覽畫面。</li>
</ol>



<h2 class="wp-block-heading">方法 2 ： JetBrains 宇宙（ IntelliJ 、 PyCharm 專屬）</h2>



<p class="wp-block-paragraph">如果你是 JetBrains 系列 IDE 的忠實粉絲，根本不用離開視窗，裡面就自帶強大功能。</p>



<ol start="1" class="wp-block-list">
<li>點選 File 進入 Settings ，接著找到 Plugins 區塊。</li>



<li>搜尋 PlantUML Integration 並點擊安裝。</li>



<li>把 IDE 重新啟動。</li>



<li>接下來只要打開任何 .puml 檔案，右邊就會自動長出專屬的預覽視窗，完全不用操心。</li>
</ol>



<h2 class="wp-block-heading">方法 3 ： 獨立 Java 檔案（免安裝開發工具）</h2>



<p class="wp-block-paragraph">如果不想為了看張圖就開啟笨重的開發工具，也可以用官方提供的輕量級 Java 小工具。</p>



<ol start="1" class="wp-block-list">
<li>請先確保電腦裡有安裝 Java 執行環境。</li>



<li>到 <a href="https://plantuml.com/" target="_blank" rel="noreferrer noopener">PlantUML 官方網站</a> 下載 plantuml.jar 檔案。</li>



<li>打開命令提示字元，切換到下載的資料夾，輸入 java -jar plantuml.jar -gui 這一行指令。</li>



<li>畫面上就會跳出一個小巧的視窗，只要選好放 .puml 檔案的資料夾，每次只要存檔，它就會自動幫你刷新圖片。</li>
</ol>



<h2 class="wp-block-heading">方法 4 ： 線上免安裝（終極懶人法）</h2>



<p class="wp-block-paragraph">如果只是臨時要看一下檔案，連一秒鐘都不想浪費在安裝上，那就直接交給瀏覽器吧。</p>



<ul class="wp-block-list">
<li>官方線上伺服器：直接把程式碼複製貼上到 <a href="https://gemini.google.com/www.plantuml.com/plantuml" target="_blank" rel="noreferrer noopener">PlantUML Web Server</a> 就能立刻看到結果。</li>



<li>網頁編輯器： <a href="https://gemini.google.com/planttext.com" target="_blank" rel="noreferrer noopener">PlantText</a> 是一個非常受歡迎的線上即時編輯器，畫面好看而且專為 PlantUML 設計，非常適合臨時救急。</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/06/preview-plant-uml-puml/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>在使用 Windows 系統 ssh: UNPROTECTED PRIVATE KEY FILE!</title>
		<link>https://stackoverflow.max-everyday.com/2026/06/%e5%9c%a8%e4%bd%bf%e7%94%a8-windows-%e7%b3%bb%e7%b5%b1-ssh-unprotected-private-key-file/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/06/%e5%9c%a8%e4%bd%bf%e7%94%a8-windows-%e7%b3%bb%e7%b5%b1-ssh-unprotected-private-key-file/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 24 Jun 2026 09:25:29 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<category><![CDATA[ssh]]></category>
		<category><![CDATA[Windows]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8593</guid>

					<description><![CDATA[在 Windows 執行 ssh 顯示錯誤訊息 ...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1024" height="572" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/16307345641785880220.jpg?v=1782293092" alt="" class="wp-image-8594" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/16307345641785880220.jpg?v=1782293092 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/16307345641785880220-600x335.jpg?v=1782293092 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/06/16307345641785880220-768x429.jpg?v=1782293092 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">在 Windows 執行 ssh  顯示錯誤訊息</p>



<pre class="wp-block-preformatted">C:\Users\max&gt;ssh 4.216.213.123<br>Bad permissions. Try removing permissions for user: BUILTIN\\Users (S-1-5-32-545) on file C:/Users/max/.ssh/vm-max-pc-key.pem.<br>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<br>@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @<br>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<br>Permissions for 'C:\\Users\\max/.ssh/vm-max-pc-key.pem' are too open.<br>It is required that your private key files are NOT accessible by others.<br>This private key will be ignored.<br>Load key "C:\\Users\\max/.ssh/vm-max-pc-key.pem": bad permissions<br>azureuser@4.216.213.123: Permission denied (publickey).</pre>



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



<p class="wp-block-paragraph">在使用 Windows 系統連線到遠端伺服器的時候，是不是常常遇到 SSH 跟你鬧脾氣？</p>



<p class="wp-block-paragraph">畫面跳出一大串警告，寫著 WARNING UNPROTECTED PRIVATE KEY FILE ，然後無情地拒絕連線。這其實不是你的伺服器壞掉，而是 SSH 的安全機制太過傲嬌。它覺得你的金鑰檔案太過公開，像是把家裡鑰匙直接貼在大門口一樣危險，所以故意裝作不認識你。</p>



<p class="wp-block-paragraph">這個問題通常是因為金鑰檔案的權限開太大，導致系統內的其他使用者也能讀取。要解決這個尷尬的狀況，有兩種快速的方法可以馴服它。</p>



<p class="wp-block-paragraph">第一種方法是使用命令提示字元。</p>



<p class="wp-block-paragraph">請開啟命令提示字元，並依序輸入以下指令。記得將路徑換成自己的金鑰檔案路徑。</p>



<p class="wp-block-paragraph">第一步是取消繼承權限，指令為 </p>



<pre class="wp-block-code"><code>icacls C:\Users\your_username\.ssh\my_key.pem /inheritance:r</code></pre>



<p class="wp-block-paragraph">第二步是重新給予自己完整的控制權限，指令為 </p>



<pre class="wp-block-code"><code>icacls C:\Users\your_username\.ssh\my_key.pem /grant:r your_username:F </code></pre>



<p class="wp-block-paragraph">第二種方法是使用圖形介面，適合喜歡用滑鼠點擊的人。</p>



<p class="wp-block-paragraph">請打開檔案總管，找到金鑰檔案。在檔案上按右鍵選擇內容，切換到安全性標籤，接著點擊進階。</p>



<p class="wp-block-paragraph">在進階安全性設定視窗中，點擊停用繼承，並選擇將繼承的權限轉換為此物件的明確權限。</p>



<p class="wp-block-paragraph">最後在權限項目清單中，找到 Users 這個群組並將它移除，只留下自己的使用者帳號擁有存取權限。點擊套用並確定就完成了。</p>



<p class="wp-block-paragraph">設定完成之後，再次嘗試原本的連線指令，應該就能順利連上伺服器。如果還是失敗，可以嘗試在指令中明確加上減 i 參數，例如 </p>



<pre class="wp-block-code"><code>ssh -i C:\Users\your_username.ssh\my_key.pem user@1.2.3.4 </code></pre>



<p class="wp-block-paragraph">這樣就能成功登入了。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/06/%e5%9c%a8%e4%bd%bf%e7%94%a8-windows-%e7%b3%bb%e7%b5%b1-ssh-unprotected-private-key-file/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>把 unsafe-inline 從你的 CSP 裡面拿掉？ 醒醒吧，你的前端元件會直接罷工！</title>
		<link>https://stackoverflow.max-everyday.com/2026/06/%e6%8a%8a-unsafe-inline-%e5%be%9e%e4%bd%a0%e7%9a%84-csp-%e8%a3%a1%e9%9d%a2%e6%8b%bf%e6%8e%89%ef%bc%9f-%e9%86%92%e9%86%92%e5%90%a7%ef%bc%8c%e4%bd%a0%e7%9a%84%e5%89%8d%e7%ab%af%e5%85%83%e4%bb%b6/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/06/%e6%8a%8a-unsafe-inline-%e5%be%9e%e4%bd%a0%e7%9a%84-csp-%e8%a3%a1%e9%9d%a2%e6%8b%bf%e6%8e%89%ef%bc%9f-%e9%86%92%e9%86%92%e5%90%a7%ef%bc%8c%e4%bd%a0%e7%9a%84%e5%89%8d%e7%ab%af%e5%85%83%e4%bb%b6/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Mon, 22 Jun 2026 00:51:02 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8587</guid>

					<description><![CDATA[在網頁資安的攻防戰中，很多工程師在被資安掃描器（...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">在網頁資安的攻防戰中，很多工程師在被資安掃描器（ 例如 ZAP ）洗臉之後，看到報告上寫著大大的「 Medium 風險： style-src 包含 unsafe-inline 」，第一個直覺通常是：「 噢，那我就在 Content-Security-Policy 裡面把 unsafe-inline 拿掉不就得了？」</p>



<p class="wp-block-paragraph">如果你現在用的是 Vue 3 搭配 Element Plus，而你在拿掉的瞬間，你的網頁基本上就半身不遂了。</p>



<ul class="wp-block-list">
<li>短期（建議）： 維持 Element Plus，接受 unsafe-inline Medium 風險，專注業務功能。</li>



<li>中期： 若資安掃描壓力大，評估 Naive UI 替換（相同 Vue 生態、學習成本低）。</li>



<li>長期： 新專案直接選 React + shadcn/ui 或 React + Mantine，避免此問題。</li>
</ul>



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



<h3 class="wp-block-heading">兩大資安大哉問： 到底能不能直接拔掉？</h3>



<p class="wp-block-paragraph">我們先把問題拆成兩個層面來看。</p>



<h4 class="wp-block-heading">1. 在 FastAPI 設定 CSP 跟在 Nginx 設定，有差嗎？</h4>



<p class="wp-block-paragraph">答案是： 完全沒有差別。</p>



<p class="wp-block-paragraph">瀏覽器是很單純的，它只看最後收到的 HTTP response header。 不管這個資安政策是後端框架 FastAPI 吐出來的，還是前端大門 Nginx 塞給它的，對瀏覽器來說都長得一樣。</p>



<p class="wp-block-paragraph">目前專案只有在 FastAPI 設定，那就繼續留在那邊就好，不需要特地搬家。</p>



<h4 class="wp-block-heading">2. 移除 unsafe-inline 之後，Element Plus 還能動嗎？</h4>



<p class="wp-block-paragraph">答案是： 絕對會壞掉，而且壞得很難看。</p>



<p class="wp-block-paragraph">如果你的 CSP 政策直接移除了 unsafe-inline，下面這些畫面上最亮眼的主角會集體罷工：</p>



<ul class="wp-block-list">
<li><strong>el-select / el-dropdown</strong>： 下拉選單直接迷路，位置飛到九霄雲外或是乾脆隱形。</li>



<li><strong>el-tooltip / el-popover</strong>： 滑鼠移過去原本該跳出的提示，現在比你的前任還要冷淡，完全沒反應。</li>



<li><strong>el-dialog</strong>： 彈出視窗直接彈歪，跟你的預期完全對不上。</li>



<li><strong>el-table 固定列</strong>： 說好要固定的欄位，現在放飛自我跟著一起滾動。</li>
</ul>



<p class="wp-block-paragraph">為什麼會這樣？ 因為這些元件在運作時，JavaScript 會在幕後瘋狂計算畫面的座標，然後直接把 <code>style="top: 200px; left: 150px;"</code> 這種類型的行內樣式塞進 HTML 標籤裡。</p>



<p class="wp-block-paragraph">當你把 unsafe-inline 拿掉，瀏覽器就會化身鐵面判官，只要看到這種行內樣式，一律當成非法入侵直接封鎖。 結果就是你的 CSP 報告看起來滿分，但使用者的畫面看起來像一場災難。</p>



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



<h3 class="wp-block-heading">那難道無解了嗎？ 現代資安的標準解法</h3>



<p class="wp-block-paragraph">好消息是，我們不需要為了資安把整個 Element Plus 換掉（ 畢竟換框架是會出人命的 ）。 在 CSP Level 3 的現代標準中，資安政策已經被細分得更聰明了。</p>



<p class="wp-block-paragraph">以前的 <code>style-src</code> 是一刀切，但現在我們可以把「 標籤 」和「 屬性 」分開管理。</p>



<p class="wp-block-paragraph">你可以試試看這套完美的資安配置：</p>



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



<pre class="wp-block-code"><code>Content-Security-Policy: style-src-elem 'self' https://cdnjs.cloudflare.com; style-src-attr 'unsafe-inline';
</code></pre>



<p class="wp-block-paragraph">這段設定的意思其實就是跟瀏覽器講悄悄話：</p>



<ul class="wp-block-list">
<li><strong>style-src-elem &#8216;self&#8217;</strong>： 只要有人想用 <code>&lt;style&gt;</code> 標籤或外掛惡意 CSS 檔案來劫持網頁，一律給我擋掉！ 這招直接封鎖了最危險的 CSS 注入攻擊。</li>



<li><strong>style-src-attr &#8216;unsafe-inline&#8217;</strong>： 至於 HTML 標籤裡面的 <code>style="..."</code> 屬性，我們就開個特權給它過吧。</li>
</ul>



<h4 class="wp-block-heading">改前 vs 改後 戰力分析</h4>



<p class="wp-block-paragraph">這樣改到底差在哪裡？ 我們直接看對比：</p>



<ul class="wp-block-list">
<li><strong><code>&lt;style&gt;</code> 注入攻擊</strong>： 改前是允許的（ 超危險 ），改後直接封鎖（ 安全 ）。</li>



<li><strong>外掛惡意 CSS 檔案</strong>： 改前是允許的（ 超危險 ），改後直接封鎖（ 安全 ）。</li>



<li><strong>Element Plus 動態定位</strong>： 改前正常，改後依然正常運作。</li>



<li><strong>資安掃描器的警告</strong>： 改前會跳出黃牌警告，改後警告直接消失。</li>
</ul>



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



<h3 class="wp-block-heading">結論</h3>



<p class="wp-block-paragraph">想兼顧資安跟漂亮的 UI，不需要直接把 unsafe-inline 趕盡殺絕。</p>



<p class="wp-block-paragraph">透過 <code>style-src-elem</code> 和 <code>style-src-attr</code> 的分工合作，你既能把大門關緊防範黑客，又能留個小窗讓 Element Plus 的元件繼續正常跳舞。 這才是現代工程師優雅的解決之道。</p>



<p class="wp-block-paragraph">結論上來說，改用 </p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/06/%e6%8a%8a-unsafe-inline-%e5%be%9e%e4%bd%a0%e7%9a%84-csp-%e8%a3%a1%e9%9d%a2%e6%8b%bf%e6%8e%89%ef%bc%9f-%e9%86%92%e9%86%92%e5%90%a7%ef%bc%8c%e4%bd%a0%e7%9a%84%e5%89%8d%e7%ab%af%e5%85%83%e4%bb%b6/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CSP (Content Security Policy) 的網站要如何安全的使用 Inline script</title>
		<link>https://stackoverflow.max-everyday.com/2026/06/csp-content-security-policy-%e7%9a%84%e7%b6%b2%e7%ab%99%e8%a6%81%e5%a6%82%e4%bd%95%e5%ae%89%e5%85%a8%e7%9a%84%e4%bd%bf%e7%94%a8-inline-script/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/06/csp-content-security-policy-%e7%9a%84%e7%b6%b2%e7%ab%99%e8%a6%81%e5%a6%82%e4%bd%95%e5%ae%89%e5%85%a8%e7%9a%84%e4%bd%bf%e7%94%a8-inline-script/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 09:01:22 +0000</pubDate>
				<category><![CDATA[css筆記]]></category>
		<category><![CDATA[css]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8583</guid>

					<description><![CDATA[當你在幫網站做健康檢查，看到弱掃報告（ 像是 Z...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">當你在幫網站做健康檢查，看到弱掃報告（ 像是 ZAP ）跳出一個橘黃色的 Medium 警告，寫著 <code>style-src 'unsafe-inline'</code> 或是發現 <code>script-src</code> 滿是漏洞時，心裡是不是頓時涼了一半？</p>



<p class="wp-block-paragraph">簡單來說，如果你的 CSP （ 網頁安全政策 ）開了 <code>unsafe-inline</code> ，就等於是在大門裝了密碼鎖，卻把密碼用便利貼貼在門口一樣。駭客只要找到機會塞一段惡意腳本（ XSS 攻擊 ），你的網站就直接變成他的遊樂場。</p>



<p class="wp-block-paragraph">但是偏偏像是 Google Tag Manager （ GTM ）這種必裝的分析工具，官方給的程式碼裡面就是有一大段 Inline Script ：</p>



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



<pre class="wp-block-code"><code>&lt;script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"&gt;&lt;/script&gt;
&lt;script&gt;
  window.dataLayer = window.dataLayer || &#91;];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXXXXX');
&lt;/script&gt;
</code></pre>



<p class="wp-block-paragraph">既想要防禦 XSS 攻擊，又不能把 GTM <sup></sup>拔掉，這時候你有兩條路可以走，讓我們來看看這兩個相愛相殺的解決方案。</p>



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



<h3 class="wp-block-heading">方案一：使用 Nonce （ 一次性密碼鎖 ）</h3>



<p class="wp-block-paragraph">這個方法就像是去看演唱會，每次進場工作人員都會在你的手上蓋一個當天限定、過期失效的印章。</p>



<p class="wp-block-paragraph">在 CSP 標頭（ Header ）裡，你可以這樣設定：</p>



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



<pre class="wp-block-code"><code>Content-Security-Policy: script-src 'nonce-每天換的神秘亂數密碼'; img-src www.googletagmanager.com
</code></pre>



<p class="wp-block-paragraph">然後在你的網頁程式碼中，那段 GTM 的標籤也必須帶上相同的密碼：</p>



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



<pre class="wp-block-code"><code>&lt;script nonce='每天換的神秘亂數密碼'&gt;
  // GTM 的扣（Code）放這裡
&lt;/script&gt;
</code></pre>



<h4 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;" /> 工程師真心話</h4>



<ul class="wp-block-list">
<li><strong>優點</strong>：超級安全。因為駭客根本猜不到你下一個 Request 的亂數密碼是什麼，就算他成功塞了惡意指令，也會因為沒有通關密碼而被瀏覽器直接擋在門外。</li>



<li><strong>缺點（ 也就是坑 ）</strong>：這非常考驗後端的大腦。你的伺服器必須在「 每次 」收到請求時，都用高強度的加密演算法生出一組全新的隨機字串。最慘的是，你的網頁從此跟「 回應快取 」（ Response Cache ）說再見，因為一旦網頁被快取，密碼就不會變了，那這個鎖就形同虛設。</li>
</ul>



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



<h3 class="wp-block-heading">方案二：使用 Hash （ 驗收指紋認證 ）</h3>



<p class="wp-block-paragraph">如果你的網站是 SPA （ 單頁應用程式 ），或是部署在 GitHub Pages 這種根本沒有後端可以幫忙生密碼的靜態伺服器，那 Hash 就是你的救星。</p>



<p class="wp-block-paragraph">這個概念是，瀏覽器會直接去核對這段程式碼的「 指紋 」。</p>



<p class="wp-block-paragraph">步驟非常簡單，把 <code>&lt;script&gt;</code> 到 <code>&lt;/script&gt;</code> 之間的程式碼內容，丟到 <a target="_blank" rel="noreferrer noopener" href="https://stackoverflow.max-everyday.com/wp-admin/post.php?post=8583&amp;action=edit">SHA256 線上計算工具</a> 裡面去跑一下，會得到一串像是 <code>0bbd063b7...</code> 的雜湊值（ Hash ）。</p>



<p class="wp-block-paragraph">接著把這串指紋放進你的 CSP 設定裡：</p>



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



<pre class="wp-block-code"><code>Content-Security-Policy: script-src 'sha256-計算出來的那串指紋'; img-src www.googletagmanager.com
</code></pre>



<h4 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;" /> 工程師真心話</h4>



<ul class="wp-block-list">
<li><strong>優點</strong>：前端自己就能搞定，不需要後端伺服器配合，而且網頁依然可以開開心心地做快取，提升載入速度。</li>



<li><strong>缺點（ 最雷的地方 ）</strong>：
<ol start="1" class="wp-block-list">
<li><strong>字元敏感度破表</strong>：SHA256 是一個極度龜毛的演算法。只要你的程式碼裡面多了一個空格、少了一個換行，甚至不同工程師的電腦存檔時，斷行符號一個是 LF （ <code>\n</code> ）一個是 CRLF （ <code>\r\n</code> ），算出來的指紋就完全不一樣。指紋一對不上，你的 GTM 就會直接被 CSP 射下來。</li>



<li><strong>維護地獄</strong>：哪天行銷團隊突然要求修改 GTM 裡面的參數，只要動到一個字，你就必須重新計算一次 Hash 值、重新更新 CSP 標頭。如果交接沒做好，後續接手的工程師改了程式碼發現網站功能壞掉，可能要在電腦前哭著找兇手找很久。</li>
</ol>
</li>
</ul>



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



<h3 class="wp-block-heading">總結防禦指南</h3>



<p class="wp-block-paragraph">想要高枕無憂、後端架構也撐得住，請選 <strong>Nonce</strong> 。</p>



<p class="wp-block-paragraph">如果是靜態網站、追求速度與快取，請選 <strong>Hash</strong> ，但每次改動程式碼時，請記得去燒香拜拜順便更新指紋。</p>



<p class="wp-block-paragraph">總之，快把 <code>unsafe-inline</code> 從你的 CSP 裡面拿掉，別再讓你的網站裸奔了。</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/06/csp-content-security-policy-%e7%9a%84%e7%b6%b2%e7%ab%99%e8%a6%81%e5%a6%82%e4%bd%95%e5%ae%89%e5%85%a8%e7%9a%84%e4%bd%bf%e7%94%a8-inline-script/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
