ESP32 esp_http_server でクエリパラメータを URL デコードしパストラバーサルを防ぐ方法

ESP32 HTTP API がクエリパラメータでファイルパス(?path=config.json)を受け取る場合、ファイルシステムに触れる前に URL デコードしてトラバーサル試行を拒否しなければならない。そうしないと ?path=../x や ?path=%2e%2e%2f で LittleFS マウントから脱出される。

URL デコーダ

クエリ文字列は特殊バイトを %XX、スペースを + でエンコードする。最小限のデコーダ:

HTTPHandlersFiles.cpp
std::string url_decode(const std::string &in) {
    std::string out;
    out.reserve(in.size());
    for (size_t i = 0; i < in.size(); ++i) {
        char c = in[i];
        if (c == '%' && i + 2 < in.size()) {
            char hex[3] = { in[i+1], in[i+2], '\0' };
            char decoded = static_cast<char>(strtol(hex, NULL, 16));
            out.push_back(decoded);
            i += 2;
        } else if (c == '+') {
            out.push_back(' ');
        } else {
            out.push_back(c);
        }
    }
    return out;
}

パスサニタイザ

先にデコードし、それからサニタイズ——サニタイズ後にデコードすると %2e%2e%2f(../)を見逃す。クエリ/フラグメントの残りを取り除き、パスをマウントポイントに対して相対に強制し、すべての .. を拒否する:

HTTPHandlersFiles.cpp
bool sanitize_path(const std::string &in, std::string &out) {
    std::string dec = url_decode(in);
    size_t qpos = dec.find('?');
    if (qpos != std::string::npos) dec = dec.substr(0, qpos);
    size_t hpos = dec.find('#');
    if (hpos != std::string::npos) dec = dec.substr(0, hpos);
    size_t start = 0;
    while (start < dec.size() && dec[start] == '/') start++;  // 相対に強制
    out = dec.substr(start);
    if (out.find("..") != std::string::npos) return false;
    return true;
}

ファイルシステムに触れるすべてのハンドラで使う:

usage.cpp
std::string rel = parser.GetParameter("path");
if (!sanitize_path(rel, rel) || rel.empty()) {
    httpd_resp_set_status(req, "400 Bad Request");
    httpd_resp_sendstr(req, "{\"error\": \"Invalid path\"}");
    return ESP_OK;
}
std::string fullpath = std::string(LittleFSManager::mountPoint()) + "/" + rel;

注意点

  • すべての .. 部分文字列を拒否するのは必要以上に厳格(foo..bar も拒否される)が、組込み API ではエッジケースの正確さより単純さが勝る。
  • システムファイル用に拒否リストを保持し(例:nodes.json)、有効なパスでもファームウェアが管理する状態を削除できないようにする。
  • クエリパース自体には httpd_req_get_url_query_str + httpd_query_key_value、または小さな QueryURLParser ラッパがうまく機能する。

関連記事


同じカテゴリの類似記事をチェック: ESP32 C/C++