135 votes

Assainissement des chaînes de caractères pour les rendre sûres pour les URL et les noms de fichiers ?

J'essaie de trouver une fonction qui fasse un bon travail d'assainissement de certaines chaînes de caractères afin qu'elles puissent être utilisées en toute sécurité dans l'URL (comme le slug d'un article) et également en toute sécurité comme noms de fichiers. Par exemple, lorsque quelqu'un télécharge un fichier, je veux m'assurer que je supprime tous les caractères dangereux du nom.

Jusqu'à présent, j'ai mis au point la fonction suivante qui, je l'espère, résoudra ce problème et autorisera également les données étrangères UTF-8.

/**
 * Convert a string to the file/URL safe "slug" form
 *
 * @param string $string the string to clean
 * @param bool $is_filename TRUE will allow additional filename characters
 * @return string
 */
function sanitize($string = '', $is_filename = FALSE)
{
 // Replace all weird characters with dashes
 $string = preg_replace('/[^\w\-'. ($is_filename ? '~_\.' : ''). ']+/u', '-', $string);

 // Only allow one dash separator at a time (and make string lowercase)
 return mb_strtolower(preg_replace('/--+/u', '-', $string), 'UTF-8');
}

Est-ce que quelqu'un a des données d'échantillon délicates que je peux comparer à ces données ou connaît-il un meilleur moyen de protéger nos applications des mauvais noms ?

$is-filename permet quelques caractères supplémentaires, comme les fichiers vim temporaires.

mise à jour : suppression du caractère étoile car je n'ai pas trouvé d'utilisation valide

87voto

Xeoncross Points 13263

J'ai trouvé cette fonction plus importante dans le Chyrp code :

/**
 * Function: sanitize
 * Returns a sanitized string, typically for URLs.
 *
 * Parameters:
 *     $string - The string to sanitize.
 *     $force_lowercase - Force the string to lowercase?
 *     $anal - If set to *true*, will remove all non-alphanumeric characters.
 */
function sanitize($string, $force_lowercase = true, $anal = false) {
    $strip = array("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]",
                   "}", "\\", "|", ";", ":", "\"", "'", "‘", "’", "“", "”", "–", "—",
                   "—", "–", ",", "<", ".", ">", "/", "?");
    $clean = trim(str_replace($strip, "", strip_tags($string)));
    $clean = preg_replace('/\s+/', "-", $clean);
    $clean = ($anal) ? preg_replace("/[^a-zA-Z0-9]/", "", $clean) : $clean ;
    return ($force_lowercase) ?
        (function_exists('mb_strtolower')) ?
            mb_strtolower($clean, 'UTF-8') :
            strtolower($clean) :
        $clean;
}

et celui-ci dans le wordpress code

/**
 * Sanitizes a filename replacing whitespace with dashes
 *
 * Removes special characters that are illegal in filenames on certain
 * operating systems and special characters requiring special escaping
 * to manipulate at the command line. Replaces spaces and consecutive
 * dashes with a single dash. Trim period, dash and underscore from beginning
 * and end of filename.
 *
 * @since 2.1.0
 *
 * @param string $filename The filename to be sanitized
 * @return string The sanitized filename
 */
function sanitize_file_name( $filename ) {
    $filename_raw = $filename;
    $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}");
    $special_chars = apply_filters('sanitize_file_name_chars', $special_chars, $filename_raw);
    $filename = str_replace($special_chars, '', $filename);
    $filename = preg_replace('/[\s-]+/', '-', $filename);
    $filename = trim($filename, '.-_');
    return apply_filters('sanitize_file_name', $filename, $filename_raw);
}

Mise à jour septembre 2012

Alix Axel a fait un travail incroyable dans ce domaine. Son framework phunction comprend plusieurs filtres et transformations de texte de grande qualité.

57voto

Alan Donnelly Points 1133

Quelques observations sur votre solution :

  1. Le "u" à la fin de votre modèle signifie que le motif et non le texte qu'il met en correspondance sera interprété comme UTF-8 (je suppose que vous avez supposé que c'était le cas ?).
  2. \w correspond au caractère de soulignement. Vous l'incluez spécifiquement pour les fichiers, ce qui laisse supposer que vous ne les voulez pas dans les URL, mais dans le code que vous avez, les URL seront autorisées à inclure un trait de soulignement.
  3. L'inclusion de "foreign UTF-8" semble dépendre du contexte local. Il n'est pas clair s'il s'agit de la locale du serveur ou du client. Extrait de la documentation de PHP :

Un caractère de "mot" est une lettre ou un chiffre ou le caractère de soulignement, c'est-à-dire tout caractère qui peut faire partie d'un "mot" Perl. La définition des lettres et des chiffres est contrôlée par les tables de caractères du PCRE et peut varier si une correspondance locale est effectuée. Par exemple, dans la locale "fr" (français), certains codes de caractères supérieurs à 128 sont utilisés pour les lettres accentuées, et ils sont mis en correspondance avec les caractères suivants \w.

Création du slug

