Come impedire l'attraversamento della directory quando si uniscono i percorsi in node.js?

11

Ho una webapp node.js nella quale ho bisogno di concatenare due percorsi in modo sicuro. Il primo (a sinistra) è una costante e il secondo (a destra) è relativo al primo e proviene da input dell'utente non attendibile. Il percorso risultante dovrebbe essere qualcosa che si trova sotto il primo percorso. Quindi la situazione è questa:

path1 = "public/html";                // Hardcoded path.
path2 = req.query.path;               // Untrusted user input.
result = safePathJoin(path1, path2);  // Result can be e.g. public/html/index.htm,
                                      // but never private/config.xml

Ciò di cui ho bisogno è la funzione safePathJoin() che è sicura contro gli attacchi traversali di directory. Il mio primo approccio ingenuo è questo:

safePathJoin = function(path1, path2) {
    path1 = path.normalize(path1);
    var result = path.join(path1, path2);
    return result.startsWith(path1) ? result : undefined;
}

È abbastanza buono? C'è un modo standard per farlo? Qualche suggerimento?

    
posta Anders 19.05.2016 - 13:52
fonte

2 risposte

9

Ecco un approccio che ho usato in questa situazione:

  1. path.normalize() gestisce tutti . e .. , quindi puoi essere certo che se uno dei due è presente , sarà nella parte anteriore del percorso.
  2. Rimuovi ../../ dalla parte anteriore del tuo percorso.

var safeSuffix = path.normalize(unsafeSuffix).replace(/^(\.\.[\/\])+/, '');
var safeJoin = path.join(basePath, safeSuffix);

Informazioni sul tuo approccio: la verifica del prefisso mi sembra una buona idea. Ci sono un paio di problemi che vedo con la tua implementazione:

  • Hai controllato un prefisso senza una barra finale: ../html-other verrà risolto in public/html-other , che presumo non sia quello che desideri.
  • Si verificherebbero problemi nei sistemi Windows, dove .normalize() convertirà / in \ , il che significa che i percorsi no potrebbero funzionare.

Quando ho fatto il prefisso-check (per situazioni leggermente diverse), ecco cosa ho trovato:

function checkPrefix(prefix, candidate) {
    // .resolve() removes trailing slashes
    var absPrefix = path.resolve(prefix) + path.sep;
    var absCandidate = path.resolve(candidate) + path.sep;
    return absCandidate.substring(0, absPrefix.length) === absPrefix;
}

(Sì, ho aggiunto path.sep a entrambi, in modo che il prefisso stesso passi il test.)

    
risposta data 19.05.2016 - 14:44
fonte
0

Dato il requisito di ricevere input dell'utente path che è una sottodirectory di una directory root definita e consentire all'utente di accedere (in qualsiasi modo) combinato root + path directory, per me il più sicuro l'opzione sarebbe quella di consentire solo i percorsi assoluti come input e verificare che sia sanificato da qualsiasi percorso di nome directory speciale ( . , .. ) uguale all'ingresso originale:

// Input

const path1 = "public/html";
const path2 = req.query.path;

// Checkout

const isNotSpecialDirName = part => !(['', '.', '..'].includes(part));

const path2Clean = path2.split(path.sep).filter(isNotSpecialDirName).join(path.sep);

// Output

if (path2Clean !== path2) {
  // Obfuscated with Not Found
  throw new Error('Not Found');
}

const result = path.join(path1, path2Clean);
    
risposta data 15.01.2019 - 17:19
fonte

Leggi altre domande sui tag