Node.jsでのXPathインジェクション

Node.jsでのXPathインジェクション

説明

利用者の入力をXPath式に直接含めると、XPathインジェクションが発生するおそれがあります。引用符、論理演算子、ワイルドカードを挿入され、意図しないノードの取得や認証の回避につながる可能性があります。Node.jsのxpathライブラリで、動的に組み立てた式をselect、select1、evaluateなどへ渡す場合も同様です。

想定される影響

  • XMLを使った認証や検索の処理を回避されるおそれがあります。
  • 機密のXMLノードや追加のレコードが漏えいする可能性があります。
  • 同じ式を複数のエンドポイントで使うと、同じ脆弱性が繰り返されるおそれがあります。

対処方法

  • 利用者の入力をXPath式へ直接連結しないでください。
  • 式を固定して値の比較をアプリケーションで行うか、ライブラリの変数バインディングを使ってください。
  • 利用者に任意のXPathを書かせず、必要な検索の種類を許可リストで制限してください。
  • 取得するノードと返すフィールドを最小限にし、リクエスト元のデータアクセス権限を別途確認してください。

例

変更前

javascript
const express = require("express");
const xpath = require("xpath");
const app = express();

app.get("/bad", (req, res) => {
  const expr = "//users/user[@name='" + req.query.user + "']";
  const result = xpath.select(expr, doc);
  res.json(result.map((node) => ({ name: node.getAttribute("name") })));
});

変更後

javascript
const express = require("express");
const xpath = require("xpath");
const app = express();

app.get("/good", (req, res) => {
  const user = req.query.user;
  if (typeof user !== "string" || user.length > 100) {
    return res.status(400).json({ error: "invalid user" });
  }
  const result = xpath.select("//users/user", doc)
    .filter((node) => node.getAttribute("name") === user);
  res.json(result.map((node) => ({ name: node.getAttribute("name") })));
});

変更前は入力によりXPathの構造を変えられます。変更後は固定の式でノードを選び、名前を値として比較するため、検索条件を維持しながらインジェクションを防ぎます。docは準備済みのXML文書で、解析とデータへのアクセス権限確認は省略しています。名前の検索だけで利用者を認証しないでください。

参考資料