網站的快取機制和參數處理是常見的功能,但如果實作不當,可能會導致嚴重的安全漏洞。
本篇文將深入探討兩種常見的攻擊:Cache Pollution(快取污染)和 HTTP Parameter Pollution (HPP)。
我們將透過實際的程式碼和測試案例來了解這些漏洞的原理和防禦方法。
快取(Cache)是一種暫時儲存資料的機制
目的是加速後續對相同資料的存取
快取污染發生的原因如下
駭客可以透過以下方式發現快取污染的可能性:
Cache-Control
、Expires
、ETag
等 HTTP 標頭,了解網站的快取策略這個案例突顯了快取系統在安全性方面的重要性,
尤其是在處理大規模、多租戶環境時。
它也說明了為什麼快取污染可能會造成遠超出單一網站範圍的嚴重後果。
https://hackerone.com/reports/1010858
2020年10月18日,一位安全研究員(9529)向 Acronis 報告了一個 Web 快取污染漏洞。
研究員在 www.acronis.com 發現了快取污染問題。這種漏洞可能被用來分發各種攻擊,如 XSS、JavaScript 注入、開放重定向等。
x-forwarded-port
標頭「破壞」快取:GET /zh-cn/careers/?yig1bt7ai4=1 HTTP/1.1
Host: www.acronis.com
Connection: close
sec-ch-ua: "Chromium";v="86", "\"Not\\A;Brand";v="99", "Google Chrome";v="86"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.80 Safari/537.36 yig1bt7ai4
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9, text/yig1bt7ai4
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://www.acronis.com/zh-cn/cloud/cyber-protect/
Accept-Encoding: gzip, deflate, yig1bt7ai4
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
x-forwarded-port: zwrtxqvas9lm4kzkia
Origin: https://yig1bt7ai4.com
GET /zh-cn/careers/ HTTP/1.1
Host: www.acronis.com
Connection: close
sec-ch-ua: "Chromium";v="86", "\"Not\\A;Brand";v="99", "Google Chrome";v="86"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.80 Safari/537.36 yig1bt7ai4
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9, text/yig1bt7ai4
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://www.acronis.com/zh-cn/cloud/cyber-protect/
Accept-Encoding: gzip, deflate, yig1bt7ai4
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
/zh-cn/careers/
頁面時,會看到被污染的結果。研究員發現,除了 x-forwarded-port
,還可以使用 x-forwarded-host
標頭來達到類似效果:
GET /zh-cn/careers/?yig1bt7ai4=2 HTTP/1.1
Host: www.acronis.com
Connection: close
Pragma: no-cache
Cache-Control: no-cache
sec-ch-ua: "Chromium";v="86", "\"Not\\A;Brand";v="99", "Google Chrome";v="86"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.80 Safari/537.36
Accept: text/html,application/xhtml xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
x-forwarded-port: zwrtxqvas9lm4kzkia
x-forwarded-host: evil.acronis.com
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
x-forwarded-*
系列標頭。x-forwarded-*
系列。這個案例展示了 Web 快取污染漏洞的複雜性和潛在危險性,
強調了在實施快取機制時需要全面考慮安全性,
並對各種 HTTP 標頭和參數保持警惕。
它也凸顯了安全研究在發現和修復此類漏洞中的重要作用。
開發者應該
透過理解這些概念和案例,開發者可以更好地設計和實現安全的快取系統,防止快取污染等安全問題的發生。
https://github.com/fei3363/ithelp_web_security_2024/commit/ac8f30cd68a06b973e41af57a971ab0bd5f1d2c9
安裝必要的套件:
在 package.json
中新增 node-cache
套件:
"dependencies": {
// ... 其他相依套件
"node-cache": "^5.1.2"
}
然後執行 npm install
安裝新增的套件。
建立快取控制器:
新建檔案 web/controllers/cacheController.js
:
const NodeCache = require('node-cache');
const cache = new NodeCache();
const cacheController = {
// ... 在此處實作各個方法
};
module.exports = cacheController;
實作快取功能:
在 cacheController
物件中新增 cache
方法:
cache: function(req, res) {
const userAgent = req.headers['user-agent'];
// 以 URL 當作快取的鍵值,若有相同的 URL,則直接回傳快取的內容
const cacheKey = req.url;
if (cache.has(cacheKey)) {
console.log('Serving from cache');
return res.send(cache.get(cacheKey));
}
let content = `<h1>Welcome, ${userAgent}!</h1>`;
cache.set(cacheKey, content);
res.send(content);
},
實作快取管理功能:
在 cacheController
物件中新增以下方法:
// 針對所有快取進行清除
flush: function(req, res) {
cache.flushAll();
res.send('Cache flushed');
},
// 取得快取統計資訊
getStats: function(req, res) {
res.send(cache.getStats());
},
// 取得所有快取的鍵值
getKeys: function(req, res) {
res.send(cache.keys());
},
// 取得所有快取的值
getallvalue: function(req, res) {
res.send(cache.mget(cache.keys()));
},
// 取得所有快取的 TTL(Time To Live)
getallvaluettl: function(req, res) {
try {
const keys = cache.keys();
const ttls = {};
keys.forEach(key => {
try {
ttls[key] = cache.getTtl(key);
} catch (err) {
console.error(`Error getting TTL for key ${key}:`, err);
ttls[key] = null; // 或者其他表示錯誤的值
}
});
res.send(ttls);
} catch (err) {
console.error('Error in getallvaluettl:', err);
res.status(500).send('Internal Server Error');
}
}
建立路由:
新建檔案 web/routes/cacheRouter.js
:
const express = require('express');
const cacheController = require('../controllers/cacheController');
const cacheRouter = express.Router();
cacheRouter.get('/', cacheController.cache);
cacheRouter.get('/flush', cacheController.flush);
cacheRouter.get('/stats', cacheController.getStats);
cacheRouter.get('/keys', cacheController.getKeys);
cacheRouter.get('/getallvalue', cacheController.getallvalue);
cacheRouter.get('/getallttl', cacheController.getallvaluettl);
module.exports = cacheRouter;
在主應用程式中使用路由:
在 web/server.js
中新增:
const cacheRouter = require('./routes/cacheRouter');
// ... 其他程式碼
app.use('/api/cache', cacheRouter);
這個實作過程建立了一個基本的快取系統,但它包含了潛在的 Cache Pollution 和 HTTP Parameter Pollution (HPP) 漏洞。
主要的問題在於:
這樣的設計目的是為了展示常見的錯誤做法,並在後續的學習中逐步改進這些問題。
實際的正式環境中,應該從一開始就實施安全的做法,包括:
curl http://nodelab.feifei.tw/api/cache/flush && echo
curl http://nodelab.feifei.tw/api/cache/getallvalue && echo
curl -H "User-Agent: <script>alert('Hacked')</script>" http://nodelab.feifei.tw/api/cache/ && echo
curl http://nodelab.feifei.tw/api/cache/getallvalue && echo
curl http://nodelab.feifei.tw/api/cache/ && echo
curl http://nodelab.feifei.tw/api/cache/stats && echo
當系統試圖從快取中取得資料時,可能會發生兩種情況:
{"hits":3,"misses":0,"keys":1,"ksize":1,"vsize":51}
"hits":3
表示系統成功地從快取中查詢到所需資料 3 次"misses":0
表示系統沒有遇到任何快取未命中的情況快取命中率是評估快取效率的重要指標:
快取命中率 = 命中次數 / (命中次數 + 未命中次數)
在這個案例中:3 / (3 + 0) = 100%
這表示快取效率非常高,所有請求都從快取中得到了滿足。
雖然高命中率通常是好事,但也需要考慮:
理解快取命中的概念對於優化系統性能和資源使用非常重要,尤其在設計大型或高流量的應用時。
curl http://nodelab.feifei.tw/api/cache/getallttl && echo
在 node-cache 中,TTL (Time To Live) 值為 0 的意義
如圖所示,所有的快取鍵值都顯示 TTL 為 0,代表:
cache.set(cacheKey, content, 3600); // 設定 TTL 為 1 小時(3600 秒)
const cache = new NodeCache({ stdTTL: 600 }); // 預設 TTL 為 10 分鐘
// controllers/cacheController.js
const NodeCache = require('node-cache');
const crypto = require('crypto');
const sanitizeHtml = require('sanitize-html');
const cache = new NodeCache({ stdTTL: 600 }); // 設定預設過期時間為 600 秒
const cacheController = {
cache: function(req, res) {
// 1. 使用更複雜的快取鍵值
const cacheKey = generateCacheKey(req);
if (cache.has(cacheKey)) {
console.log('Serving from cache');
return res.send(cache.get(cacheKey));
}
// 2. 對使用者輸入進行清理和驗證
const userAgent = sanitizeHtml(req.headers['user-agent'] || 'Unknown');
let content = `<h1>Welcome, ${userAgent}!</h1>`;
// 3. 設定快取,包含過期時間
cache.set(cacheKey, content, 300); // 設定此特定內容的過期時間為 300 秒
// 4. 實施內容安全策略
res.setHeader('Content-Security-Policy', "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline';");
res.send(content);
},
// ... 其他方法保持不變
};
// 生成複雜的快取鍵值
function generateCacheKey(req) {
const { url, method, headers } = req;
const keyParts = [
url,
method,
headers['user-agent'],
headers['accept-language'],
// 可以新增更多相關的請求特徵
];
return crypto.createHash('md5').update(keyParts.join('|')).digest('hex');
}
module.exports = cacheController;
generateCacheKey
函數建立一個基於多個請求特徵的唯一鍵sanitize-html
庫清理 User-Agent 字串,防止 XSS 攻擊HTTP Parameter Pollution (HPP) 是一種透過操縱 HTTP 請求參數來混淆應用程式邏輯或繞過安全控制的攻擊技術。
當應用程式不正確地處理多個同名參數時,可能會導致意外的行為。
app.get('/api/cache', (req, res) => {
const url = req.query.url;
// 使用 url 參數進行某些操作
});
這種實作可能容易受到 HPP 攻擊,因為它沒有正確處理多個同名參數的情況。
curl http://nodelab.feifei.tw/api/cache/flush && echo
Cache flushed
curl "http://nodelab.feifei.tw/api/cache/?url=http://example.com&url=http://attacker.com" && echo
<h1>Welcome, curl/7.81.0!</h1>
curl http://nodelab.feifei.tw/api/cache/getallvalue && echo
{"/?url=http://example.com&url=http://attacker.com":"<h1>Welcome, curl/7.81.0!</h1>"}
curl "http://nodelab.feifei.tw/api/cache/?url=http://attacker.com" && echo
<h1>Welcome, curl/7.81.0!</h1>
curl http://nodelab.feifei.tw/api/cache/getallvalue && echo
{"/?url=http://example.com&url=http://attacker.com":"<h1>Welcome, curl/7.81.0!</h1>","/?url=http://attacker.com":"<h1>Welcome, curl/7.81.0!</h1>"}
curl "http://nodelab.feifei.tw/api/cache/" && echo
<h1>Welcome, curl/7.81.0!</h1>
curl http://nodelab.feifei.tw/api/cache/getallvalue && echo
{"/?url=http://example.com&url=http://attacker.com":"<h1>Welcome, curl/7.81.0!</h1>","/?url=http://attacker.com":"<h1>Welcome, curl/7.81.0!</h1>","/":"<h1>Welcome, curl/7.81.0!</h1>"}
可以修改快取鍵值生成邏輯:
function generateCacheKey(req) {
const { url, method, headers } = req;
const parsedUrl = new URL(url, `http://${req.headers.host}`);
const sortedParams = Array.from(parsedUrl.searchParams.entries())
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([key, value]) => `${key}=${value}`)
.join('&');
const keyParts = [
parsedUrl.pathname,
sortedParams,
method,
headers['user-agent'],
// 可以新增更多相關的請求特徵
];
return crypto.createHash('md5').update(keyParts.join('|')).digest('hex');
}
Cache Pollution 和 HTTP Parameter Pollution 都是源於不當的輸入處理和不安全的設計。
透過理解這些漏洞的原理,實施適當的防禦措施,我們可以提高應用程式的安全性。
答案:B) 快取儲存
解析:Cache Pollution 攻擊主要利用網站的快取機制。
攻擊者透過操縱輸入來污染快取,使得快取中存儲惡意或不正確的內容,進而影響其他使用者。
答案:C) URL
解析:在提供的範例中,系統使用 URL 作為快取鍵。
這種方法容易受到攻擊,因為不同的 URL 參數組合可能導致不同的快取項,增加了 Cache Pollution 的風險。
答案:B) 混淆應用程式邏輯
解析:HPP 攻擊的主要目標是混淆應用程式的邏輯。
透過提供多個同名參數,攻擊者試圖使應用程式以意外的方式處理這些參數,從而繞過安全檢查或觸發非預期的行為。
答案:C) 增加伺服器記憶體
解析:增加伺服器記憶體不能直接防禦 Cache Pollution。
有效的方法包括使用複雜的快取鍵值、清理使用者輸入、設定適當的過期時間等。
這些方法都直接針對快取機制的安全性,而不是硬體資源。
答案:C) 使用陣列來處理可能重複的參數
解析:使用陣列來處理可能重複的參數是處理 HPP 攻擊的正確方法。
這允許應用程式正確地捕捉所有提供的參數值,而不是忽略或只處理部分參數。
這種方法使得應用程式能夠更好地控制如何處理多個同名參數。