¿Cómo evitar el cruce de directorios al unir rutas en node.js?

9

Tengo una aplicación web node.js en la que necesito concatenar dos rutas de forma segura. La primera (la más a la izquierda) es una constante, y la segunda (la más a la derecha) es relativa a la primera y proviene de una entrada de usuario no confiable. La ruta resultante debe ser algo que está debajo de la primera ruta. Así que la situación es esta:

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

Lo que necesito es la función safePathJoin() que es segura contra ataques transversales de directorios. Mi primer enfoque ingenuo es el siguiente:

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

¿Esto es lo suficientemente bueno? ¿Hay una manera estándar de hacer esto? ¿Alguna sugerencia?

    
pregunta Anders 19.05.2016 - 13:52
fuente

1 respuesta

7

Este es un enfoque que he usado en esta situación:

  1. path.normalize() maneja todos los . y .. , por lo que puede estar seguro de que si hay alguno presente , estará al frente del camino.
  2. Elimine cualquier ../../ de la parte delantera de su ruta.

Entonces:

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

Sobre su enfoque: comprobar el prefijo me parece una buena idea. Hay un par de problemas que veo con su implementación:

  • Has buscado un prefijo sin una barra inclinada: ../html-other se resolverá en public/html-other , lo que supongo que no es lo que quieres.
  • Tendrías problemas en los sistemas Windows, donde .normalize() convertiría / a \ , lo que significa que las rutas no funcionarían.

Cuando he realizado la comprobación de prefijos (para situaciones ligeramente diferentes), esto es lo que acabé con:

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í, agregué path.sep a ambos, para que el propio prefijo dir pase la prueba.)

    
respondido por el cloudfeet 19.05.2016 - 14:44
fuente

Lea otras preguntas en las etiquetas