Vous ne devriez probablement pas inclure les caractères accentués dans le slug de votre message car, techniquement, ils devraient être encodés en pourcentage (selon les règles d'encodage des URL) et vous aurez des URL peu esthétiques.

Donc, si j'étais vous, après la mise en minuscule, je convertirais tous les caractères "spéciaux" en leur équivalent (par exemple é -> e) et je remplacerais les caractères non [a-z] par "-", en me limitant à des séries d'un seul "-" comme vous l'avez fait. Il y a une implémentation de la conversion des caractères spéciaux ici : https://web.archive.org/web/20130208144021/http://neo22s.com/slug

Assainissement en général

L'OWASP a une implémentation PHP de son API de sécurité d'entreprise qui, entre autres, inclut des méthodes pour coder et décoder en toute sécurité les entrées et sorties de votre application.

L'interface Encoder fournit :

canonicalize (string $input, [bool $strict = true])
decodeFromBase64 (string $input)
decodeFromURL (string $input)
encodeForBase64 (string $input, [bool $wrap = false])
encodeForCSS (string $input)
encodeForHTML (string $input)
encodeForHTMLAttribute (string $input)
encodeForJavaScript (string $input)
encodeForOS (Codec $codec, string $input)
encodeForSQL (Codec $codec, string $input)
encodeForURL (string $input)
encodeForVBScript (string $input)
encodeForXML (string $input)
encodeForXMLAttribute (string $input)
encodeForXPath (string $input)

https://github.com/OWASP/PHP-ESAPI https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API

30voto

SoLoGHoST Points 922

Cela devrait rendre vos noms de fichiers sûrs...

$string = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $string);

et une solution plus profonde à ce problème est :

// Remove special accented characters - ie. sí.
$clean_name = strtr($string, array('Š' => 'S','Ž' => 'Z','š' => 's','ž' => 'z','Ÿ' => 'Y','À' => 'A','Á' => 'A','Â' => 'A','Ã' => 'A','Ä' => 'A','Å' => 'A','Ç' => 'C','È' => 'E','É' => 'E','Ê' => 'E','Ë' => 'E','Ì' => 'I','Í' => 'I','Î' => 'I','Ï' => 'I','Ñ' => 'N','Ò' => 'O','Ó' => 'O','Ô' => 'O','Õ' => 'O','Ö' => 'O','Ø' => 'O','Ù' => 'U','Ú' => 'U','Û' => 'U','Ü' => 'U','Ý' => 'Y','à' => 'a','á' => 'a','â' => 'a','ã' => 'a','ä' => 'a','å' => 'a','ç' => 'c','è' => 'e','é' => 'e','ê' => 'e','ë' => 'e','ì' => 'i','í' => 'i','î' => 'i','ï' => 'i','ñ' => 'n','ò' => 'o','ó' => 'o','ô' => 'o','õ' => 'o','ö' => 'o','ø' => 'o','ù' => 'u','ú' => 'u','û' => 'u','ü' => 'u','ý' => 'y','ÿ' => 'y'));
$clean_name = strtr($clean_name, array('Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', 'Œ' => 'OE', 'œ' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u'));

$clean_name = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $clean_name);

Cela suppose que vous voulez un point dans le nom du fichier. Si vous voulez qu'il soit transféré en minuscule, utilisez simplement

$clean_name = strtolower($clean_name);

pour la dernière ligne.

21voto

John Conde Points 102874

Essayez ça :

function normal_chars($string)
{
    $string = htmlentities($string, ENT_QUOTES, 'UTF-8');
    $string = preg_replace('~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', '$1', $string);
    $string = html_entity_decode($string, ENT_QUOTES, 'UTF-8');
    $string = preg_replace(array('~[^0-9a-z]~i', '~[ -]+~'), ' ', $string);

    return trim($string, ' -');
}

Examples:

echo normal_chars('Álix----_Ãxel!?!?'); // Alix Axel
echo normal_chars('áéíóúÁÉÍÓÚ'); // aeiouAEIOU
echo normal_chars('üÿÄËÏÖÜŸåÅ'); // uyAEIOUYaA

En fonction de la réponse choisie dans ce fil : Nom d'utilisateur compatible avec l'URL en PHP ?

11voto

alex Points 186293

J'ai toujours pensé Kohana a fait un assez bon travail. .

public static function title($title, $separator = '-', $ascii_only = FALSE)
{
if ($ascii_only === TRUE)
{
// Transliterate non-ASCII characters
$title = UTF8::transliterate_to_ascii($title);

// Remove all characters that are not the separator, a-z, 0-9, or whitespace
$title = preg_replace('![^'.preg_quote($separator).'a-z0-9\s]+!', '', strtolower($title));
}
else
{
// Remove all characters that are not the separator, letters, numbers, or whitespace
$title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', UTF8::strtolower($title));
}

// Replace all separator characters and whitespace by a single separator
$title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);

// Trim separators from the beginning and end
return trim($title, $separator);
}

L'outil pratique UTF8::transliterate_to_ascii() va transformer des choses comme ñ => n.

Bien sûr, vous pouvez remplacer l'autre UTF8::* avec les fonctions mb_*.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X