Je cherche une fonction php qui va assainir une chaîne et la rendre prête à utiliser pour un nom de fichier. Quelqu'un en connaît un pratique?
(Je pourrais en écrire un, mais je crains d'oublier un personnage!)
Edition: pour sauvegarder des fichiers sur un système de fichiers Windows NTFS.
Au lieu de vous soucier de négliger les personnages, pourquoi ne pas utiliser une liste blanche de personnages? Par exemple, vous pouvez autoriser simplement le bon vieux a-z
, 0-9
, _
, et une seule instance d'une période (.
). Ceci est évidemment plus limitant que la plupart des systèmes de fichiers, mais devrait vous protéger.
En apportant un petit ajustement à la solution de Tor Valamo pour résoudre le problème signalé par Dominic Rodger, vous pouvez utiliser:
// 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);
Pourquoi ne pas utiliser rawurlencode ()? http://www.php.net/manual/en/function.rawurlencode.php
Voici une fonction qui désinfecte même les caractères chinois:
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;
}
Voici l'explication
OK, certains noms de fichiers ne seront pas liés mais dans la plupart des cas, cela fonctionnera.
ex. Nom d'origine: "-და-ტიპოგრაფიული. Jpg"
Nom de la sortie: "-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 "
C'est mieux comme ça qu'une erreur 404.
J'espère que c'était utile.
Carl.
SOLUTION 1 - simple et efficace
$file_name = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $url ) );
[^a-z0-9]+
Assurera, le nom du fichier ne conserve que des lettres et des chiffres'-'
Pour que le nom du fichier soit lisibleExemple:
URL: http://stackoverflow.com/questions/2021624/string-sanitizer-for-filename
File: http-stackoverflow-com-questions-2021624-string-sanitizer-for-filename
SOLUTION 2 - pour les très longues URL
Vous voulez mettre en cache le contenu de l'URL et vous devez simplement avoir un nom de fichier unique. Je voudrais utiliser cette fonction:
$file_name = md5( strtolower( $url ) )
cela créera un nom de fichier avec une longueur fixe. Le hash MD5 est dans la plupart des cas assez unique pour ce type d’utilisation.
Exemple:
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
Voici comment vous pouvez désinfecter un système de fichiers comme demandé
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;
}
Tout le reste est autorisé dans un système de fichiers, la réponse à la question est donc parfaite ...
... mais il pourrait être dangereux d'autoriser par exemple les guillemets simples '
dans un nom de fichier si vous l'utilisez plus tard dans un contexte HTML non sécurisé, car ce nom de fichier absolument légal:
' onerror= 'alert(document.cookie).jpg
devient un trou XSS :
<img src='<? echo $image ?>' />
// output:
<img src=' ' onerror= 'alert(document.cookie)' />
À cause de cela, le logiciel populaire de CMS Wordpress le supprime et ils apprennent année par année à travers le chemin dur (nombreux rapports de bugs) qu'il est utile d'ajouter de plus en plus de caractères:
$special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
// ... a few rows later are whitespaces removed as well ...
preg_replace( '/[\r\n\t -]+/', '-', $filename )
Enfin, leur liste inclut maintenant la plupart des caractères qui font partie de la liste caractères redirigés et caractères non protégés par une URL .
Bien sûr, vous pouvez simplement encoder tous ces caractères sur une sortie HTML, mais la plupart des développeurs et moi-même suivons l'idiome "Mieux vaut prévenir que guérir" et les supprimer à l'avance.
Donc, finalement, je suggérerais d'utiliser ceci:
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;
}
Tout ce qui ne pose pas de problème avec le système de fichiers doit faire partie d'une fonction supplémentaire:
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;
}
Et à ce stade, vous devez générer un nom de fichier si le résultat est vide et vous pouvez décider si vous souhaitez coder les caractères UTF-8. Mais vous n'en avez pas besoin, car UTF-8 est autorisé dans tous les systèmes de fichiers utilisés dans des contextes d'hébergement Web.
La seule chose que vous devez faire est d’utiliser urlencode()
(comme vous le feriez avec toutes vos URL) pour que le nom de fichier საბეჭდი_მანქანა.jpg
Devienne cette URL en tant que <img src>
Ou <a href>
: http://www.maxrev.de/html/img/%E1%83%A1%E1%83%90%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 le fait, je peux donc poster ce lien comme un utilisateur le ferait:
http://www.maxrev.de/html/img/ საბეჭდი_. jpg
Donc, ceci est un nom de fichier légal complet et pas un problème comme @ SequenceDigitale.com mentionné dans sa réponse .
preg_replace("[^\w\s\d\.\-_~,;:\[\]\(\]]", '', $file)
Ajouter/supprimer plus de caractères valides en fonction de ce qui est autorisé pour votre système.
Sinon, vous pouvez essayer de créer le fichier, puis de renvoyer une erreur si elle est mauvaise.
Tempnam () le fera pour vous.
http://us2.php.net/manual/en/function.tempnam.php
mais cela crée un nom entièrement nouveau.
Pour effacer une chaîne existante, limitez simplement ce que vos utilisateurs peuvent saisir et faites en lettres, chiffres, points, traits d'union et traits de soulignement, puis effacez-les avec une simple expression régulière. Vérifiez quels caractères doivent être évités ou vous pourriez obtenir de faux positifs.
$sanitized = preg_replace('/[^a-zA-Z0-9\-\._]/','', $filename);
PHP fournit une fonction pour assainir un texte en différents formats
Comment :
echo filter_var(
"Lorem Ipsum has been the industry's",FILTER_SANITIZE_URL
);
Blockquote
LoremIpsumhasbeentheindustry's
L'expression suivante crée une chaîne agréable, propre et utilisable:
/[^a-z0-9\._-]+/gi
Conversion facturation financière actuelle en facturation financière aujourd'hui
En apportant un petit ajustement à la solution de Sean Vieira pour permettre les points simples, vous pouvez utiliser:
preg_replace("([^\w\s\d\.\-_~,;:\[\]\(\)]|[\.]{2,})", '', $file)
Celles-ci sont peut-être un peu lourdes, mais elles sont suffisamment flexibles pour transformer n'importe quelle chaîne en un nom de fichier ou nom de dossier "sûr" de style en
(ou diable, même épuré, même si vous le pliez).
1) Construire un nom de fichier complet (avec un nom de repli dans le cas où l'entrée est totalement tronquée):
str_file($raw_string, $Word_separator, $file_extension, $fallback_name, $length);
2) Ou en utilisant simplement le filtre util sans construire un nom de fichier complet (le mode strict true
ne permettra pas [] ou () dans le nom du fichier):
str_file_filter($string, $separator, $strict, $length);
3) Et voici ces fonctions:
// 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(" ", ' ', $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;
}
}
Donc, supposons que certaines entrées utilisateur soient: .....<div></div><script></script>& Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული
Et nous voulons le convertir en quelque chose de plus convivial pour créer un fichier tar.gz avec une longueur de nom de fichier de 255 caractères. Voici un exemple d'utilisation. Remarque: cet exemple inclut une extension tar.gz malformée en guise de validation technique. Vous devez néanmoins filtrer l'extension après que la chaîne a été créée en fonction de votre liste blanche.
$raw_str = '.....<div></div><script></script>& Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not = 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);
Le résultat serait: _wei_gbel_file_name_dcor_._._._z_._y_._x_._this_name_is_462_not_that_grrrreat_][09]()1234747)_.tar.gz
Vous pouvez y jouer ici: https://3v4l.org/iSgi8
Ou un élément essentiel: https://Gist.github.com/dhaupin/b109d3a8464239b7754a
EDIT: filtre de script mis à jour pour
Au lieu de l'espace, lien 3v4l mis à jour
Il semble que tout dépend de la question: est-il possible de créer un nom de fichier qui peut être utilisé pour pirater un serveur (ou causer un dommage quelconque)? Sinon, il semble que la solution simple soit d'essayer de créer le fichier là où il sera utilisé (en fin de compte, c'est le système d'exploitation de votre choix, sans aucun doute). Laissez le système d’exploitation le résoudre. S'il se plaint, signalez-le à l'utilisateur sous forme d'erreur de validation.
Cela présente l'avantage supplémentaire d'être portable de manière fiable, car tous les systèmes d'exploitation (j'en suis presque sûr) se plaindront si le nom du fichier n'est pas correctement formé pour ce système d'exploitation.
Si est possible de faire des choses néfastes avec un nom de fichier, certaines mesures peuvent éventuellement être appliquées avant de tester le nom de fichier sur le système d'exploitation résident - mesures moins compliqué qu'un "assainissement" complet du nom de fichier.
Le meilleur que je connaisse aujourd'hui est la méthode statique Strings :: webalize du framework Nette.
BTW, cela traduit tous les signes diacritiques à leur base .. š => s ü => u ß => ss etc.
Pour les noms de fichiers, vous devez ajouter un point "." paramètre de caractères autorisés.
/**
* 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;
}
/
et ..
dans le nom de fichier fourni par l'utilisateur peut être dangereux. Donc, vous devriez vous en débarrasser par quelque chose comme:
$fname = str_replace('..', '', $fname);
$fname = str_replace('/', '', $fname);
safe: remplace chaque séquence de NOT "a-zA-Z0-9_-" par un tiret; ajoutez vous-même une extension.
$name = preg_replace('/[^a-zA-Z0-9_-]+/', '-', strtolower($name)).$extension;
une manière
$bad='/[\/:*?"<>|]/';
$string = 'fi?le*';
function sanitize($str,$pat)
{
return preg_replace($pat,"",$str);
}
echo sanitize($string,$bad);