string sanitizer para nome de arquivo

113

Estou procurando uma função php que irá higienizar uma string e torná-la pronta para ser usada para um nome de arquivo. Alguém conhece um útil?

(Eu poderia escrever um, mas estou preocupado em esquecer um personagem!)

Editar: para salvar arquivos em um sistema de arquivos Windows NTFS.

user151841
fonte
1
Você pode ser mais específico: O que acontecerá com os tremas (remover ou converter em caractere de base?) O que acontecerá com os caracteres especiais?
Pekka
Para qual sistema de arquivos? Eles são diferentes. Veja en.wikipedia.org/wiki/…
Gordon
Windows :) Precisa de 15 caracteres.
user151841
1
Gostaria de salientar que as soluções de "lista negra" sugeridas em algumas das respostas não são suficientes, pois é inviável verificar todos os caracteres indesejáveis ​​possíveis (além dos caracteres especiais, há caracteres com acentos e tremas, inteiros alfabetos não ingleses / latinos, caracteres de controle, etc. para lidar com). Portanto, eu diria que uma abordagem de "lista branca" é sempre melhor, e normalizar a string (como sugerido pelo comentário de Blair McMillan sobre a resposta de Dominic Rodger) permitirá o tratamento natural de quaisquer letras com acentos, tremas, etc.
Sean the Bean
Uma boa maneira, talvez usando expressões regulares, veja este script python que fiz: github.com/gsscoder/normalize-fn
gsscoder

Respostas:

41

Em vez de se preocupar em ignorar os personagens - que tal usar uma lista branca de personagens que você gostaria de usar? Por exemplo, você poderia permitir que apenas o bom e velho a-z, 0-9, _, e uma única instância de um ponto ( .). Obviamente, isso é mais limitante do que a maioria dos sistemas de arquivos, mas deve mantê-lo seguro.

Dominic Rodger
fonte
40
Não é bom para idiomas com tremas. Isso resultaria em Qubec para Québec, Dsseldorf para Düsseldorf e assim por diante.
Pekka
15
Verdade - mas como eu disse: "Por exemplo".
Dominic Rodger
5
O que pode ser perfeitamente aceitável para o OP. Caso contrário, use algo como php.net/manual/en/class.normalizer.php
Blair McMillan
3
Na verdade, não foi isso que foi perguntado. O op pede uma função para higienizar a string, não uma alternativa.
i.am.michiel
3
@ i.am.michiel, talvez, mas dado que o OP o aceitou, presumo que o consideraram útil.
Dominic Rodger
157

Fazendo um pequeno ajuste na solução de Tor Valamo para corrigir o problema notado por Dominic Rodger, você poderia 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
fonte
43
Eu amo viciados em regex! -_ ~
AVProgrammer
2
@ iim.hlk - sim, estava faltando o parêntese de embrulho. Eu adicionei aqueles agora. Obrigado!
Sean Vieira
2
há uma falha aí, você deve dividi-la em duas e executar a verificação ..depois. Por exemplo, .?.acabaria sendo ... Embora, como você filtra, /eu não consiga ver como você exploraria isso ainda mais agora, mas isso mostra por que a verificação de ..é ineficaz aqui. Melhor ainda, provavelmente, não substitua, apenas rejeite se não se qualificar.
falstro
2
Porque nenhum desses valores é ilegal no sistema de arquivos do Windows e por que perder mais informações do que você precisa? Você pode alterar a expressão regular para simplesmente [^a-z0-9_-]se quiser ser realmente restritivo - ou apenas usar um nome gerado e descartar o nome fornecido e evitar todos esses problemas. :-)
Sean Vieira
3
Observe que: é ilegal.
JasonXA
49

É assim que você pode limpar um sistema de arquivos conforme solicitado

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 o resto é permitido em um sistema de arquivos, então a pergunta está perfeitamente respondida ...

... mas pode ser perigoso permitir, por exemplo, aspas simples 'em um nome de arquivo se você usá-lo posteriormente em um contexto HTML não seguro porque este nome de arquivo absolutamente legal:

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

torna-se um buraco XSS :

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

Por causa disso, o popular software CMS Wordpress os remove, mas eles cobrem todos os caracteres relevantes somente após algumas atualizações :

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

Finalmente sua lista inclui agora a maioria dos personagens que fazem parte da URI rerserved-personagens e URL caracteres inseguros lista.

