iT邦幫忙

2024 iThome 鐵人賽

DAY 20
0
Security

資安這條路:系統化學習網站安全與網站滲透測試系列 第 20

資安這條路:Day 20 SSRF 漏洞解析與於 docker-compose 實作其他服務

  • 分享至 

  • xImage
  •  

前言

伺服器代表使用者或應用程式本身發起請求是很常見的功能。以下是一些具體的例子:

  1. 第三方 API 整合:
    • 天氣資訊:應用程式可能需要從氣象局的 API 取得最新天氣資料。
    • 付款系統:在處理線上付款時,伺服器需要與付款 gateway (如 PayPal 或信用卡處理系統)進行通訊。
  2. 社群媒體整合:
    • 分享功能:當使用者想在網站上分享內容到 Facebook 或 Twitter 時,伺服器可能需要與這些平台的 API 互動。
  3. 內容聚合:
    • RSS 訂閱:伺服器可能需要定期從多個 RSS 來源取得最新的新聞或部落格文章。
  4. 微服務架構:
    • 在分散式系統中,一個微服務可能需要呼叫其他微服務的 API 來完成特定任務。
  5. 代理請求:
    • 為了繞過跨來源資源共用(CORS)限制,前端可能將請求發送到自己的伺服器,然後由伺服器轉發到目標 API。
  6. 檔案下載:
    • 當使用者要求下載遠端伺服器上的檔案時,應用程式的伺服器可能會先下載該檔案,然後再提供給使用者。
  7. 電子郵件服務:
    • 發送通知郵件時,伺服器需要與 SMTP 伺服器通訊。
  8. 地圖和地理位置服務:
    • 使用 Google Maps API 或其他地理位置服務來提供位置相關的功能。
  9. 身份驗證服務:
    • 使用 OAuth 進行第三方登入時,伺服器需要與身份提供者(如 Google、Facebook)進行通訊。
  10. 資料庫同步:
    • 在分散式資料庫系統中,一個節點可能需要向其他節點發送請求以同步資料。
  11. 快取更新:
    • 當主資料來源更新時,伺服器可能需要主動刷新分散式快取系統(如 Redis)中的資料。
  12. 網頁爬蟲功能:
    • 如果應用程式需要從其他網站擷取資訊,伺服器可能需要發送 HTTP 請求來取得和解析網頁內容。

這些功能都涉及伺服器代表使用者或應用程式本身發起請求,而如果沒有適當的安全措施,這些功能可能會成為 SSRF 攻擊的潛在目標。

因此,在實作這些功能時,確保對所有外部請求進行嚴格的驗證和控制是非常重要的。但如果沒有適當的安全措施,可能會被惡意利用,導致 SSRF(Server-Side Request Forgery,伺服器端請求偽造)攻擊。

SSRF 攻擊允許攻擊者從目標伺服器發起未經授權的請求,這可能導致敏感資訊洩露、內部系統存取,甚至遠端程式碼執行。

本文將深入探討 SSRF 的概念、風險,以及在 Node.js 環境中的具體實作和防禦措施。

什麼是 SSRF

SSRF 是一種安全漏洞,攻擊者能夠利用易受攻擊的伺服器作為代理,發送惡意請求。這些請求可能針對內部網路資源、外部系統,或者伺服器本身。

在正常情況下,伺服器可能會代表使用者取得資源,例如:

const axios = require('axios');

app.get('/fetch-resource', async (req, res) => {
  const { url } = req.query;
  const response = await axios.get(url);
  res.json(response.data);
});

然而,如果沒有適當的驗證和限制,攻擊者可能會濫用此功能存取非預期的資源。

攻擊流程

  1. 識別漏洞:攻擊者尋找允許使用者提供 URL 或 IP 位址的輸入點。
  2. 構造惡意請求:攻擊者建立指向內部資源或敏感服務的 URL。
  3. 利用伺服器發送請求:攻擊者透過易受攻擊的端點提交惡意 URL。
  4. 伺服器執行請求:伺服器代表攻擊者存取目標資源。
  5. 取得敏感資訊:攻擊者可能接收到本應受保護的資料或利用伺服器的特權存取權限。

實作 SSRF 漏洞環境與內部 API

在本次實作中,我們將建立一個易受 SSRF(Server-Side Request Forgery,伺服器端請求偽造)攻擊的環境,並設定一個內部 API 來模擬敏感資料。這將幫助我們更好地理解 SSRF 漏洞的風險和防禦策略。

