desinfectante de cuerdas para nombre de archivo

113

Estoy buscando una función php que desinfecte una cadena y la deje lista para usar como nombre de archivo. ¿Alguien sabe de uno útil?

(¡Podría escribir uno, pero me preocupa que pase por alto un personaje!)

Editar: para guardar archivos en un sistema de archivos NTFS de Windows.

usuario151841
fuente
1
Puede ser más específico: ¿Qué va a pasar con las diéresis (eliminar o convertir en carácter base?) ¿Qué va a pasar con los caracteres especiales?
Pekka
¿Para qué sistema de archivos? Ellos difieren. Ver en.wikipedia.org/wiki/…
Gordon
Windows :) Necesita 15 caracteres.
user151841
1
Me gustaría señalar que las soluciones de "lista negra" sugeridas en algunas de las respuestas no son suficientes, ya que no es factible verificar todos los posibles caracteres indeseables (además de los caracteres especiales, hay caracteres con acentos y diéresis, todo alfabetos no ingleses / latinos, caracteres de control, etc. para tratar). Entonces, diría que un enfoque de "lista blanca" siempre es mejor, y normalizar la cadena (como sugiere el comentario de Blair McMillan sobre la respuesta de Dominic Rodger) permitirá un manejo natural de cualquier letra con acentos, diéresis, etc.
Sean the Bean
Una buena manera tal vez usando expresiones regulares, vea este script de Python que hice: github.com/gsscoder/normalize-fn
gsscoder

Respuestas:

42

En lugar de preocuparse por pasar por alto los personajes, ¿qué tal si usas una lista blanca de personajes que te gusta que te usen? Por ejemplo, se puede permitir que sólo un buen ol' a-z, 0-9, _, y una sola instancia de un punto ( .). Obviamente, eso es más limitante que la mayoría de los sistemas de archivos, pero debería mantenerlo a salvo.

Dominic Rodger
fuente
40
No es bueno para los idiomas con diéresis. Esto daría como resultado Qubec para Québec, Dsseldorf para Düsseldorf, y así sucesivamente.
Pekka
15
Cierto, pero como dije: "Por ejemplo".
Dominic Rodger
5
Lo que puede ser perfectamente aceptable para el OP. De lo contrario, use algo como php.net/manual/en/class.normalizer.php
Blair McMillan
3
En realidad, eso no es lo que se preguntó. La operación solicita una función para desinfectar la cuerda, no una alternativa.
i.am.michiel
3
@ i.am.michiel, quizás, pero dado que el OP lo aceptó, asumiré que lo encontraron útil.
Dominic Rodger
157

Al hacer un pequeño ajuste a la solución de Tor Valamo para solucionar el problema observado por Dominic Rodger, podría usar:

// Remove anything which isn't a word, whitespace, number
// or any of the following caracters -_~,;[]().
// If you don't need to handle multi-byte characters
// you can use preg_replace rather than mb_ereg_replace
// Thanks @Łukasz Rysiak!
$file = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $file);
// Remove any runs of periods (thanks falstro!)
$file = mb_ereg_replace("([\.]{2,})", '', $file);
Sean Vieira
fuente
43
¡Me encantan los adictos a las expresiones regulares! -_ ~
AVProgrammer
2
@ iim.hlk - sí, faltaba el paréntesis de cierre. Los he agregado ahora. ¡Gracias!
Sean Vieira
2
hay una falla allí, debe dividirla en dos y ejecutar la verificación ..después. Por ejemplo .?.acabaría siendo ... Aunque ya que filtra /, no veo cómo explotarlo más en este momento, pero muestra por qué la verificación no ..es efectiva aquí. Mejor aún, probablemente, no reemplace, simplemente rechace si no califica.
falstro
2
Porque ninguno de esos valores es ilegal en el sistema de archivos de Windows y ¿por qué perder más información de la necesaria? Puede cambiar la expresión regular a simplemente [^a-z0-9_-]si desea ser realmente restrictivo, o simplemente usar un nombre generado y desechar el nombre de pila y evitar todos estos problemas. :-)
Sean Vieira
3
Tenga en cuenta que: es ilegal.
JasonXA
49