É claro que você poderia simplesmente codificar todos esses caracteres na saída HTML, mas a maioria dos desenvolvedores e eu também, seguimos a expressão "Melhor prevenir do que remediar" e excluí-los antecipadamente.

Então, finalmente, sugiro usar isso:

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;
}

Tudo o mais que não cause problemas com o sistema de arquivos deve fazer parte de uma função 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;
}

E, neste ponto, você precisa gerar um nome de arquivo se o resultado estiver vazio e você pode decidir se deseja codificar caracteres UTF-8. Mas você não precisa disso, pois o UTF-8 é permitido em todos os sistemas de arquivos usados ​​em contextos de hospedagem na web.

A única coisa que você precisa fazer é usar urlencode()(como você espera fazer com todos os seus URLs) para que o nome do arquivo საბეჭდი_მანქანა.jpgse torne este URL como seu <img src>ou <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 faz isso, então posso postar este link como um usuário faria:
http://www.maxrev.de/html/img/ საბეჭდი_მანქანა. Jpg

Portanto, este é um nome de arquivo legal completo e não é um problema, como @ SequenceDigitale.com mencionou em sua resposta .

mgutt
fonte
3
Bom trabalho. A resposta mais útil para mim. +1
Ah ... A função funciona bem, mas há algum tempo começou a colocar - entre todos os personagens, tipo r-u-l-e-se não tenho ideia do porquê isso aconteceu. Claro é que não é culpa da função, mas apenas perguntar - qual seria o motivo de tal comportamento? Codificação errada?
1
Bem ... Acabei de fazer um debug e acontece logo após o preg_replacein filter_filename().
Depois de remover esses comentários, ele começou a funcionar novamente.
Quais comentários você removeu? Envie-me um e-mail se for mais fácil: gutt.it/contact.htm
mgutt
43

Que tal usar rawurlencode ()? http://www.php.net/manual/en/function.rawurlencode.php

Esta é uma função que higieniza até mesmo os caracteres chineses:

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;
}

Aqui está a explicação

  1. Remover tags HTML
  2. Remover quebra / abas / carro de retorno
  3. Remover caracteres ilegais para pasta e nome de arquivo
  4. Coloque a corda em minúsculas
  5. Remova acentos estrangeiros, como Éàû, convertendo-o em entidades html e, em seguida, remova o código e mantenha a letra.
  6. Substitua os espaços por travessões
  7. Codifique caracteres especiais que poderiam passar pelas etapas anteriores e insira um nome de arquivo em conflito no servidor. ex. "中文 百强 网"
  8. Substitua "%" por travessões para garantir que o link do arquivo não seja reescrito pelo navegador ao consultar o arquivo.

OK, algum nome de arquivo não será vantajoso, mas na maioria dos casos funcionará.

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

Nome de saída: "-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 "

É melhor assim do que um erro 404.

Espero que tenha sido útil.

Carl.

SequenceDigitale.com
fonte
1
Você não está removendo caracteres NULL e Control. ASCII de 0 a 32 deve ser removido da string.
Basil Musa
UTF-8 é permitido no sistema de arquivos e em URLs, então por que deveria produzir um erro 404? A única coisa que você precisa fazer é codificar o URL http://www.maxrev.de/html/img/საბეჭდი_მანქანა.jpgpara 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.jpgno código fonte HTML como você faz espero que com todos os seus URLs.
mgutt de
1
Alguns outros pontos: Você remove as tags HTML por completo strip_tags()e depois disso [<>]. Por isso strip_tags()não é realmente necessário. O mesmo ponto são as aspas. Não há aspas quando você decodifica com ENT_QUOTES. E o str_replace()não remove os espaços em branco consecutivos e, em seguida, você usa strtolower()para string multibyte. E por que você converte para minúsculas? E finalmente você não pegou nenhum personagem reservado como @BasilMusa mencionou. Mais detalhes em minha resposta: stackoverflow.com/a/42058764/318765
mgutt
me apaixonei por isso!
Yash Kumar Verma
39

SOLUÇÃO 1 - simples e eficaz

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

  • strtolower () garante que o nome do arquivo está em minúsculas (já que maiúsculas e minúsculas não importam dentro da URL, mas no nome de arquivo NTFS)
  • [^a-z0-9]+ irá garantir, o nome do arquivo mantém apenas letras e números
  • Substituir caracteres inválidos por '-'mantém o nome do arquivo legível

