Bastionado, seguridad en sistemas: Seguridad de la orden "while read" en Bash pre { background:#eeeeee; border:1px solid #A6B0BF; font-size:120%; line-height:100%; overflow:auto; padding:10px; color:#000000 } pre:hover { border:1px solid #efefef; } code { font-size:120%; text-align:left; margin:0;padding:0; color: #000000;} .clear { clear:both; overflow:hidden; }

Bienvenido al blog

Bienvenidos al blog de seguridad en sistemas

sábado, 12 de marzo de 2011

Seguridad de la orden "while read" en Bash


Cuando necesitamos recorrer las líneas de un fichero mediante un script de shell Bash nos encontramos con la necesidad de modificar el separador natural, que es el espacio, por el salto de linea. Para ello modificamos la variable “IFS” tal y como se ilustra en el siguiente script:
#!/bin/bash --
OIFS=$IFS
IFS="
"
for i in `cat /etc/resolv.conf`;
do
       echo $i
done
$ ./prueba.sh
# Generated by NetworkManager
domain local.lan
search local.lan
nameserver 80.58.61.250
nameserver 80.58.61.254

Pero la modificación del separador natural puede alterar el funcionamiento de las acciones realizadas dentro del bucle. Por ello se suele emplear la instrucción “while read” que permita también la lectura de ficheros por líneas:
#!/bin/bash --
valor="caca"
cat /etc/resolv.conf | while read i
do
      echo $i
done
$ ./prueba.sh
(mismo resultado que el anterior)
Hasta este punto todo claro, pero... ¿estáis seguros de lo que interpreta Bash en la sentencia anterior? Realmente se ejecuta todo el código dentro del “while read” como un subproceso, por lo cual, todas las modificaciones realizadas sobre las variables dentro de la instrucción solo existen dentro de ese subproceso. Veámoslo:
#!/bin/bash --
valor="caca"
cat /etc/resolv.conf | while read i
do
       valor=$i
done
echo "Valor: $valor"
$ ./prueba.sh
Valor: caca
Aunque la variable declarada antes de la instrucción “while read” sea modificada dentro de esta, la variable mantiene su valor sin que las modificaciones realizadas dentro del bucle le afecten, debido a que que, el código dentro del bucle se ejecuta como un subproceso.

Para que las modificaciones realizadas dentro de la instrucción se apliquen a las variables externas se debe leer el fichero de la siguiente forma:
#!/bin/bash --
valor="caca"
while read i
do
      valor=$i
done < /etc/resolv.conf
echo "Valor: $valor"
$ ./prueba.sh
Valor: nameserver 80.58.61.254
El problema de la opción anterior es que solo sirve para tratar directamente un fichero, pero no para tratar la ejecución de ordenes:
#!/bin/bash --
valor="caca"
while read i;
do
     valor=$i
done < /etc/resolv.conf | grep nameserver
echo "Valor: $valor"
$ ./prueba.sh
Valor: caca
Para poder hacerlo es necesario pasar la orden entre paréntesis y con un doble menor separados por espacio en blanco:
#!/bin/bash --
valor="caca"
while read i;
do
      valor=$i
done < <(cat /etc/resolv.conf | grep nameserver)
echo "Valor $valor"
$ ./prueba.sh
Valor: nameserver 80.58.61.254
 
De esta forma sí que se podrá lanzar ordenes y tratar el resultado de salida, línea por línea, sin necesidad de modificar la variable de separación natural IFS.

Nos vemos en la próxima entrada.

2 comentarios:

  1. Creo que este artículo se podría resumir diciendo que, si usas pipes -tuberías- sin paréntesis, estás creando un proceso nuevo. Es de primero de Bash.

    ResponderEliminar
  2. Hola gracias por contestar,

    no es solo eso, si te fijas hay un "< <(orden|orden)" ese "<" es la clave puesto que aunque pongas la tubería entre paréntesis, si no pones el segundo "<" falla: doble redirección, no juntos sino separados por un espacio en blanco.

    No considero esto de primero de Bash, puesto que hay que entender el comportamiento del "while read" junto al funcionamiento de las tuberías. A mi me pareció algo interesante.

    ResponderEliminar