Así es como puede desinfectar un sistema de archivos como se le pide

function filter_filename($name) {
    // remove illegal file system characters https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
    $name = str_replace(array_merge(
        array_map('chr', range(0, 31)),
        array('<', '>', ':', '"', '/', '\\', '|', '?', '*')
    ), '', $name);
    // maximise filename length to 255 bytes http://serverfault.com/a/9548/44086
    $ext = pathinfo($name, PATHINFO_EXTENSION);
    $name= mb_strcut(pathinfo($name, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($name)) . ($ext ? '.' . $ext : '');
    return $name;
}

Todo lo demás está permitido en un sistema de archivos, por lo que la pregunta está perfectamente respondida ...

... pero podría ser peligroso permitir, por ejemplo, comillas simples 'en un nombre de archivo si lo usa más tarde en un contexto HTML inseguro porque este nombre de archivo absolutamente legal:

 ' onerror= 'alert(document.cookie).jpg

se convierte en un agujero XSS :

<img src='<? echo $image ?>' />
// output:
<img src=' ' onerror= 'alert(document.cookie)' />

Por eso, el popular software CMS Wordpress los elimina, pero cubrieron todos los caracteres relevantes solo después de algunas actualizaciones :

$special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
// ... a few rows later are whitespaces removed as well ...
preg_replace( '/[\r\n\t -]+/', '-', $filename )

Finalmente su lista incluye ahora la mayoría de los personajes que forman parte de la URI rerserved caracteres y URL personajes inseguros lista.

Por supuesto, podría simplemente codificar todos estos caracteres en la salida HTML, pero la mayoría de los desarrolladores y yo también, seguimos el modismo "Más vale prevenir que curar" y eliminarlos de antemano.

Entonces, finalmente, sugeriría usar esto:

function filter_filename($filename, $beautify=true) {
    // sanitize filename
    $filename = preg_replace(
        '~
        [<>:"/\\|?*]|            # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
        [\x00-\x1F]|             # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
        [\x7F\xA0\xAD]|          # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
        [#\[\]@!$&\'()+,;=]|     # URI reserved https://tools.ietf.org/html/rfc3986#section-2.2
        [{}^\~`]                 # URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt
        ~x',
        '-', $filename);
    // avoids ".", ".." or ".hiddenFiles"
    $filename = ltrim($filename, '.-');
    // optional beautification
    if ($beautify) $filename = beautify_filename($filename);
    // maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $filename = mb_strcut(pathinfo($filename, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($filename)) . ($ext ? '.' . $ext : '');
    return $filename;
}

Todo lo demás que no cause problemas con el sistema de archivos debe ser parte de una función adicional:

function beautify_filename($filename) {
    // reduce consecutive characters
    $filename = preg_replace(array(
        // "file   name.zip" becomes "file-name.zip"
        '/ +/',
        // "file___name.zip" becomes "file-name.zip"
        '/_+/',
        // "file---name.zip" becomes "file-name.zip"
        '/-+/'
    ), '-', $filename);
    $filename = preg_replace(array(
        // "file--.--.-.--name.zip" becomes "file.name.zip"
        '/-*\.-*/',
        // "file...name..zip" becomes "file.name.zip"
        '/\.{2,}/'
    ), '.', $filename);
    // lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625
    $filename = mb_strtolower($filename, mb_detect_encoding($filename));
    // ".file-name.-" becomes "file-name"
    $filename = trim($filename, '.-');
    return $filename;
}

Y en este punto, debe generar un nombre de archivo si el resultado está vacío y puede decidir si desea codificar caracteres UTF-8. Pero no lo necesita, ya que UTF-8 está permitido en todos los sistemas de archivos que se utilizan en contextos de alojamiento web.

Lo único que tiene que hacer es usar urlencode()(como con suerte lo hace con todas sus URL) para que el nombre del archivo se საბეჭდი_მანქანა.jpgconvierta en esta URL como su <img src>o <a href>: http://www.maxrev.de/html/img/%E1%83% A1% E1% 83% 90% E1% 83% 91% E1% 83% 94% E1% 83% AD% E1% 83% 93% E1% 83% 98_% E1% 83% 9B% E1% 83% 90% E1% 83% 9C% E1% 83% A5% E1% 83% 90% E1% 83% 9C% E1% 83% 90.jpg

Stackoverflow hace eso, así que puedo publicar este enlace como lo haría un usuario:
http://www.maxrev.de/html/img/ საბეჭდი_მანქანა. Jpg

Así que este es un nombre de archivo legal completo y no es un problema como @ SequenceDigitale.com mencionó en su respuesta .

mgutt
fuente
3
Buen trabajo. La respuesta más útil para mí. +1
Oh ... La función funciona bien, pero desde hace algún tiempo empezó a poner - entre cada carácter, como r-u-l-e-sy no tengo idea de por qué sucede esto. Seguro que no es culpa de la función, sino simplemente preguntarse: ¿cuál podría ser la razón de tal comportamiento? Codificación incorrecta?
1
Oh, bueno ... Sólo hizo una depuración y sucede justo después de la preg_replaceen filter_filename().
Después de eliminar estos comentarios, comenzó a funcionar nuevamente.
¿Qué comentarios eliminaste? Envíame un correo electrónico si esto es más fácil: gutt.it/contact.htm
mgutt
43

¿Qué pasa con el uso de rawurlencode ()? http://www.php.net/manual/en/function.rawurlencode.php

Aquí hay una función que desinfecta incluso los caracteres chinos:

public static function normalizeString ($str = '')
{
    $str = strip_tags($str); 
    $str = preg_replace('/[\r\n\t ]+/', ' ', $str);
    $str = preg_replace('/[\"\*\/\:\<\>\?\'\|]+/', ' ', $str);
    $str = strtolower($str);
    $str = html_entity_decode( $str, ENT_QUOTES, "utf-8" );
    $str = htmlentities($str, ENT_QUOTES, "utf-8");
    $str = preg_replace("/(&)([a-z])([a-z]+;)/i", '$2', $str);
    $str = str_replace(' ', '-', $str);
    $str = rawurlencode($str);
    $str = str_replace('%', '-', $str);
    return $str;
}

Aquí está la explicación

  1. Eliminar etiquetas HTML
  2. Eliminar rotura / pestañas / carro de retorno
  3. Eliminar caracteres ilegales para carpeta y nombre de archivo
  4. Pon la cuerda en minúsculas
  5. Elimine los acentos extranjeros como Éàû convirtiéndolo en entidades html y luego elimine el código y conserve la letra.
  6. Reemplazar espacios con guiones
  7. Codifique caracteres especiales que podrían pasar los pasos anteriores e ingresar un nombre de archivo de conflicto en el servidor. ex. "中文 百强 网"
  8. Reemplace "%" con guiones para asegurarse de que el navegador no vuelva a escribir el enlace del archivo al consultar el archivo.

Bien, algún nombre de archivo no será relevante, pero en la mayoría de los casos funcionará.

ex. Nombre original: "საბეჭდი-და-ტიპოგრაფიული. Jpg"

Nombre de salida: "-E1-83-A1-E1-83-90-E1-83-91-E1-83-94-E1-83-AD-E1-83-93-E1-83-98 - E1- 83-93-E1-83-90 - E1-83-A2-E1-83-98-E1-83-9E-E1-83-9D-E1-83-92-E1-83-A0-E1-83 -90-E1-83-A4-E1-83-98-E1-83-A3-E1-83-9A-E1-83-98.jpg "

Es mejor así que un error 404.

Espero que haya sido útil.

Carl.

SequenceDigitale.com
fuente
1
No eliminará los caracteres NULL y Control. El ASCII de 0 a 32 debe eliminarse de la cadena.
Basil Musa
UTF-8 está permitido en el sistema de archivos y está permitido en las URL, entonces, ¿por qué debería producir un error 404? Lo único que tiene que hacer es codificar la URL http://www.maxrev.de/html/img/საბეჭდი_მანქანა.jpga http://www.maxrev.de/html/img/%E1%83%A1%E1%83%90%E1%83%91%E1%83%94%E1%83%AD%E1%83%93%E1%83%98_%E1%83%9B%E1%83%90%E1%83%9C%E1%83%A5%E1%83%90%E1%83%9C%E1%83%90.jpgen el código fuente HTML como es de esperar que lo hace con todas sus URL.
mgutt
1
Algunos otros puntos: eliminas las etiquetas HTML hasta el final strip_tags()y después las eliminas [<>]. Por eso strip_tags()realmente no se necesita en absoluto. El mismo punto son las citas. No quedan comillas cuando decodifica con ENT_QUOTES. Y str_replace()no elimina los espacios en blanco consecutivos y luego los usa strtolower()para una cadena de varios bytes. ¿Y por qué convierte a minúsculas? Y finalmente no captó ningún personaje reservado como mencionó @BasilMusa. Más detalles en mi respuesta: stackoverflow.com/a/42058764/318765
mgutt
me enamoré de ella!
Yash Kumar Verma
39

SOLUCIÓN 1 - simple y eficaz

$file_name = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $url ) );

  • strtolower () garantiza que el nombre del archivo está en minúsculas (ya que el caso no importa dentro de la URL, sino en el nombre del archivo NTFS)
  • [^a-z0-9]+ asegurará, el nombre del archivo solo conserva letras y números
  • Sustituya los caracteres no válidos con '-'mantiene legible el nombre del archivo

Ejemplo:

URL:  http://stackoverflow.com/questions/2021624/string-sanitizer-for-filename
File: http-stackoverflow-com-questions-2021624-string-sanitizer-for-filename

SOLUCIÓN 2 : para URL muy largas

Desea almacenar en caché el contenido de la URL y solo necesita tener nombres de archivo únicos. Usaría esta función:

$file_name = md5( strtolower( $url ) )

esto creará un nombre de archivo con una longitud fija. El hash MD5 es en la mayoría de los casos lo suficientemente único para este tipo de uso.

Ejemplo:

URL:  https://www.amazon.com/Interstellar-Matthew-McConaughey/dp/B00TU9UFTS/ref=s9_nwrsa_gw_g318_i10_r?_encoding=UTF8&fpl=fresh&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=desktop-1&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_t=36701&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_i=desktop
File: 51301f3edb513f6543779c3a5433b01c
Philipp
fuente
4
Quizás MD5 podría por un problema: tenga cuidado al usar hash con URL. Si bien la raíz cuadrada del número skrenta.com/2007/08/md5_tutorial.html de las URL sigue siendo mucho mayor que el tamaño actual de la web, si tiene una colisión, obtendrá páginas sobre Britney Spears cuando esperaba páginas. sobre Bugzilla. Probablemente no sea un problema en nuestro caso, pero para miles de millones de páginas optaría por un algoritmo hash mucho más grande como SHA 256 o lo evitaría por completo. Fuente: boyter.org/2013/01/code-for-a-search-engine-in-php-part-1
adilbo
15

Bueno, tempnam () lo hará por ti.

http://us2.php.net/manual/en/function.tempnam.php

pero eso crea un nombre completamente nuevo.

Para desinfectar una cadena existente, simplemente restrinja lo que sus usuarios pueden ingresar y conviértalo en letras, números, puntos, guiones y subrayados y luego desinfecte con una simple expresión regular. Compruebe qué caracteres deben escaparse o podría obtener falsos positivos.

$sanitized = preg_replace('/[^a-zA-Z0-9\-\._]/','', $filename);
Mark Moline
fuente
13
preg_replace("[^\w\s\d\.\-_~,;:\[\]\(\]]", '', $file)

Agregue / elimine más caracteres válidos dependiendo de lo que esté permitido para su sistema.

Alternativamente, puede intentar crear el archivo y luego devolver un error si es malo.

Tor Valamo
fuente
5
Eso permitiría a través de nombres de archivos como .., lo que puede ser un problema o no.
Dominic Rodger
@Dom: solo verifique eso por separado, ya que es un valor fijo.
Tor Valamo
10

PHP proporciona una función para desinfectar un texto a un formato diferente

filter.filters.sanitize

Cómo :

echo filter_var(
   "Lorem Ipsum has been the industry's",FILTER_SANITIZE_URL
); 

Blockquote LoremIpsumhasbeentheindustry's

120DEV
fuente
1
Bien, pero no eliminaría las barras, lo que podría ser un problema: Recorrido de directorio.
func0der
7

seguro: reemplace cada secuencia de NOT "a-zA-Z0-9_-" por un guión; agregue una extensión usted mismo.

$name = preg_replace('/[^a-zA-Z0-9_-]+/', '-', strtolower($name)).'.'.$extension;
commonpike
fuente
1
Necesita agregar la extensión del archivo separada por ".": $ Nombre = preg_replace ('/ [^ a-zA-Z0-9 _-] + /', '-', strtolower ($ nombre)). '.' . $ extensión;
Smith
6

La siguiente expresión crea una cadena agradable, limpia y utilizable:

/[^a-z0-9\._-]+/gi

Convirtiendo lo financiero de hoy: facturación en facturación financiera de hoy

Sampson
fuente
¿Entonces un nombre de archivo no puede tener un punto o un guión bajo, o algo por el estilo?
Tor Valamo
2
@Jonathan: ¿qué pasa con las cursivas?
Dominic Rodger
@Tor, sí, lo siento. Actualizado. @ Dominic, solo enfatizando el texto.
Sampson
¿Qué es el gism? Recibo "Advertencia: preg_replace () [function.preg-replace]: Modificador desconocido 'g'"
user151841
1
@ user151841 Para preg_replaceel indicador global está implícito. Por tanto, no es necesario utilizar g si se utiliza preg_replace. Cuando queremos controlar el número de reemplazos, preg_replace tiene un limitparámetro para eso. Lea la documentación de preg_replace para obtener más información.
rineez
6

Haciendo un pequeño ajuste a la solución de Sean Vieira para permitir puntos individuales, podría usar:

preg_replace("([^\w\s\d\.\-_~,;:\[\]\(\)]|[\.]{2,})", '', $file)
Carl Johnson
fuente
2

Estos pueden ser un poco pesados, pero son lo suficientemente flexibles como para desinfectar cualquier cuerda en una "caja fuerte". en nombre de archivo o carpeta de estilo (o diablos, incluso babosas limpias y cosas si se dobla).

1) Crear un nombre de archivo completo (con un nombre alternativo en caso de que la entrada esté totalmente truncada):

str_file($raw_string, $word_separator, $file_extension, $fallback_name, $length);

2) O usando solo la utilidad de filtro sin construir un nombre de archivo completo (el modo estricto trueno permitirá [] o () en el nombre de archivo):

str_file_filter($string, $separator, $strict, $length);

3) Y aquí están esas funciones:

// Returns filesystem-safe string after cleaning, filtering, and trimming input
function str_file_filter(
    $str,
    $sep = '_',
    $strict = false,
    $trim = 248) {

    $str = strip_tags(htmlspecialchars_decode(strtolower($str))); // lowercase -> decode -> strip tags
    $str = str_replace("%20", ' ', $str); // convert rogue %20s into spaces
    $str = preg_replace("/%[a-z0-9]{1,2}/i", '', $str); // remove hexy things
    $str = str_replace("&nbsp;", ' ', $str); // convert all nbsp into space
    $str = preg_replace("/&#?[a-z0-9]{2,8};/i", '', $str); // remove the other non-tag things
    $str = preg_replace("/\s+/", $sep, $str); // filter multiple spaces
    $str = preg_replace("/\.+/", '.', $str); // filter multiple periods
    $str = preg_replace("/^\.+/", '', $str); // trim leading period

    if ($strict) {
        $str = preg_replace("/([^\w\d\\" . $sep . ".])/", '', $str); // only allow words and digits
    } else {
        $str = preg_replace("/([^\w\d\\" . $sep . "\[\]\(\).])/", '', $str); // allow words, digits, [], and ()
    }

    $str = preg_replace("/\\" . $sep . "+/", $sep, $str); // filter multiple separators
    $str = substr($str, 0, $trim); // trim filename to desired length, note 255 char limit on windows

    return $str;
}


// Returns full file name including fallback and extension
function str_file(
    $str,
    $sep = '_',
    $ext = '',
    $default = '',
    $trim = 248) {

    // Run $str and/or $ext through filters to clean up strings
    $str = str_file_filter($str, $sep);
    $ext = '.' . str_file_filter($ext, '', true);

    // Default file name in case all chars are trimmed from $str, then ensure there is an id at tail
    if (empty($str) && empty($default)) {
        $str = 'no_name__' . date('Y-m-d_H-m_A') . '__' . uniqid();
    } elseif (empty($str)) {
        $str = $default;
    }

    // Return completed string
    if (!empty($ext)) {
        return $str . $ext;
    } else {
        return $str;
    }
}

Entonces, digamos que alguna entrada del usuario es: .....&lt;div&gt;&lt;/div&gt;<script></script>&amp; Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული

Y queremos convertirlo en algo más amigable para hacer un tar.gz con una longitud de nombre de archivo de 255 caracteres. Aquí hay un ejemplo de uso. Nota: este ejemplo incluye una extensión tar.gz con formato incorrecto como prueba de concepto, aún debe filtrar la extensión después de que la cadena se construya contra su lista blanca.

$raw_str = '.....&lt;div&gt;&lt;/div&gt;<script></script>&amp; Weiß Göbel 中文百强网File name  %20   %20 %21 %2C Décor  \/.  /. .  z \... y \...... x ./  “This name” is & 462^^ not &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული';
$fallback_str = 'generated_' . date('Y-m-d_H-m_A');
$bad_extension = '....t&+++a()r.gz[]';

echo str_file($raw_str, '_', $bad_extension, $fallback_str);

La salida sería: _wei_gbel_file_name_dcor_._._._z_._y_._x_._this_name_is_462_not_that_grrrreat_][09]()1234747)_.tar.gz

Puedes jugar con él aquí: https://3v4l.org/iSgi8

O una esencia: https://gist.github.com/dhaupin/b109d3a8464239b7754a

EDITAR: filtro de script actualizado para en &nbsp;lugar de espacio, enlace 3v4l actualizado

Dhaupin
fuente
1

Lo mejor que sé hoy es el método estático Strings :: webalize de Nette framework.

Por cierto, esto traduce todos los signos diacríticos a su básico .. š => s ü => u ß => ss etc.

Para los nombres de archivo, debe agregar un punto "." al parámetro de caracteres permitidos.

/**
 * Converts to ASCII.
 * @param  string  UTF-8 encoding
 * @return string  ASCII
 */
public static function toAscii($s)
{
    static $transliterator = NULL;
    if ($transliterator === NULL && class_exists('Transliterator', FALSE)) {
        $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII');
    }

    $s = preg_replace('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s);
    $s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06");
    $s = str_replace(
        array("\xE2\x80\x9E", "\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x9A", "\xE2\x80\x98", "\xE2\x80\x99", "\xC2\xB0"),
        array("\x03", "\x03", "\x03", "\x02", "\x02", "\x02", "\x04"), $s
    );
    if ($transliterator !== NULL) {
        $s = $transliterator->transliterate($s);
    }
    if (ICONV_IMPL === 'glibc') {
        $s = str_replace(
            array("\xC2\xBB", "\xC2\xAB", "\xE2\x80\xA6", "\xE2\x84\xA2", "\xC2\xA9", "\xC2\xAE"),
            array('>>', '<<', '...', 'TM', '(c)', '(R)'), $s
        );
        $s = @iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s); // intentionally @
        $s = strtr($s, "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e"
            . "\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3"
            . "\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
            . "\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe"
            . "\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
            'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.');
        $s = preg_replace('#[^\x00-\x7F]++#', '', $s);
    } else {
        $s = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); // intentionally @
    }
    $s = str_replace(array('`', "'", '"', '^', '~', '?'), '', $s);
    return strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?');
}


/**
 * Converts to web safe characters [a-z0-9-] text.
 * @param  string  UTF-8 encoding
 * @param  string  allowed characters
 * @param  bool
 * @return string
 */
public static function webalize($s, $charlist = NULL, $lower = TRUE)
{
    $s = self::toAscii($s);
    if ($lower) {
        $s = strtolower($s);
    }
    $s = preg_replace('#[^a-z0-9' . preg_quote($charlist, '#') . ']+#i', '-', $s);
    $s = trim($s, '-');
    return $s;
}
DnD
fuente
¿Por qué quiere reemplazar los signos diacríticos? Simplemente use urlencode()antes de usar el nombre de archivo como srco href. El único sistema de archivos utilizado actualmente que tiene problemas con UTF-8 es FATx (utilizado por XBOX): en.wikipedia.org/wiki/Comparison_of_file_systems#Limits Y no creo que esto sea utilizado por servidores web
mgutt
1

Parece que todo esto depende de la pregunta: ¿es posible crear un nombre de archivo que pueda usarse para piratear un servidor (o hacer algún otro daño)? Si no es así, entonces parece que la respuesta simple es intentar crear el archivo donde, en última instancia, se utilizará (ya que ese será el sistema operativo de elección, sin duda). Deje que el sistema operativo lo resuelva. Si se queja, transfiera esa queja al usuario como un error de validación.

Esto tiene el beneficio adicional de ser confiablemente portátil, ya que todos los sistemas operativos (estoy bastante seguro) se quejarán si el nombre del archivo no está formado correctamente para ese sistema operativo.

Si es posible hacer cosas nefastas con un nombre de archivo, tal vez haya medidas que se puedan aplicar antes de probar el nombre de archivo en el sistema operativo residente; medidas menos complicadas que un "saneamiento" completo del nombre de archivo.

ReverseEMF
fuente
0

de una sola mano

$bad='/[\/:*?"<>|]/';
$string = 'fi?le*';

function sanitize($str,$pat)
{
    return preg_replace($pat,"",$str);

}
echo sanitize($string,$bad);
ghostdog74
fuente
¿Qué pasa con los caracteres no imprimibles? En este caso, es mejor utilizar el enfoque de lista blanca que el enfoque de lista negra. Básicamente, permita solo los nombres de archivo ASCII imprimibles, excluyendo las letras especiales, por supuesto. Pero para los lugares que no están en inglés, ese es otro problema.
TheRealChx101
0

/y ..en el nombre de archivo proporcionado por el usuario puede ser perjudicial. Entonces deberías deshacerte de estos con algo como:

$fname = str_replace('..', '', $fname);
$fname = str_replace('/',  '', $fname);
juego terminado
fuente
¡Esto es insuficiente! Por ejemplo, el nombre de archivo "./.name" seguirá saliendo del directorio actual. (Eliminar ... no hace nada aquí, pero eliminar / convertirá el ./. En .. y, por lo tanto, saldrá del directorio de destino.)
cemper93
3
@ cemper93 No, esta respuesta solo convertirá la cadena en la ..nameque no saldrá de nada. Eliminar todos los caracteres separadores de ruta debería ser suficiente para evitar cualquier cruce de directorio. (La eliminación de ..es técnicamente innecesaria.)
cdhowie
@cdhowie Sí, pero el nombre del archivo se ./.convierte en ... Y finalmente esta respuesta pierde todos los demás caracteres reservados del sistema de archivos como NULL. Más en mi respuesta: stackoverflow.com/a/42058764/318765
mgutt
-4

$ fname = str_replace ('/', '', $ fname);

Dado que los usuarios pueden usar la barra para separar dos palabras, sería mejor reemplazarla con un guión en lugar de NULL

usuario2246924
fuente
¿Dónde se dice que reemplazaría con NULL? Además, esto no maneja todos los caracteres especiales.
Travis Pessetto
Sí, hay otros caracteres especiales que también necesitan ser tratados. str_replace no será la mejor oferta aquí de todos modos.
Martin Kovachev