Exemplo:

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

SOLUÇÃO 2 - para URLs muito longos

Você deseja armazenar em cache o conteúdo da URL e só precisa ter nomes de arquivo exclusivos. Eu usaria esta função:

$file_name = md5( strtolower( $url ) )

isso criará um nome de arquivo com comprimento fixo. O hash MD5 é, na maioria dos casos, exclusivo o suficiente para esse tipo de uso.

Exemplo:

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
fonte
4
Talvez MD5 pudesse por um problema: Tenha cuidado ao usar hashes com URL. Embora a raiz quadrada do número skrenta.com/2007/08/md5_tutorial.html de URLs ainda seja muito maior do que o tamanho da web atual, se houver uma colisão, você obterá páginas sobre Britney Spears quando esperava páginas sobre o Bugzilla. Provavelmente, não é um problema em nosso caso, mas para bilhões de páginas, eu optaria por um algoritmo de hashing muito maior, como SHA 256, ou o evitaria por completo. Fonte: boyter.org/2013/01/code-for-a-search-engine-in-php-part-1
adilbo
15

Bem, tempnam () fará isso por você.

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

mas isso cria um nome totalmente novo.

Para limpar uma string existente, basta restringir o que seus usuários podem inserir e transformá-la em letras, números, ponto, hífen e sublinhado e então limpar com um regex simples. Verifique quais caracteres precisam ser escapados ou você pode obter falsos positivos.

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

Adicione / remova mais caracteres válidos dependendo do que é permitido para seu sistema.

Como alternativa, você pode tentar criar o arquivo e retornar um erro se ele estiver ruim.

Tor Valamo
fonte
5
Isso permitiria ..usar nomes de arquivo como , o que pode ou não ser um problema.
Dominic Rodger
@Dom - verifique isso separadamente, pois é um valor fixo.
Tor Valamo
10

PHP fornece uma função para limpar um texto para um formato diferente

filter.filters.sanitize

Como :

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

Bloco de citação LoremIpsumhasbeentheindustry's

120DEV
fonte
1
Ótimo, mas não removeria barras, o que poderia ser um problema: travessia de diretório.
func0der
6

A expressão a seguir cria uma string agradável, limpa e utilizável:

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

Transformando o financeiro de hoje: faturamento em faturamento financeiro de hoje

Sampson
fonte
então um nome de arquivo não pode ter um ponto ou um sublinhado, ou algo assim?
Tor Valamo
2
@ Jonathan - o que é esse itálico?
Dominic Rodger
@Tor, sim, desculpe. Atualizada. @Dominic, apenas dando ênfase ao texto.
Sampson
O que é gism? Recebo "Aviso: preg_replace () [function.preg-replace]: Modificador desconhecido 'g'"
user151841
1
@ user151841 Pois preg_replaceo sinalizador global está implícito. Portanto, não há necessidade de g se preg_replace estiver sendo usado. Quando queremos controlar o número de substituições preg_replace tem um limitparâmetro para isso. Leia a documentação preg_replace para mais.
rineez
6

Fazendo um pequeno ajuste na solução de Sean Vieira para permitir pontos únicos, você pode usar:

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

seguro: substitua toda sequência de NÃO "a-zA-Z0-9_-" por um travessão; adicione uma extensão você mesmo.

$name = preg_replace('/[^a-zA-Z0-9_-]+/', '-', strtolower($name)).'.'.$extension;
pique comum
fonte
1
Você precisa adicionar a extensão do arquivo separada por um ".": $ Name = preg_replace ('/ [^ a-zA-Z0-9 _-] + /', '-', strtolower ($ name)). '.' . $ extension;
Smith
2

Eles podem ser um pouco pesados, mas são flexíveis o suficiente para limpar qualquer string em um ennome de arquivo ou pasta de estilo "seguro" (ou diabos, até mesmo slugs limpos e outras coisas se você dobrar).

1) Construindo um nome de arquivo completo (com nome substituto caso a entrada esteja totalmente truncada):

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

2) Ou usando apenas o utilitário de filtro sem construir um nome de arquivo completo (o modo estrito truenão permitirá [] ou () no nome do arquivo):

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

3) E aqui estão essas funções:

// 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;
    }
}