程式碼

https://github.com/fei3363/ithelp_web_security_2024/commit/25e04fa7eed55743621c94de14dd61932e42deb1

更新 docker-compose.yml

首先,我們需要在 docker-compose.yml 檔案中新增 internal-api 服務:

services:
  app:
    # ... 其他設定 ...
    depends_on:
      - mongo
      - postgres
      - internal-api

  internal-api:
    build:
      context: ./internal-api
      dockerfile: Dockerfile
    environment:
      - SECRET_KEY=supersecretkey123
    command: sh -c "npm install && npm start"

  # ... 其他服務 ...

這個設定將建立一個新的 internal-api 服務,它將模擬我們的內部敏感 API。

建立內部 API

接下來,我們需要建立 internal-api 目錄,並在其中建立必要的檔案:

Dockerfile

internal-api/Dockerfile 中:

FROM node:14
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 4000
CMD [ "node", "server.js" ]

package.json

internal-api/package.json 中:

{
  "name": "internal-api",
  "version": "1.0.0",
  "description": "Internal API for SSRF demonstration",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.17.1"
  }
}

server.js

internal-api/server.js 中:

const express = require('express');
const app = express();
const port = 4000;

const sensitiveData = {
  admin: {
    username: 'admin',
    password: 'super_secret_password'
  },
  secretKey: process.env.SECRET_KEY || 'default_secret_key'
};

const localOnly = (req, res, next) => {
  const ip = req.ip.replace(/^::ffff:/, '');
  if (ip === '127.0.0.1' || ip.startsWith('172.') || ip.startsWith('192.168.')) {
    next();
  } else {
    res.status(403).send('Access denied');
  }
};

app.use(localOnly);

app.get('/api/sensitive', (req, res) => {
  res.json(sensitiveData);
});

app.get('/health', (req, res) => {
  res.status(200).send('OK');
});

app.listen(port, () => {
  console.log(`Internal API running on port ${port}`);
});

這個內部 API 包含了敏感資料,並試圖透過 IP 位址限制來保護自己。

更新主應用程式

現在,我們需要更新主應用程式以建立 SSRF 漏洞:

更新 package.json

web/package.json 中新增 axios 相依套件:

"dependencies": {
  // ... 其他相依套件 ...
  "axios": "^0.21.1"
}

更新 server.js

web/server.js 中新增新的路由:

const axios = require('axios');

// ... 其他程式碼 ...

app.use(bodyParser.json());
app.get('/fetch', async (req, res) => {
  const { url } = req.query;
  
  if (!url) {
    return res.status(400).send('URL parameter is required');
  }

  try {
    const response = await axios.get(url);
    res.json(response.data);
  } catch (error) {
    console.error(error);
    res.status(500).send('Error fetching URL');
  }
});

這個新的 /fetch 路由允許客戶端指定一個 URL,伺服器將取得該 URL 的內容並回傳。這是一個典型的 SSRF 漏洞,因為它允許攻擊者存取內部網路資源。

測試 SSRF 漏洞

現在,我們可以測試 SSRF 漏洞:

啟動所有服務:

docker-compose up --build

使用 curl 確認是否可看到健康狀態

image

http://nodelab.feifei.tw/fetch?url=http://internal-api:4000/health

使用 curl 指令嘗試存取內部 API

curl "http://localhost:3000/fetch?url=http://internal-api:4000/api/sensitive"

http://nodelab.feifei.tw/fetch?url=http://internal-api:4000/api/sensitive

image

如果一切設定正確,這個請求應該能夠成功取得內部 API 的敏感資料。

小結

透過這個實作,我們建立了一個包含 SSRF 漏洞的環境,以及一個模擬內部敏感 API 的服務。這個設定允許我們:

  1. 了解 SSRF 漏洞如何被利用來存取內部資源。
  2. 認識到僅依靠 IP 位址限制來保護內部 API 的不足。
  3. 實作如何在 Docker 環境中設定多個相互相依套件的服務。

在實際的開發中,我們應該實施更嚴格的安全措施,如 URL 白名單、更嚴格的輸入驗證,以及網路分段等,以防止 SSRF 攻擊。

更複雜的攻擊場景

