[{"data":1,"prerenderedAt":1297},["ShallowReactive",2],{"navigation":3,"\u002Farticles\u002Fsecurity-best-practices":34,"\u002Farticles\u002Fsecurity-best-practices-surround":1292},[4],{"title":5,"path":6,"stem":7,"children":8,"page":33},"Articles","\u002Farticles","articles",[9,13,17,21,25,29],{"title":10,"path":11,"stem":12},"用 Daily Snapshot 提升統計查詢速度","\u002Farticles\u002Fdaily-snapshot","articles\u002Fdaily-snapshot",{"title":14,"path":15,"stem":16},"GKE 部署","\u002Farticles\u002Fgke-deployment","articles\u002Fgke-deployment",{"title":18,"path":19,"stem":20},"從 LLM 到 Agent：打通底層邏輯","\u002Farticles\u002Fllm-to-agent","articles\u002Fllm-to-agent",{"title":22,"path":23,"stem":24},"資訊安全實踐","\u002Farticles\u002Fsecurity-best-practices","articles\u002Fsecurity-best-practices",{"title":26,"path":27,"stem":28},"單機架構的性能優化","\u002Farticles\u002Fsingle-machine-performance","articles\u002Fsingle-machine-performance",{"title":30,"path":31,"stem":32},"伺服器渲染 SSR","\u002Farticles\u002Fssr","articles\u002Fssr",false,{"id":35,"title":22,"author":36,"body":40,"date":1284,"description":1285,"extension":1286,"externalUrl":1287,"image":1288,"meta":1289,"minRead":540,"navigation":137,"path":23,"seo":1290,"stem":24,"__hash__":1291},"blog\u002Farticles\u002Fsecurity-best-practices.md",{"name":37,"avatar":38},"Gary",{"src":39,"alt":37},"\u002Fimages\u002Fselfie.webp",{"type":41,"value":42,"toc":1261},"minimark",[43,47,52,56,183,189,210,214,217,248,251,254,258,261,351,355,358,410,415,423,426,487,489,493,496,544,546,549,552,590,595,610,612,616,620,623,762,766,773,883,885,888,892,994,998,1001,1044,1046,1049,1052,1172,1177,1188,1190,1194,1254,1257],[44,45,46],"h2",{"id":46},"身份驗證與授權",[48,49,51],"h3",{"id":50},"使用-jwt-的注意事項","使用 JWT 的注意事項",[53,54,55],"p",{},"JWT（JSON Web Token）是常見的無狀態驗證方案，但實作細節很容易踩坑。",[57,58,63],"pre",{"className":59,"code":60,"language":61,"meta":62,"style":62},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F ❌ 錯誤：演算法設為 none，任何人都能偽造 token\njwt.verify(token, secret, { algorithms: ['none'] })\n\n\u002F\u002F ✅ 正確：明確指定演算法\njwt.verify(token, secret, { algorithms: ['HS256'] })\n","ts","",[64,65,66,75,132,139,145],"code",{"__ignoreMap":62},[67,68,71],"span",{"class":69,"line":70},"line",1,[67,72,74],{"class":73},"sHwdD","\u002F\u002F ❌ 錯誤：演算法設為 none，任何人都能偽造 token\n",[67,76,78,82,86,90,93,96,99,101,104,108,111,114,117,121,123,126,129],{"class":69,"line":77},2,[67,79,81],{"class":80},"sTEyZ","jwt",[67,83,85],{"class":84},"sMK4o",".",[67,87,89],{"class":88},"s2Zo4","verify",[67,91,92],{"class":80},"(token",[67,94,95],{"class":84},",",[67,97,98],{"class":80}," secret",[67,100,95],{"class":84},[67,102,103],{"class":84}," {",[67,105,107],{"class":106},"swJcz"," algorithms",[67,109,110],{"class":84},":",[67,112,113],{"class":80}," [",[67,115,116],{"class":84},"'",[67,118,120],{"class":119},"sfazB","none",[67,122,116],{"class":84},[67,124,125],{"class":80},"] ",[67,127,128],{"class":84},"}",[67,130,131],{"class":80},")\n",[67,133,135],{"class":69,"line":134},3,[67,136,138],{"emptyLinePlaceholder":137},true,"\n",[67,140,142],{"class":69,"line":141},4,[67,143,144],{"class":73},"\u002F\u002F ✅ 正確：明確指定演算法\n",[67,146,148,150,152,154,156,158,160,162,164,166,168,170,172,175,177,179,181],{"class":69,"line":147},5,[67,149,81],{"class":80},[67,151,85],{"class":84},[67,153,89],{"class":88},[67,155,92],{"class":80},[67,157,95],{"class":84},[67,159,98],{"class":80},[67,161,95],{"class":84},[67,163,103],{"class":84},[67,165,107],{"class":106},[67,167,110],{"class":84},[67,169,113],{"class":80},[67,171,116],{"class":84},[67,173,174],{"class":119},"HS256",[67,176,116],{"class":84},[67,178,125],{"class":80},[67,180,128],{"class":84},[67,182,131],{"class":80},[53,184,185],{},[186,187,188],"strong",{},"常見錯誤：",[190,191,192,204,207],"ul",{},[193,194,195,196,199,200,203],"li",{},"將 JWT 存在 ",[64,197,198],{},"localStorage","，容易被 XSS 竊取，應改用 ",[64,201,202],{},"httpOnly"," Cookie",[193,205,206],{},"Token 有效期設太長，應配合 Refresh Token 機制縮短 Access Token 壽命",[193,208,209],{},"沒有實作 Token 撤銷機制，登出後 token 仍然有效",[48,211,213],{"id":212},"最小權限原則principle-of-least-privilege","最小權限原則（Principle of Least Privilege）",[53,215,216],{},"每個用戶、服務、資料庫帳號只給完成任務所需的最小權限。",[57,218,222],{"className":219,"code":220,"language":221,"meta":62,"style":62},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","-- ❌ 給 API 帳號完整資料庫權限\nGRANT ALL PRIVILEGES ON *.* TO 'api_user'@'%';\n\n-- ✅ 只給必要的讀寫權限\nGRANT SELECT, INSERT, UPDATE ON app_db.orders TO 'api_user'@'localhost';\n","sql",[64,223,224,229,234,238,243],{"__ignoreMap":62},[67,225,226],{"class":69,"line":70},[67,227,228],{},"-- ❌ 給 API 帳號完整資料庫權限\n",[67,230,231],{"class":69,"line":77},[67,232,233],{},"GRANT ALL PRIVILEGES ON *.* TO 'api_user'@'%';\n",[67,235,236],{"class":69,"line":134},[67,237,138],{"emptyLinePlaceholder":137},[67,239,240],{"class":69,"line":141},[67,241,242],{},"-- ✅ 只給必要的讀寫權限\n",[67,244,245],{"class":69,"line":147},[67,246,247],{},"GRANT SELECT, INSERT, UPDATE ON app_db.orders TO 'api_user'@'localhost';\n",[249,250],"hr",{},[44,252,253],{"id":253},"輸入驗證與防注入",[48,255,257],{"id":256},"sql-injection","SQL Injection",[53,259,260],{},"永遠不要用字串拼接組 SQL 查詢。",[57,262,264],{"className":59,"code":263,"language":61,"meta":62,"style":62},"\u002F\u002F ❌ 危險：攻擊者輸入 ' OR '1'='1 即可繞過驗證\nconst query = `SELECT * FROM users WHERE email = '${email}'`\n\n\u002F\u002F ✅ 使用參數化查詢\nconst query = 'SELECT * FROM users WHERE email = $1'\nawait db.query(query, [email])\n",[64,265,266,271,302,306,311,328],{"__ignoreMap":62},[67,267,268],{"class":69,"line":70},[67,269,270],{"class":73},"\u002F\u002F ❌ 危險：攻擊者輸入 ' OR '1'='1 即可繞過驗證\n",[67,272,273,277,280,283,286,289,292,295,297,299],{"class":69,"line":77},[67,274,276],{"class":275},"spNyl","const",[67,278,279],{"class":80}," query ",[67,281,282],{"class":84},"=",[67,284,285],{"class":84}," `",[67,287,288],{"class":119},"SELECT * FROM users WHERE email = '",[67,290,291],{"class":84},"${",[67,293,294],{"class":80},"email",[67,296,128],{"class":84},[67,298,116],{"class":119},[67,300,301],{"class":84},"`\n",[67,303,304],{"class":69,"line":134},[67,305,138],{"emptyLinePlaceholder":137},[67,307,308],{"class":69,"line":141},[67,309,310],{"class":73},"\u002F\u002F ✅ 使用參數化查詢\n",[67,312,313,315,317,319,322,325],{"class":69,"line":147},[67,314,276],{"class":275},[67,316,279],{"class":80},[67,318,282],{"class":84},[67,320,321],{"class":84}," '",[67,323,324],{"class":119},"SELECT * FROM users WHERE email = $1",[67,326,327],{"class":84},"'\n",[67,329,331,335,338,340,343,346,348],{"class":69,"line":330},6,[67,332,334],{"class":333},"s7zQu","await",[67,336,337],{"class":80}," db",[67,339,85],{"class":84},[67,341,342],{"class":88},"query",[67,344,345],{"class":80},"(query",[67,347,95],{"class":84},[67,349,350],{"class":80}," [email])\n",[48,352,354],{"id":353},"xsscross-site-scripting","XSS（Cross-Site Scripting）",[53,356,357],{},"對所有用戶輸入進行跳脫處理，避免注入惡意腳本。",[57,359,361],{"className":59,"code":360,"language":61,"meta":62,"style":62},"import DOMPurify from 'dompurify'\n\n\u002F\u002F ✅ 渲染用戶輸入的 HTML 前先清理\nconst clean = DOMPurify.sanitize(userInput)\n",[64,362,363,381,385,390],{"__ignoreMap":62},[67,364,365,368,371,374,376,379],{"class":69,"line":70},[67,366,367],{"class":333},"import",[67,369,370],{"class":80}," DOMPurify ",[67,372,373],{"class":333},"from",[67,375,321],{"class":84},[67,377,378],{"class":119},"dompurify",[67,380,327],{"class":84},[67,382,383],{"class":69,"line":77},[67,384,138],{"emptyLinePlaceholder":137},[67,386,387],{"class":69,"line":134},[67,388,389],{"class":73},"\u002F\u002F ✅ 渲染用戶輸入的 HTML 前先清理\n",[67,391,392,394,397,399,402,404,407],{"class":69,"line":141},[67,393,276],{"class":275},[67,395,396],{"class":80}," clean ",[67,398,282],{"class":84},[67,400,401],{"class":80}," DOMPurify",[67,403,85],{"class":84},[67,405,406],{"class":88},"sanitize",[67,408,409],{"class":80},"(userInput)\n",[53,411,412],{},[186,413,414],{},"HTTP Header 防護：",[57,416,421],{"className":417,"code":419,"language":420},[418],"language-text","Content-Security-Policy: default-src 'self'\nX-Content-Type-Options: nosniff\nX-Frame-Options: DENY\n","text",[64,422,419],{"__ignoreMap":62},[48,424,425],{"id":425},"環境變數管理",[57,427,431],{"className":428,"code":429,"language":430,"meta":62,"style":62},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# ❌ 絕對不能 commit 進 git\nDB_PASSWORD=mysecretpassword\nJWT_SECRET=abc123\n\n# ✅ 用 .env 並加入 .gitignore\necho \".env\" >> .gitignore\n","bash",[64,432,433,438,448,458,462,467],{"__ignoreMap":62},[67,434,435],{"class":69,"line":70},[67,436,437],{"class":73},"# ❌ 絕對不能 commit 進 git\n",[67,439,440,443,445],{"class":69,"line":77},[67,441,442],{"class":80},"DB_PASSWORD",[67,444,282],{"class":84},[67,446,447],{"class":119},"mysecretpassword\n",[67,449,450,453,455],{"class":69,"line":134},[67,451,452],{"class":80},"JWT_SECRET",[67,454,282],{"class":84},[67,456,457],{"class":119},"abc123\n",[67,459,460],{"class":69,"line":141},[67,461,138],{"emptyLinePlaceholder":137},[67,463,464],{"class":69,"line":147},[67,465,466],{"class":73},"# ✅ 用 .env 並加入 .gitignore\n",[67,468,469,472,475,478,481,484],{"class":69,"line":330},[67,470,471],{"class":88},"echo",[67,473,474],{"class":84}," \"",[67,476,477],{"class":119},".env",[67,479,480],{"class":84},"\"",[67,482,483],{"class":84}," >>",[67,485,486],{"class":119}," .gitignore\n",[249,488],{},[44,490,492],{"id":491},"https-與資料傳輸","HTTPS 與資料傳輸",[53,494,495],{},"所有生產環境流量必須走 HTTPS，並確保 TLS 設定正確。",[57,497,501],{"className":498,"code":499,"language":500,"meta":62,"style":62},"language-nginx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","server {\n    listen 443 ssl;\n    ssl_protocols TLSv1.2 TLSv1.3;   # 停用舊版 TLS\n    ssl_ciphers HIGH:!aNULL:!MD5;\n\n    # HSTS：強制瀏覽器只走 HTTPS\n    add_header Strict-Transport-Security \"max-age=31536000\" always;\n}\n","nginx",[64,502,503,508,513,518,523,527,532,538],{"__ignoreMap":62},[67,504,505],{"class":69,"line":70},[67,506,507],{},"server {\n",[67,509,510],{"class":69,"line":77},[67,511,512],{},"    listen 443 ssl;\n",[67,514,515],{"class":69,"line":134},[67,516,517],{},"    ssl_protocols TLSv1.2 TLSv1.3;   # 停用舊版 TLS\n",[67,519,520],{"class":69,"line":141},[67,521,522],{},"    ssl_ciphers HIGH:!aNULL:!MD5;\n",[67,524,525],{"class":69,"line":147},[67,526,138],{"emptyLinePlaceholder":137},[67,528,529],{"class":69,"line":330},[67,530,531],{},"    # HSTS：強制瀏覽器只走 HTTPS\n",[67,533,535],{"class":69,"line":534},7,[67,536,537],{},"    add_header Strict-Transport-Security \"max-age=31536000\" always;\n",[67,539,541],{"class":69,"line":540},8,[67,542,543],{},"}\n",[249,545],{},[44,547,548],{"id":548},"依賴管理",[53,550,551],{},"第三方套件是常見的攻擊入口，需要定期審查。",[57,553,555],{"className":428,"code":554,"language":430,"meta":62,"style":62},"# 掃描已知漏洞\nnpm audit\n\n# 自動修復低風險漏洞\nnpm audit fix\n",[64,556,557,562,571,575,580],{"__ignoreMap":62},[67,558,559],{"class":69,"line":70},[67,560,561],{"class":73},"# 掃描已知漏洞\n",[67,563,564,568],{"class":69,"line":77},[67,565,567],{"class":566},"sBMFI","npm",[67,569,570],{"class":119}," audit\n",[67,572,573],{"class":69,"line":134},[67,574,138],{"emptyLinePlaceholder":137},[67,576,577],{"class":69,"line":141},[67,578,579],{"class":73},"# 自動修復低風險漏洞\n",[67,581,582,584,587],{"class":69,"line":147},[67,583,567],{"class":566},[67,585,586],{"class":119}," audit",[67,588,589],{"class":119}," fix\n",[53,591,592],{},[186,593,594],{},"最佳實踐：",[190,596,597,604,607],{},[193,598,599,600,603],{},"鎖定套件版本（",[64,601,602],{},"package-lock.json"," 要 commit）",[193,605,606],{},"定期更新依賴，訂閱安全通報（如 GitHub Dependabot）",[193,608,609],{},"不引入不必要的套件，減少攻擊面",[249,611],{},[44,613,615],{"id":614},"api-安全","API 安全",[48,617,619],{"id":618},"rate-limiting","Rate Limiting",[53,621,622],{},"防止暴力破解與 DDoS。",[57,624,626],{"className":59,"code":625,"language":61,"meta":62,"style":62},"import rateLimit from 'express-rate-limit'\n\nconst loginLimiter = rateLimit({\n  windowMs: 15 * 60 * 1000, \u002F\u002F 15 分鐘\n  max: 10,                   \u002F\u002F 最多 10 次嘗試\n  message: '嘗試次數過多，請稍後再試'\n})\n\napp.post('\u002Fauth\u002Flogin', loginLimiter, loginHandler)\n",[64,627,628,644,648,666,693,708,722,728,732],{"__ignoreMap":62},[67,629,630,632,635,637,639,642],{"class":69,"line":70},[67,631,367],{"class":333},[67,633,634],{"class":80}," rateLimit ",[67,636,373],{"class":333},[67,638,321],{"class":84},[67,640,641],{"class":119},"express-rate-limit",[67,643,327],{"class":84},[67,645,646],{"class":69,"line":77},[67,647,138],{"emptyLinePlaceholder":137},[67,649,650,652,655,657,660,663],{"class":69,"line":134},[67,651,276],{"class":275},[67,653,654],{"class":80}," loginLimiter ",[67,656,282],{"class":84},[67,658,659],{"class":88}," rateLimit",[67,661,662],{"class":80},"(",[67,664,665],{"class":84},"{\n",[67,667,668,671,673,677,680,683,685,688,690],{"class":69,"line":141},[67,669,670],{"class":106},"  windowMs",[67,672,110],{"class":84},[67,674,676],{"class":675},"sbssI"," 15",[67,678,679],{"class":84}," *",[67,681,682],{"class":675}," 60",[67,684,679],{"class":84},[67,686,687],{"class":675}," 1000",[67,689,95],{"class":84},[67,691,692],{"class":73}," \u002F\u002F 15 分鐘\n",[67,694,695,698,700,703,705],{"class":69,"line":147},[67,696,697],{"class":106},"  max",[67,699,110],{"class":84},[67,701,702],{"class":675}," 10",[67,704,95],{"class":84},[67,706,707],{"class":73},"                   \u002F\u002F 最多 10 次嘗試\n",[67,709,710,713,715,717,720],{"class":69,"line":330},[67,711,712],{"class":106},"  message",[67,714,110],{"class":84},[67,716,321],{"class":84},[67,718,719],{"class":119},"嘗試次數過多，請稍後再試",[67,721,327],{"class":84},[67,723,724,726],{"class":69,"line":534},[67,725,128],{"class":84},[67,727,131],{"class":80},[67,729,730],{"class":69,"line":540},[67,731,138],{"emptyLinePlaceholder":137},[67,733,735,738,740,743,745,747,750,752,754,757,759],{"class":69,"line":734},9,[67,736,737],{"class":80},"app",[67,739,85],{"class":84},[67,741,742],{"class":88},"post",[67,744,662],{"class":80},[67,746,116],{"class":84},[67,748,749],{"class":119},"\u002Fauth\u002Flogin",[67,751,116],{"class":84},[67,753,95],{"class":84},[67,755,756],{"class":80}," loginLimiter",[67,758,95],{"class":84},[67,760,761],{"class":80}," loginHandler)\n",[48,763,765],{"id":764},"cors-設定","CORS 設定",[53,767,768,769,772],{},"不要用 ",[64,770,771],{},"*"," 允許所有來源。",[57,774,776],{"className":59,"code":775,"language":61,"meta":62,"style":62},"\u002F\u002F ❌ 允許所有來源\napp.use(cors({ origin: '*' }))\n\n\u002F\u002F ✅ 明確指定允許的來源\napp.use(cors({\n  origin: ['https:\u002F\u002Fyourdomain.com'],\n  credentials: true\n}))\n",[64,777,778,783,819,823,828,844,866,877],{"__ignoreMap":62},[67,779,780],{"class":69,"line":70},[67,781,782],{"class":73},"\u002F\u002F ❌ 允許所有來源\n",[67,784,785,787,789,792,794,797,799,802,805,807,809,811,813,816],{"class":69,"line":77},[67,786,737],{"class":80},[67,788,85],{"class":84},[67,790,791],{"class":88},"use",[67,793,662],{"class":80},[67,795,796],{"class":88},"cors",[67,798,662],{"class":80},[67,800,801],{"class":84},"{",[67,803,804],{"class":106}," origin",[67,806,110],{"class":84},[67,808,321],{"class":84},[67,810,771],{"class":119},[67,812,116],{"class":84},[67,814,815],{"class":84}," }",[67,817,818],{"class":80},"))\n",[67,820,821],{"class":69,"line":134},[67,822,138],{"emptyLinePlaceholder":137},[67,824,825],{"class":69,"line":141},[67,826,827],{"class":73},"\u002F\u002F ✅ 明確指定允許的來源\n",[67,829,830,832,834,836,838,840,842],{"class":69,"line":147},[67,831,737],{"class":80},[67,833,85],{"class":84},[67,835,791],{"class":88},[67,837,662],{"class":80},[67,839,796],{"class":88},[67,841,662],{"class":80},[67,843,665],{"class":84},[67,845,846,849,851,853,855,858,860,863],{"class":69,"line":330},[67,847,848],{"class":106},"  origin",[67,850,110],{"class":84},[67,852,113],{"class":80},[67,854,116],{"class":84},[67,856,857],{"class":119},"https:\u002F\u002Fyourdomain.com",[67,859,116],{"class":84},[67,861,862],{"class":80},"]",[67,864,865],{"class":84},",\n",[67,867,868,871,873],{"class":69,"line":534},[67,869,870],{"class":106},"  credentials",[67,872,110],{"class":84},[67,874,876],{"class":875},"sfNiH"," true\n",[67,878,879,881],{"class":69,"line":540},[67,880,128],{"class":84},[67,882,818],{"class":80},[249,884],{},[44,886,887],{"id":887},"基礎設施安全",[48,889,891],{"id":890},"kubernetes-gke","Kubernetes \u002F GKE",[57,893,897],{"className":894,"code":895,"language":896,"meta":62,"style":62},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# ✅ 不以 root 身份執行容器\nsecurityContext:\n  runAsNonRoot: true\n  runAsUser: 1000\n  readOnlyRootFilesystem: true\n\n# ✅ 限制資源，防止單一 Pod 吃光資源\nresources:\n  limits:\n    cpu: \"500m\"\n    memory: \"256Mi\"\n","yaml",[64,898,899,904,912,921,931,940,944,949,956,963,979],{"__ignoreMap":62},[67,900,901],{"class":69,"line":70},[67,902,903],{"class":73},"# ✅ 不以 root 身份執行容器\n",[67,905,906,909],{"class":69,"line":77},[67,907,908],{"class":106},"securityContext",[67,910,911],{"class":84},":\n",[67,913,914,917,919],{"class":69,"line":134},[67,915,916],{"class":106},"  runAsNonRoot",[67,918,110],{"class":84},[67,920,876],{"class":875},[67,922,923,926,928],{"class":69,"line":141},[67,924,925],{"class":106},"  runAsUser",[67,927,110],{"class":84},[67,929,930],{"class":675}," 1000\n",[67,932,933,936,938],{"class":69,"line":147},[67,934,935],{"class":106},"  readOnlyRootFilesystem",[67,937,110],{"class":84},[67,939,876],{"class":875},[67,941,942],{"class":69,"line":330},[67,943,138],{"emptyLinePlaceholder":137},[67,945,946],{"class":69,"line":534},[67,947,948],{"class":73},"# ✅ 限制資源，防止單一 Pod 吃光資源\n",[67,950,951,954],{"class":69,"line":540},[67,952,953],{"class":106},"resources",[67,955,911],{"class":84},[67,957,958,961],{"class":69,"line":734},[67,959,960],{"class":106},"  limits",[67,962,911],{"class":84},[67,964,966,969,971,973,976],{"class":69,"line":965},10,[67,967,968],{"class":106},"    cpu",[67,970,110],{"class":84},[67,972,474],{"class":84},[67,974,975],{"class":119},"500m",[67,977,978],{"class":84},"\"\n",[67,980,982,985,987,989,992],{"class":69,"line":981},11,[67,983,984],{"class":106},"    memory",[67,986,110],{"class":84},[67,988,474],{"class":84},[67,990,991],{"class":119},"256Mi",[67,993,978],{"class":84},[48,995,997],{"id":996},"secret-管理","Secret 管理",[53,999,1000],{},"不要把 secret 直接寫在 YAML 裡。",[57,1002,1004],{"className":428,"code":1003,"language":430,"meta":62,"style":62},"# ✅ 使用 Kubernetes Secret\nkubectl create secret generic db-secret \\\n  --from-literal=password=mysecretpassword\n\n# 或使用 Google Secret Manager \u002F AWS Secrets Manager\n",[64,1005,1006,1011,1030,1035,1039],{"__ignoreMap":62},[67,1007,1008],{"class":69,"line":70},[67,1009,1010],{"class":73},"# ✅ 使用 Kubernetes Secret\n",[67,1012,1013,1016,1019,1021,1024,1027],{"class":69,"line":77},[67,1014,1015],{"class":566},"kubectl",[67,1017,1018],{"class":119}," create",[67,1020,98],{"class":119},[67,1022,1023],{"class":119}," generic",[67,1025,1026],{"class":119}," db-secret",[67,1028,1029],{"class":80}," \\\n",[67,1031,1032],{"class":69,"line":134},[67,1033,1034],{"class":119},"  --from-literal=password=mysecretpassword\n",[67,1036,1037],{"class":69,"line":141},[67,1038,138],{"emptyLinePlaceholder":137},[67,1040,1041],{"class":69,"line":147},[67,1042,1043],{"class":73},"# 或使用 Google Secret Manager \u002F AWS Secrets Manager\n",[249,1045],{},[44,1047,1048],{"id":1048},"日誌與監控",[53,1050,1051],{},"記錄足夠的資訊幫助事後調查，但避免記錄敏感資料。",[57,1053,1055],{"className":59,"code":1054,"language":61,"meta":62,"style":62},"\u002F\u002F ❌ 日誌包含密碼\nlogger.info(`Login attempt: ${email} \u002F ${password}`)\n\n\u002F\u002F ✅ 只記錄必要資訊\nlogger.info(`Login attempt: ${email}`, { ip: req.ip, userAgent: req.headers['user-agent'] })\n",[64,1056,1057,1062,1099,1103,1108],{"__ignoreMap":62},[67,1058,1059],{"class":69,"line":70},[67,1060,1061],{"class":73},"\u002F\u002F ❌ 日誌包含密碼\n",[67,1063,1064,1067,1069,1072,1074,1077,1080,1082,1084,1086,1089,1091,1094,1097],{"class":69,"line":77},[67,1065,1066],{"class":80},"logger",[67,1068,85],{"class":84},[67,1070,1071],{"class":88},"info",[67,1073,662],{"class":80},[67,1075,1076],{"class":84},"`",[67,1078,1079],{"class":119},"Login attempt: ",[67,1081,291],{"class":84},[67,1083,294],{"class":80},[67,1085,128],{"class":84},[67,1087,1088],{"class":119}," \u002F ",[67,1090,291],{"class":84},[67,1092,1093],{"class":80},"password",[67,1095,1096],{"class":84},"}`",[67,1098,131],{"class":80},[67,1100,1101],{"class":69,"line":134},[67,1102,138],{"emptyLinePlaceholder":137},[67,1104,1105],{"class":69,"line":141},[67,1106,1107],{"class":73},"\u002F\u002F ✅ 只記錄必要資訊\n",[67,1109,1110,1112,1114,1116,1118,1120,1122,1124,1126,1128,1130,1132,1135,1137,1140,1142,1145,1147,1150,1152,1154,1156,1159,1161,1164,1166,1168,1170],{"class":69,"line":147},[67,1111,1066],{"class":80},[67,1113,85],{"class":84},[67,1115,1071],{"class":88},[67,1117,662],{"class":80},[67,1119,1076],{"class":84},[67,1121,1079],{"class":119},[67,1123,291],{"class":84},[67,1125,294],{"class":80},[67,1127,1096],{"class":84},[67,1129,95],{"class":84},[67,1131,103],{"class":84},[67,1133,1134],{"class":106}," ip",[67,1136,110],{"class":84},[67,1138,1139],{"class":80}," req",[67,1141,85],{"class":84},[67,1143,1144],{"class":80},"ip",[67,1146,95],{"class":84},[67,1148,1149],{"class":106}," userAgent",[67,1151,110],{"class":84},[67,1153,1139],{"class":80},[67,1155,85],{"class":84},[67,1157,1158],{"class":80},"headers[",[67,1160,116],{"class":84},[67,1162,1163],{"class":119},"user-agent",[67,1165,116],{"class":84},[67,1167,125],{"class":80},[67,1169,128],{"class":84},[67,1171,131],{"class":80},[53,1173,1174],{},[186,1175,1176],{},"應該監控的指標：",[190,1178,1179,1182,1185],{},[193,1180,1181],{},"異常登入失敗次數",[193,1183,1184],{},"非預期的 API 錯誤率上升",[193,1186,1187],{},"非工作時間的大量資料存取",[249,1189],{},[44,1191,1193],{"id":1192},"安全開發流程devsecops","安全開發流程（DevSecOps）",[1195,1196,1197,1210],"table",{},[1198,1199,1200],"thead",{},[1201,1202,1203,1207],"tr",{},[1204,1205,1206],"th",{},"階段",[1204,1208,1209],{},"實踐",[1211,1212,1213,1222,1238,1246],"tbody",{},[1201,1214,1215,1219],{},[1216,1217,1218],"td",{},"開發",[1216,1220,1221],{},"Code Review、靜態分析（ESLint security rules）",[1201,1223,1224,1227],{},[1216,1225,1226],{},"CI\u002FCD",[1216,1228,1229,1230,1233,1234,1237],{},"自動化掃描（",[64,1231,1232],{},"npm audit","、",[64,1235,1236],{},"trivy"," 掃 Docker image）",[1201,1239,1240,1243],{},[1216,1241,1242],{},"部署",[1216,1244,1245],{},"最小權限、Secret Manager、網路隔離",[1201,1247,1248,1251],{},[1216,1249,1250],{},"運營",[1216,1252,1253],{},"日誌監控、定期滲透測試、漏洞回報機制",[53,1255,1256],{},"資安不是一次性的工作，而是需要在整個開發流程中持續落實的文化。",[1258,1259,1260],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":62,"searchDepth":77,"depth":77,"links":1262},[1263,1267,1272,1273,1274,1278,1282,1283],{"id":46,"depth":77,"text":46,"children":1264},[1265,1266],{"id":50,"depth":134,"text":51},{"id":212,"depth":134,"text":213},{"id":253,"depth":77,"text":253,"children":1268},[1269,1270,1271],{"id":256,"depth":134,"text":257},{"id":353,"depth":134,"text":354},{"id":425,"depth":134,"text":425},{"id":491,"depth":77,"text":492},{"id":548,"depth":77,"text":548},{"id":614,"depth":77,"text":615,"children":1275},[1276,1277],{"id":618,"depth":134,"text":619},{"id":764,"depth":134,"text":765},{"id":887,"depth":77,"text":887,"children":1279},[1280,1281],{"id":890,"depth":134,"text":891},{"id":996,"depth":134,"text":997},{"id":1048,"depth":77,"text":1048},{"id":1192,"depth":77,"text":1193},"2025-12-05","整理日常開發中常用的資安觀念與實踐方式。","md",null,"\u002Fimages\u002Fsecurity.jpg",{},{"title":22,"description":1285},"zWX3ru6e2VKC3sHo1ft7LY1UaT_FOrgnGi5WvJmxyJU",[1293,1295],{"title":18,"path":19,"stem":20,"description":1294,"children":-1},"從 Token、Context、Prompt，到 Tool、MCP、Agent，完整梳理 AI 應用開發的核心概念與底層運作原理。",{"title":26,"path":27,"stem":28,"description":1296,"children":-1},"拆解單機系統的優化路線，以及用回應時間與吞吐量來衡量系統性能。",1781661890756]