Então, digamos que alguma entrada do usuário seja: .....&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) საბეჭდი-და-ტიპოგრაფიული

E queremos convertê-lo para algo mais amigável para fazer um tar.gz com um tamanho de nome de arquivo de 255 caracteres. Aqui está um exemplo de uso. Observação: este exemplo inclui uma extensão tar.gz malformada como uma prova de conceito, você ainda deve filtrar a extensão depois que a string for criada em sua (s) lista (s) branca (s).

$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);

A saída seria: _wei_gbel_file_name_dcor_._._._z_._y_._x_._this_name_is_462_not_that_grrrreat_][09]()1234747)_.tar.gz

Você pode brincar com ele aqui: https://3v4l.org/iSgi8

Ou um Síntese: https://gist.github.com/dhaupin/b109d3a8464239b7754a

EDITAR: filtro de script atualizado para em &nbsp;vez de espaço, link 3v4l atualizado

Dhaupin
fonte
1

O melhor que sei hoje é o método estático Strings :: webalize do framework Nette.

BTW, isso traduz todos os sinais diacríticos para seus básicos .. š => s ü => u ß => ss etc.

Para nomes de arquivos, você deve adicionar o ponto "." para o 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
fonte
Por que você deseja substituir os diacríticos? Basta usar urlencode()antes de usar o nome do arquivo como srcou href. O único sistema de arquivos usado atualmente que tem problemas com UTF-8 é o FATx (usado pelo XBOX): en.wikipedia.org/wiki/Comparison_of_file_systems#Limits E eu não acho que isso seja usado por servidores web
mgutt
1

Parece que tudo depende da questão, é possível criar um nome de arquivo que pode ser usado para invadir um servidor (ou causar algum outro dano). Do contrário, parece que a resposta simples é tentar criar o arquivo onde quer que ele seja, em última instância, usado (já que esse será o sistema operacional de escolha, sem dúvida). Deixe o sistema operacional resolver isso. Se houver reclamação, devolva a reclamação ao usuário como um erro de validação.

Isso tem o benefício adicional de ser portável de maneira confiável, já que todos (tenho certeza) os sistemas operacionais reclamarão se o nome do arquivo não for formado corretamente para aquele SO.

Se isso for possível fazer coisas nefastas com um nome de arquivo, talvez haja medidas que possam ser aplicadas antes de testar o nome do arquivo no sistema operacional residente - medidas menos complicadas do que uma "limpeza" completa do nome do arquivo.

ReverseEMF
fonte
0

mão única

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

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

}
echo sanitize($string,$bad);
ghostdog74
fonte
E quanto aos caracteres não imprimíveis? É melhor usar a abordagem de lista branca do que a abordagem de lista negra neste caso. Basicamente, permite apenas os nomes de arquivo ASCII imprimíveis, excluindo as letras especiais do curso. Mas para localidades diferentes do inglês, esse é outro problema.
TheRealChx101
0

/e ..no nome de arquivo fornecido pelo usuário pode ser prejudicial. Portanto, você deve se livrar deles por meio de algo como:

$fname = str_replace('..', '', $fname);
$fname = str_replace('/',  '', $fname);
fim de jogo
fonte
Isso é insuficiente! Por exemplo, o nome do arquivo "./.name" ainda sairá do diretório atual. (Remover .. não faz nada aqui, mas remover / irá transformar o ./. Em .. e, portanto, sair do diretório de destino.)
cemper93
3
@ cemper93 Não, esta resposta apenas tornará o barbante ..nameque não sairá de nada. A remoção de todos os caracteres separadores de caminho deve ser suficiente para evitar qualquer passagem de diretório. (A remoção de ..é tecnicamente desnecessária.)
cdhowie
@cdhowie Sim, mas o nome do arquivo ./.se torna ... E, finalmente, esta resposta perde todos os outros caracteres reservados do sistema de arquivos, como NULL. Mais em minha resposta: stackoverflow.com/a/42058764/318765
mgutt
-4

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

Uma vez que os usuários podem usar a barra para separar duas palavras, seria melhor substituí-la por um hífen em vez de NULL

user2246924
fonte
Onde está dito que ele estaria substituindo por NULL? Além disso, isso não controla todos os caracteres especiais.
Travis Pessetto
Sim - existem outros caracteres especiais que também precisam de tratamento. str_replace não será o melhor lance aqui de qualquer maneira.
Martin Kovachev