攻擊者可能利用 SSRF 執行更複雜的攻擊,例如:

  1. 存取雲端服務 memta 資料:

     http://169.254.169.254/latest/meta-data/
    
  2. 連接埠掃描:

    http://internal-server:22
    
  3. 讀取本機檔案:

    file:///etc/passwd
    

這些攻擊可能導致敏感資訊洩露、未授權存取,甚至遠端程式碼執行。

防禦措施

要防止 SSRF 攻擊,可以採取以下措施:

  1. 輸入驗證:嚴格驗證和清理使用者輸入的 URL。
const { URL } = require('url');

function isValidUrl(string) {
  try {
    new URL(string);
    return true;
  } catch (err) {
    return false;
  }
}

app.get('/fetch', async (req, res) => {
  const { url } = req.query;
  
  if (!isValidUrl(url)) {
    return res.status(400).send('Invalid URL');
  }
  
  // 繼續處理請求...
});
  1. 白名單:只允許存取預定義的安全網域或 IP 位址。
const allowedHosts = ['api.example.com', 'data.example.com'];

app.get('/fetch', async (req, res) => {
  const { url } = req.query;
  const parsedUrl = new URL(url);
  
  if (!allowedHosts.includes(parsedUrl.hostname)) {
    return res.status(403).send('Access to this host is not allowed');
  }
  
  // 繼續處理請求...
});
  1. 禁用不需要的協定:限制只能使用 HTTP 和 HTTPS。
  2. 使用外部服務:考慮使用專門的 URL 展開或預覽服務,這些服務通常有內建的安全措施。
  3. 網路分段:將包含敏感資訊的伺服器與可能受到 SSRF 攻擊的應用程式分開。
  4. 最小權限原則:確保執行 Web 應用程式的服務帳戶只有必要的最小權限。

結論

SSRF 是一種常見但危險的漏洞,它可能導致嚴重的安全問題。開發人員必須警惕這種威脅,並採取適當的防禦措施。透過實施嚴格的輸入驗證、使用白名單、限制協定,以及遵循最小權限原則,可以顯著降低 SSRF 攻擊的風險。

持續的安全稽核和更新是保護應用程式免受 SSRF 和其他新興威脅的關鍵。隨著攻擊技術的不斷演進,保持警惕和適應新的防禦策略至關重要。

小試身手

  1. SSRF 攻擊的主要目標是什麼?
    A) 客戶端瀏覽器
    B) 伺服器端應用程式
    C) 資料庫
    D) 防火牆

    答案:B
    解釋:SSRF 攻擊主要針對伺服器端應用程式,利用其代表攻擊者發送請求的能力。

  2. 下列哪項不是 SSRF 攻擊可能造成的後果?
    A) 存取內部網路資源
    B) 洩露敏感資訊
    C) 直接修改客戶端程式碼
    D) 繞過防火牆限制

    答案:C
    解釋:SSRF 攻擊發生在伺服器端,不能直接修改客戶端程式碼。其他選項都是 SSRF 可能導致的後果。

  3. 防止 SSRF 攻擊的最佳實作是什麼?
    A) 禁用所有外部請求
    B) 只使用 HTTPS
    C) 實施嚴格的 URL 驗證和白名單
    D) 增加伺服器的處理能力

    答案:C
    解釋:實施嚴格的 URL 驗證和使用白名單是防止 SSRF 攻擊的最有效方法。

  4. 在 Node.js 中,哪個模組常被用來發起可能導致 SSRF 的請求?
    A) fs
    B) http
    C) axios
    D) path

    答案:C
    解釋:axios 是一個流行的 HTTP 客戶端函式庫,常用於發起網路請求,如果使用不當,可能導致 SSRF 漏洞。

  5. SSRF 攻擊者可能試圖存取以下哪個位址來取得雲端執行個體的 matadata 資料?
    A) 8.8.8.8
    B) 127.0.0.1
    C) 169.254.169.254
    D) 192.168.1.1

    答案:C
    解釋:169.254.169.254 是許多雲端服務提供商用於儲存執行個體 matadata 資料的 IP 位址,攻擊者可能試圖透過 SSRF 存取此位址以取得敏感資訊。


上一篇
資安這條路:Day 19 Node.js 不安全反序列化與 out-of-band
下一篇
資安這條路 Day 21: JWT Token 安全性探討
系列文
資安這條路:系統化學習網站安全與網站滲透測試30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言