Dado el siguiente script, ¿cómo puedo asegurarme de que el argumento solo contenga un nombre de archivo válido dentro /home/charlesingalls/
y no una ruta ( ../home/carolineingalls/
) o comodín, etc.
Solo quiero que el script pueda eliminar un solo archivo del directorio codificado. Este script se ejecutará como un usuario privilegiado.
#!/bin/bash
rm -f /home/charlesingalls/"$1"
/
. Los comodines no se interpretan entre comillas.-r
conrm
.rm -r
es para la eliminación recursiva de un directorio y todos los archivos y directorios debajo de él. Solo es útil al eliminar directorios. Más en general, no cultive el cargamento es decir, no solo copie cosas que parecen útiles en su línea de comandos o script sin comprender lo que hacen o cómo funcionan. Los dioses del avión que traen la carga mágica pueden enojarse y eliminar todos sus archivos.Respuestas:
Si solo desea eliminar un archivo
/home/charlesingalls
(y no un archivo en un subdirectorio), entonces es fácil: simplemente verifique que el argumento no contenga a/
.Esto se ejecuta
rm
incluso si el argumento es.
o está..
vacío, pero en ese casorm
fallará de forma inofensiva en eliminar un directorio.Los comodines no son relevantes aquí ya que no se realiza ninguna expansión de comodines.
Esto es seguro incluso en presencia de enlaces simbólicos: si el archivo es un enlace simbólico, el enlace simbólico (que está en
/home/charlesingalls
) se elimina y el destino de ese enlace no se ve afectado.Tenga en cuenta que esto supone que
/home/charlesingalls
no se puede mover ni cambiar. Eso debería estar bien si el directorio está codificado en el script, pero si se determina a partir de variables, la determinación podría no ser válida para cuando serm
ejecute el comando.Con base en la información adicional de que el argumento es un nombre de host virtual, debe hacer una lista blanca en lugar de una lista negra: verifique que el nombre sea un nombre de host virtual sensible, en lugar de solo prohibir barras. Compruebo que el nombre comienza con una letra minúscula o un dígito y que no contiene caracteres que no sean letras minúsculas, dígitos, puntos y guiones.
fuente
.
Esta respuesta supone que
$1
está permitido incluir subdirectorios. Si está interesado en el caso más simple donde$1
debería ser un nombre de directorio simple, consulte una de las otras respuestas.Los comodines no se expanden entre comillas dobles. Como
$1
está entre comillas dobles, los comodines no son un problema.Ambos
../
y los enlaces simbólicos pueden ocultar la ubicación real de un archivo. A continuación se muestran pruebas para determinar si el archivo está realmente, no solo aparentemente, bajo la ruta que queremos.Sistemas más nuevos: usando
realpath
En cuanto a averiguar si el archivo es realmente si el archivo está realmente bajo
/home/charlesingalls/
o no, puede usarrealpath
:Lo anterior se ejecuta
exit 1
si el archivo especificado por$1
está en otro lugar que no sea el directorio/home/charlesingalls/
.realpath
canonicaliza todo el camino, eliminando enlaces simbólicos y../
.realpath
es parte de GNU coreutils y debería estar disponible en cualquier sistema Linux.realpath
requiere GNU coreutils 8.15 (enero de 2012) o superior .Ejemplos
Para demostrar cómo sigue realpath
../
para determinar la ubicación real de un archivo (por ejemplo,-q
se omite la opción de grep para que la salida real de grep sea visible):Para demostrar cómo sigue los enlaces simbólicos:
Sistemas más antiguos: utilizando
readlink -e
readlink
también es capaz de cononicalizar una ruta, siguiendo enlaces simbólicos y../
:Usando los mismos archivos de ejemplo:
Además de estar disponibles en sistemas GNU más antiguos, hay versiones de
readlink
BSD disponibles.fuente
coreutils
no tienerealpath
-f
("todos menos el último componente debe existir") y los ejemplos usan-e
("todos los componentes deben existir"), lo cual es un poco confuso.rm
actúa sobre el argumento en sí, no sobre su objetivo.grep -q
para evitar lagrep
salida de líneas coincidentes. Todavía obtienes el estado de salida&&
y||
aún así trabajas exactamente como estás acostumbrado.Si desea prohibir completamente las rutas, la forma más simple es probar si la variable contiene una barra diagonal (
/
). En bash:Sin embargo, esto bloqueará todos los caminos, incluidos
foo/bar
. En su..
lugar, podría probar , pero eso dejaría la posibilidad de enlaces simbólicos que apuntan a directorios fuera de la ruta de destino.Si solo desea permitir la eliminación de un solo archivo, no creo que deba usarlo
rm -r
.Además, dependiendo de lo que esté haciendo, podría usar los permisos de archivos del sistema para permitir solo eliminar archivos que el usuario podría eliminar ellos mismos. Algo como esto:
Aunque, como comentó @Gilles, esto tiene un problema de
$1
comillas : fallará si contiene una comilla simple, por lo que la variable debe probarse primero (por ejemplo,if [[ "$1" = *\'* ]] ; then fail...
o más bien mediante la inclusión en la lista blanca de un conjunto sensible de caracteres), o el nombre del archivo pasado una variable de entorno con p. ej.fuente
su
comando está roto porque la cita es incorrecta. Estás ejecutando un comando con el argumento interpolado como un fragmento de shell. Por ejemplo, si el argumento es$(touch foo)
su código se ejecutatouch foo
.'
por todas'\''
) o pasarla por otro canal, como una variable de entorno (que es lo que haría aquífile_to_remove="$1" su -c 'rm "/home/charlesingalls/$file_to_remove"'
). Resulta que se supone que el nombre del archivo es un nombre de host virtual, por lo que rechazar todos los caracteres especiales también estaría bien aquí.