Cuál es el problema
Primero, como para muchas utilidades, tendrás un problema con los nombres de archivos que comienzan con -
. Mientras en:
sh -c 'inline sh script here' other args
Los otros argumentos se pasan al inline sh script
; con el perl
equivalente
perl -e 'inline perl script here' other args
Los otros argumentos se analizan en busca de más opciones para perl primero, no para el script en línea. Entonces, por ejemplo, si hay un archivo llamado -eBEGIN{do something evil}
en el directorio actual,
perl -ne 'inline perl script here;' *
(con o sin -n
) hará algo malvado.
Al igual que para otras utilidades, la solución es usar el marcador de fin de opciones ( --
):
perl -ne 'inline perl script here;' -- *
Pero incluso entonces, sigue siendo peligroso y eso depende del <>
operador utilizado por -n
/ -p
.
El problema se explica en la perldoc perlop
documentación.
Ese operador especial se usa para leer una línea (un registro, los registros son líneas por defecto) de entrada, donde esa entrada proviene de cada uno de los argumentos a su vez pasados @ARGV
.
En:
perl -pe '' a b
-p
implica un while (<>)
bucle alrededor del código (aquí vacío).
<>
primero se abrirá a
, leerá los registros línea por línea hasta que se agote el archivo y luego se abrirá b
...
El problema es que, para abrir el archivo, utiliza la primera forma insegura de open
:
open ARGV, "the file as provided"
Con esa forma, si el argumento es
"> afile"
, se abre afile
en modo de escritura,
"cmd|"
, se ejecuta cmd
y lee su salida.
"|cmd"
, tienes una secuencia abierta para escribir en la entrada de cmd
.
Entonces, por ejemplo:
perl -pe '' 'uname|'
No genera el contenido del archivo llamado uname|
(un nombre de archivo perfectamente válido por cierto), sino el resultado del uname
comando.
Si estás corriendo:
perl -ne 'something' -- *
Y alguien ha creado un archivo llamado rm -rf "$HOME"|
(de nuevo un nombre de archivo perfectamente válido) en el directorio actual (por ejemplo, porque ese directorio fue una vez escrito por otros, o ha extraído un archivo dudoso, o ha ejecutado algún comando dudoso, o otra vulnerabilidad en algún otro software fue explotada), entonces estás en un gran problema. Las áreas donde es importante tener en cuenta ese problema son las herramientas que procesan archivos automáticamente en áreas públicas como /tmp
(o herramientas que pueden ser llamadas por tales herramientas).
Archivos llamados > foo
, foo|
, |foo
son un problema. Pero en menor medida < foo
y foo
con caracteres de espaciado ASCII iniciales o finales (incluyendo espacio, tabulación, nueva línea, cr ...) y eso significa que esos archivos no se procesarán o que el incorrecto sí lo será.
También tenga en cuenta que algunos caracteres en algunos conjuntos de caracteres de varios bytes (como ǖ
en BIG5-HKSCS) terminan en el byte 0x7c, la codificación de |
.
$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000 88 7c
210 |
0000002
Entonces, en las configuraciones regionales que usan ese juego de caracteres,
perl -pe '' ./nǖ
¡Intentaría ejecutar el ./n\x88
comando ya perl
que no intentaría interpretar ese nombre de archivo en la configuración regional del usuario!
Cómo arreglar / solucionar
AFAIK, no hay nada que pueda hacer para cambiar ese comportamiento predeterminado inseguro de perl
una vez por todas en todo el sistema.
Primero, el problema ocurre solo con los caracteres al principio y al final del nombre del archivo. Entonces, mientras perl -ne '' *
o perl -ne '' *.txt
son un problema,
perl -ne 'some code' ./*.txt
no se debe a todos los argumentos ahora comienzan con ./
y terminan en .txt
(lo que no -
, <
, >
, |
, espacio ...). En general, es una buena idea prefijar los globos con ./
. Eso también evita problemas con los archivos llamados -
o que comienzan con -
muchas otras utilidades (y aquí, eso significa que ya no necesita el --
marcador de fin de opciones ( )).
Usar -T
para activar el taint
modo ayuda hasta cierto punto. Anulará el comando si se encuentra dicho archivo malicioso (solo para los casos >
y |
, sin <
embargo , o espacios en blanco).
Eso es útil cuando se utilizan dichos comandos de forma interactiva, ya que te avisa de que hay algo dudoso. Sin embargo, eso puede no ser deseable cuando se realiza un procesamiento automático, ya que eso significa que alguien puede hacer que ese procesamiento falle simplemente creando un archivo.
Si desea procesar cada archivo, independientemente de su nombre, puede usar el ARGV::readonly
perl
módulo en CPAN (desafortunadamente, por lo general, no está instalado de manera predeterminada). Ese es un módulo muy corto que hace:
sub import{
# Tom Christiansen in Message-ID: <24692.1217339882@chthon>
# reccomends essentially the following:
for (@ARGV){
s/^(\s+)/.\/$1/; # leading whitespace preserved
s/^/< /; # force open for input
$_.=qq/\0/; # trailing whitespace preserved & pipes forbidden
};
};
Básicamente, desinfecta @ARGV convirtiéndolo, " foo|"
por ejemplo, en "< ./ foo|\0"
.
Puede hacer lo mismo en una BEGIN
declaración en su perl -n/-p
comando:
perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*
Aquí lo simplificamos suponiendo que ./
se está utilizando.
Un efecto secundario de que (y ARGV::readonly
) es sin embargo que $ARGV
en your code here
espectáculos que se arrastra carácter NUL.
Actualizar 2015-06-03
perl
v5.21.5 y superior tienen un nuevo <<>>
operador que se comporta como, <>
excepto que no realizará ese procesamiento especial. Los argumentos solo se considerarán como nombres de archivo. Entonces, con esas versiones, ahora puede escribir:
perl -e 'while(<<>>){ ...;}' -- *
(no olvide --
o use ./*
) sin temor a sobrescribir archivos o ejecutar comandos inesperados.
-n
Sin -p
embargo, todavía uso la <>
forma peligrosa . Y tenga cuidado con los enlaces simbólicos, de modo que eso no significa necesariamente que sea seguro usarlo en directorios que no sean de confianza.