it-swarm.com.de

Strings bereinigen, um URL und Dateinamen sicher zu machen?

Ich versuche, eine Funktion zu entwickeln, die bestimmte Strings gut desinfiziert, so dass sie sicher in der URL verwendet werden können (wie ein Post-Slug) und auch als Dateinamen verwendet werden können. Wenn zum Beispiel jemand eine Datei hochlädt, möchte ich sicherstellen, dass alle gefährlichen Zeichen aus dem Namen entfernt werden.

Bisher bin ich mit der folgenden Funktion gekommen, die hoffentlich dieses Problem löst und auch fremde UTF-8-Daten zulässt.

/**
 * 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');
}

Hat jemand irgendwelche kniffligen Beispieldaten, gegen die ich dagegen laufen kann - oder einen besseren Weg kennen, unsere Apps vor schlechten Namen zu schützen?

$ is-filename erlaubt einige zusätzliche Zeichen wie temporäre vim-Dateien

update: Sternzeichen entfernt, da mir keine gültige Verwendung einfällt

130
Xeoncross

Einige Anmerkungen zu Ihrer Lösung:

  1. 'u' am Ende Ihres Musters bedeutet, dass das Muster und nicht der dazu passende Text als UTF-8 interpretiert wird (ich nehme an, Sie haben letzteres angenommen?). 
  2. \ w stimmt mit dem Unterstrich überein. Sie schließen es speziell für Dateien ein, was zu der Annahme führt, dass Sie sie nicht in URLs verwenden möchten, aber in dem Code, den Sie verwenden, URLs einen Unterstrich enthalten dürfen.
  3. Die Aufnahme von "fremder UTF-8" scheint abhängig von der Ländereinstellung zu sein. Es ist nicht klar, ob dies die Ländereinstellung des Servers oder Clients ist. Aus den PHP -Dokumenten:

Ein "Word" -Zeichen ist ein Buchstabe, eine Ziffer oder der Unterstrich, dh jedes Zeichen, das Teil eines Perl- "Word" sein kann. Die Definition von Buchstaben und Ziffern wird durch die Zeichentabellen von PCRE gesteuert und kann variieren, wenn ein ländereinstellungsspezifischer Abgleich stattfindet. Beispielsweise werden in der Ländereinstellung "fr" (Französisch) einige Zeichencodes, die größer als 128 sind, für Buchstaben mit Akzent verwendet, und diese werden mit\w abgeglichen.

Die Schnecke erstellen

Sie sollten in Ihrem Post-Slug wahrscheinlich keine mit Akzenten versehenen usw. Zeichen verwenden, da diese in Prozent (pro URL-Codierungsregeln) kodiert sein sollten, damit hässlich aussehende URLs angezeigt werden.

Wenn ich Sie wäre, würde ich also nach dem Verkleinern alle 'Sonderzeichen' in ihre Entsprechungen umwandeln (z. B. é -> e) und Nicht-[az] -Zeichen durch '-' ersetzen, wobei die Anzahl der einzelnen '-' eingeschränkt wird. wie du getan hast. Es gibt eine Implementierung zum Konvertieren von Sonderzeichen hier: https://web.archive.org/web/20130208144021/http://neo22s.com/slug

Desinfektion im Allgemeinen

OWASP verfügt über eine PHP -Implementierung der Enterprise Security-API, zu der unter anderem Methoden für die sichere Kodierung und Dekodierung von Ein- und Ausgaben in Ihrer Anwendung gehören. 

Die Encoder-Schnittstelle bietet:

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-ESAPIhttps://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API

56
Alan Donnelly

Ich habe diese größere Funktion im Chyrp Code gefunden:

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

und dies im 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);
}

Update September 2012

Alix Axel hat in diesem Bereich unglaubliche Arbeit geleistet. Sein Phunction-Framework enthält mehrere großartige Textfilter und Transformationen.

86
Xeoncross

Dies sollte Ihre Dateinamen sicher machen ...

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

und eine tiefere Lösung dafür ist:

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

Dies setzt voraus, dass Sie einen Punkt im Dateinamen haben möchten. Wenn Sie ihn in Kleinbuchstaben übertragen möchten, verwenden Sie einfach

$clean_name = strtolower($clean_name);

für die letzte Zeile.

30
SoLoGHoST

Versuche dies:

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

Basierend auf der ausgewählten Antwort in diesem Thread: URL Freundlicher Benutzername in PHP?

21
John Conde

Dies ist keine exakte Antwort, da es (noch!) Keine Lösungen bietet, aber es ist zu groß, um auf einen Kommentar zu passen ...


Ich habe einige Tests (in Bezug auf Dateinamen) unter Windows 7 und Ubuntu 12.04 durchgeführt und dabei Folgendes herausgefunden:

1. PHP Kann keine Nicht-ASCII-Dateinamen verarbeiten

Obwohl sowohl Windows als auch Ubuntu Unicode-Dateinamen verarbeiten können (auch RTL-Dateinamen), erfordert PHP 5.3 Hacks, um auch mit der alten ISO-8859-1 fertig zu werden. Daher ist es besser, sie ASCII nur zur Sicherheit.

2. Die Länge des Dateinamens ist wichtig (speziell unter Windows)

Unter Ubuntu beträgt die maximale Länge eines Dateinamens (einschließlich Erweiterung) 255 (ohne Pfad):

/var/www/uploads/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345/

Unter Windows 7 (NTFS) hängt die maximale Länge eines Dateinamens jedoch vom absoluten Pfad ab:

(0 + 0 + 244 + 11 chars) C:\1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\1234567.txt
(0 + 3 + 240 + 11 chars) C:\123\123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\1234567.txt
(3 + 3 + 236 + 11 chars) C:\123\456\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1234567.txt

Wikipedia sagt, dass:

NTFS erlaubt , dass jede Pfadkomponente (Verzeichnis oder Dateiname) 255 Zeichen lang ist.

Meines Wissens (und beim Testen) ist dies falsch.

Insgesamt (Schrägstriche zählen) haben alle diese Beispiele 259 Zeichen, wenn Sie den C:\ entfernen, der 256 Zeichen ergibt (nicht 255 ?!). Die Verzeichnisse wurden mit dem Explorer erstellt, und Sie werden feststellen, dass er nicht den gesamten verfügbaren Speicherplatz für den Verzeichnisnamen belegt. Der Grund dafür ist, die Erstellung von Dateien mit der 8.3-Dateinamenskonvention zu ermöglichen. Dasselbe passiert für andere Partitionen.

Dateien müssen natürlich nicht die 8.3-Längenanforderungen reservieren:

(255 chars) E:\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901.txt

Sie können keine weiteren Unterverzeichnisse erstellen, wenn der absolute Pfad des übergeordneten Verzeichnisses mehr als 242 Zeichen enthält, da 256 = 242 + 1 + \ + 8 + . + 3. Mit Windows Explorer können Sie kein anderes Verzeichnis erstellen, wenn das übergeordnete Verzeichnis mehr als 233 Zeichen enthält (abhängig vom Gebietsschema des Systems), da 256 = 233 + 10 + \ + 8 + . + 3; der 10 ist hier die länge des strings New folder.

Das Windows-Dateisystem stellt ein unangenehmes Problem dar, wenn Sie die Interoperabilität zwischen Dateisystemen sicherstellen möchten.

3. Vorsicht vor reservierten Zeichen und Schlüsselwörtern

Neben dem Entfernen von Nicht-ASCII-, Nicht-Druck- und Steuerzeichen müssen Sie auch Folgendes neu (platzieren/verschieben):

"*/:<>?\|

Das Entfernen dieser Zeichen ist möglicherweise nicht die beste Idee, da der Dateiname möglicherweise an Bedeutung verliert. Ich denke, dass mindestens mehrere Vorkommen dieser Zeichen durch einen einzelnen Unterstrich (_) oder durch etwas Repräsentativeres (dies ist nur eine Idee) ersetzt werden sollten:

  • "*? -> _
  • /\| -> -
  • : -> [ ]-[ ]
  • < -> (
  • > -> )

Es gibt auch spezielle Schlüsselwörter, die vermieden werden sollten (wie NUL), obwohl ich nicht sicher bin, wie ich das überwinden soll. Vielleicht wäre eine schwarze Liste mit einem zufälligen Namensfallback ein guter Ansatz, um das Problem zu lösen.

4. Groß-/Kleinschreibung

Dies sollte selbstverständlich sein, aber wenn Sie die Eindeutigkeit von Dateien auf verschiedenen Betriebssystemen sicherstellen möchten, sollten Sie die Dateinamen in einen normalisierten Fall umwandeln. Auf diese Weise werden unter Linux my_file.txt und My_File.txt nicht beide zu derselben my_file.txt-Datei unter Windows.

5. Stellen Sie sicher, dass es einzigartig ist

Wenn der Dateiname bereits vorhanden ist, sollte ein eindeutiger Bezeichner an den Basisdateinamen angehängt werden.

Zu den gebräuchlichen eindeutigen Bezeichnern gehören der UNIX-Zeitstempel, ein Digest des Dateiinhalts oder eine zufällige Zeichenfolge.

6. Versteckte Dateien

Nur weil es benannt werden kann, heißt das nicht, dass es ...

Punkte werden normalerweise in Dateinamen auf die weiße Liste gesetzt, aber in Linux wird eine versteckte Datei durch einen führenden Punkt dargestellt.

7. Andere Überlegungen

Wenn Sie einige Zeichen des Dateinamens entfernen müssen, ist die Erweiterung in der Regel wichtiger als der Basisname der Datei. Erlaubt man ein beträchtliche maximale Anzahl von Zeichen für die Dateierweiterung (8-16), sollte man die Zeichen aus dem Basisnamen entfernen. Es ist auch wichtig zu beachten, dass in diesem Fall in dem unwahrscheinlichen Fall, dass mehr als eine lange Erweiterung vorhanden ist, wie z. B. _.graphmlz.tag.gz - _.graphmlz.tag, nur _ als Dateibasisname berücksichtigt werden sollte.

8. Ressourcen

Calibre handhabt das Zerfleischen von Dateinamen ziemlich anständig:

Wikipedia-Seite über das Mangeln von Dateinamen und verknüpft Kapitel aus Using Samba .


Wenn Sie beispielsweise versuchen, eine Datei zu erstellen, die gegen eine der Regeln 1/2/3 verstößt, wird ein sehr nützlicher Fehler angezeigt:

Warning: touch(): Unable to create file ... because No error in ... on line ...
13
Alix Axel

Ich habe immer gedacht Kohana hat es ziemlich gut gemacht .

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

Die praktische UTF8::transliterate_to_ascii() wird Sachen wie ñ => n drehen.

Natürlich können Sie die anderen UTF8::*-Sachen durch mb_ * -Funktionen ersetzen.

11
alex

und das ist Joomla 3.3.2 Version von JFile::makeSafe($file)

public static function makeSafe($file)
{
    // Remove any trailing dots, as those aren't ever valid file names.
    $file = rtrim($file, '.');

    $regex = array('#(\.){2,}#', '#[^A-Za-z0-9\.\_\- ]#', '#^\.#');

    return trim(preg_replace($regex, '', $file));
}
5
cedric.walter

Ich habe mich von einer anderen Quelle angepasst und ein paar zusätzliche hinzugefügt, vielleicht ein wenig übertrieben

/**
 * Convert a string into a url safe address.
 *
 * @param string $unformatted
 * @return string
 */
public function formatURL($unformatted) {

    $url = strtolower(trim($unformatted));

    //replace accent characters, forien languages
    $search = array('À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'ß', 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', 'Ā', 'ā', 'Ă', 'ă', 'Ą', 'ą', 'Ć', 'ć', 'Ĉ', 'ĉ', 'Ċ', 'ċ', 'Č', 'č', 'Ď', 'ď', 'Đ', 'đ', 'Ē', 'ē', 'Ĕ', 'ĕ', 'Ė', 'ė', 'Ę', 'ę', 'Ě', 'ě', 'Ĝ', 'ĝ', 'Ğ', 'ğ', 'Ġ', 'ġ', 'Ģ', 'ģ', 'Ĥ', 'ĥ', 'Ħ', 'ħ', 'Ĩ', 'ĩ', 'Ī', 'ī', 'Ĭ', 'ĭ', 'Į', 'į', 'İ', 'ı', 'IJ', 'ij', 'Ĵ', 'ĵ', 'Ķ', 'ķ', 'Ĺ', 'ĺ', 'Ļ', 'ļ', 'Ľ', 'ľ', 'Ŀ', 'ŀ', 'Ł', 'ł', 'Ń', 'ń', 'Ņ', 'ņ', 'Ň', 'ň', 'ʼn', 'Ō', 'ō', 'Ŏ', 'ŏ', 'Ő', 'ő', 'Œ', 'œ', 'Ŕ', 'ŕ', 'Ŗ', 'ŗ', 'Ř', 'ř', 'Ś', 'ś', 'Ŝ', 'ŝ', 'Ş', 'ş', 'Š', 'š', 'Ţ', 'ţ', 'Ť', 'ť', 'Ŧ', 'ŧ', 'Ũ', 'ũ', 'Ū', 'ū', 'Ŭ', 'ŭ', 'Ů', 'ů', 'Ű', 'ű', 'Ų', 'ų', 'Ŵ', 'ŵ', 'Ŷ', 'ŷ', 'Ÿ', 'Ź', 'ź', 'Ż', 'ż', 'Ž', 'ž', 'ſ', 'ƒ', 'Ơ', 'ơ', 'Ư', 'ư', 'Ǎ', 'ǎ', 'Ǐ', 'ǐ', 'Ǒ', 'ǒ', 'Ǔ', 'ǔ', 'Ǖ', 'ǖ', 'Ǘ', 'ǘ', 'Ǚ', 'ǚ', 'Ǜ', 'ǜ', 'Ǻ', 'ǻ', 'Ǽ', 'ǽ', 'Ǿ', 'ǿ'); 
    $replace = array('A', 'A', 'A', 'A', 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 's', 'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'y', 'A', 'a', 'A', 'a', 'A', 'a', 'C', 'c', 'C', 'c', 'C', 'c', 'C', 'c', 'D', 'd', 'D', 'd', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'G', 'g', 'G', 'g', 'G', 'g', 'G', 'g', 'H', 'h', 'H', 'h', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'IJ', 'ij', 'J', 'j', 'K', 'k', 'L', 'l', 'L', 'l', 'L', 'l', 'L', 'l', 'l', 'l', 'N', 'n', 'N', 'n', 'N', 'n', 'n', 'O', 'o', 'O', 'o', 'O', 'o', 'OE', 'oe', 'R', 'r', 'R', 'r', 'R', 'r', 'S', 's', 'S', 's', 'S', 's', 'S', 's', 'T', 't', 'T', 't', 'T', 't', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'W', 'w', 'Y', 'y', 'Y', 'Z', 'z', 'Z', 'z', 'Z', 'z', 's', 'f', 'O', 'o', 'U', 'u', 'A', 'a', 'I', 'i', 'O', 'o', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'A', 'a', 'AE', 'ae', 'O', 'o'); 
    $url = str_replace($search, $replace, $url);

    //replace common characters
    $search = array('&', '£', '$'); 
    $replace = array('and', 'pounds', 'dollars'); 
    $url= str_replace($search, $replace, $url);

    // remove - for spaces and union characters
    $find = array(' ', '&', '\r\n', '\n', '+', ',', '//');
    $url = str_replace($find, '-', $url);

    //delete and replace rest of special chars
    $find = array('/[^a-z0-9\-<>]/', '/[\-]+/', '/<[^>]*>/');
    $replace = array('', '-', '');
    $uri = preg_replace($find, $replace, $url);

    return $uri;
}
5
John Magnolia

In Bezug auf das Hochladen von Dateien ist es am sichersten, wenn der Benutzer den Dateinamen nicht kontrolliert. Wie bereits angedeutet, speichern Sie den kanonisierten Dateinamen in einer Datenbank zusammen mit einem zufällig ausgewählten und eindeutigen Namen, den Sie als tatsächlichen Dateinamen verwenden.

Mit OWASP ESAPI könnten diese Namen folgendermaßen generiert werden:

$userFilename   = ESAPI::getEncoder()->canonicalize($input_string);
$safeFilename   = ESAPI::getRandomizer()->getRandomFilename();

Sie könnten einen Zeitstempel an den $ safeFilename anhängen, um sicherzustellen, dass der zufällig generierte Dateiname eindeutig ist, ohne nach einer vorhandenen Datei suchen zu müssen.

In Bezug auf die Codierung für URL und erneut unter Verwendung von ESAPI:

$safeForURL     = ESAPI::getEncoder()->encodeForURL($input_string);

Diese Methode führt die Kanonisierung vor der Codierung der Zeichenfolge durch und verarbeitet alle Zeichencodierungen.

5
jah

Ich empfehle * URLify für PHP (480+ Sterne auf Github) - "der PHP - Port von URLify.js aus dem Django-Projekt. Transliteriert Nicht-ASCII-Zeichen für die Verwendung in URLs ".

Grundnutzung:

So generieren Sie Slugs für URLs:

<?php

echo URLify::filter (' J\'étudie le français ');
// "jetudie-le-francais"

echo URLify::filter ('Lo siento, no hablo español.');
// "lo-siento-no-hablo-espanol"

?>

So generieren Sie Slugs für Dateinamen:

<?php

echo URLify::filter ('фото.jpg', 60, "", true);
// "foto.jpg"

?>

* Keiner der anderen Vorschläge entsprach meinen Kriterien:

  • Sollte per Composer installiert werden können
  • Sollte sich nicht auf iconv verlassen, da es sich auf verschiedenen Systemen unterschiedlich verhält
  • Sollte erweiterbar sein, um Überschreibungen und benutzerdefinierte Zeichenersetzungen zu ermöglichen
  • Beliebt (zum Beispiel viele Sterne auf Github)
  • Hat Tests

Als Bonus entfernt URLify auch bestimmte Wörter und entfernt alle nicht transliterierten Zeichen. 

Hier ist ein Testfall, bei dem tonnenweise Fremdzeichen mithilfe von URLify transliteriert werden: https://Gist.github.com/motin/a65e6c1cc303e46900d10894bf2da87f

4
Motin

Ich denke nicht, dass es sicher ist, eine Liste von Zeichen zu entfernen. Ich möchte lieber folgendes verwenden:

Für Dateinamen: Verwenden Sie eine interne ID oder einen Hashwert des Dateieintrags. Speichern Sie den Dokumentnamen in einer Datenbank. Auf diese Weise können Sie den ursprünglichen Dateinamen beibehalten und die Datei trotzdem finden.

Für URL-Parameter: Verwenden Sie urlencode(), um alle Sonderzeichen zu codieren.

4
ZeissS

Hier ist die Implementierung von CodeIgniter.

/**
 * Sanitize Filename
 *
 * @param   string  $str        Input file name
 * @param   bool    $relative_path  Whether to preserve paths
 * @return  string
 */
public function sanitize_filename($str, $relative_path = FALSE)
{
    $bad = array(
        '../', '<!--', '-->', '<', '>',
        "'", '"', '&', '$', '#',
        '{', '}', '[', ']', '=',
        ';', '?', '%20', '%22',
        '%3c',      // <
        '%253c',    // <
        '%3e',      // >
        '%0e',      // >
        '%28',      // (
        '%29',      // )
        '%2528',    // (
        '%26',      // &
        '%24',      // $
        '%3f',      // ?
        '%3b',      // ;
        '%3d'       // =
    );

    if ( ! $relative_path)
    {
        $bad[] = './';
        $bad[] = '/';
    }

    $str = remove_invisible_characters($str, FALSE);
    return stripslashes(str_replace($bad, '', $str));
}

Und die remove_invisible_characters Abhängigkeit.

function remove_invisible_characters($str, $url_encoded = TRUE)
{
    $non_displayables = array();

    // every control character except newline (dec 10),
    // carriage return (dec 13) and horizontal tab (dec 09)
    if ($url_encoded)
    {
        $non_displayables[] = '/%0[0-8bcef]/';  // url encoded 00-08, 11, 12, 14, 15
        $non_displayables[] = '/%1[0-9a-f]/';   // url encoded 16-31
    }

    $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';   // 00-08, 11, 12, 14-31, 127

    do
    {
        $str = preg_replace($non_displayables, '', $str, -1, $count);
    }
    while ($count);

    return $str;
}
3
Kevin Mark

Je nachdem, wie Sie es verwenden, möchten Sie möglicherweise eine Längenbegrenzung hinzufügen, um den Pufferüberlauf zu schützen.

3
Tgr

Es gibt bereits mehrere Lösungen für diese Frage, aber ich habe den größten Teil des Codes hier gelesen und getestet, und ich habe diese Lösung erhalten, die eine Mischung aus dem ist, was ich hier gelernt habe:

Die Funktion

Die Funktion ist hier in einem Symfony2 - Bundle gebündelt, kann aber extrahiert werden, um als plain PHP verwendet zu werden. Sie hat nur eine Abhängigkeit von der iconv-Funktion, die aktiviert werden muss:

Filesystem.php :

<?php

namespace COil\Bundle\COilCoreBundle\Component\HttpKernel\Util;

use Symfony\Component\HttpKernel\Util\Filesystem as BaseFilesystem;

/**
 * Extends the Symfony filesystem object.
 */
class Filesystem extends BaseFilesystem
{
    /**
     * Make a filename safe to use in any function. (Accents, spaces, special chars...)
     * The iconv function must be activated.
     *
     * @param string  $fileName       The filename to sanitize (with or without extension)
     * @param string  $defaultIfEmpty The default string returned for a non valid filename (only special chars or separators)
     * @param string  $separator      The default separator
     * @param boolean $lowerCase      Tells if the string must converted to lower case
     *
     * @author COil <https://github.com/COil>
     * @see    http://stackoverflow.com/questions/2668854/sanitizing-strings-to-make-them-url-and-filename-safe
     *
     * @return string
     */
    public function sanitizeFilename($fileName, $defaultIfEmpty = 'default', $separator = '_', $lowerCase = true)
    {
    // Gather file informations and store its extension
    $fileInfos = pathinfo($fileName);
    $fileExt   = array_key_exists('extension', $fileInfos) ? '.'. strtolower($fileInfos['extension']) : '';

    // Removes accents
    $fileName = @iconv('UTF-8', 'us-ascii//TRANSLIT', $fileInfos['filename']);

    // Removes all characters that are not separators, letters, numbers, dots or whitespaces
    $fileName = preg_replace("/[^ a-zA-Z". preg_quote($separator). "\d\.\s]/", '', $lowerCase ? strtolower($fileName) : $fileName);

    // Replaces all successive separators into a single one
    $fileName = preg_replace('!['. preg_quote($separator).'\s]+!u', $separator, $fileName);

    // Trim beginning and ending seperators
    $fileName = trim($fileName, $separator);

    // If empty use the default string
    if (empty($fileName)) {
        $fileName = $defaultIfEmpty;
    }

    return $fileName. $fileExt;
    }
}

Die Unit-Tests

Interessant ist, dass ich PHPUnit-Tests erstellt habe, um zuerst Edge-Fälle zu testen und so zu überprüfen, ob sie Ihren Anforderungen entsprechen: (Wenn Sie einen Fehler finden, fügen Sie einfach einen Testfall hinzu.)

FilesystemTest.php :

<?php

namespace COil\Bundle\COilCoreBundle\Tests\Unit\Helper;

use COil\Bundle\COilCoreBundle\Component\HttpKernel\Util\Filesystem;

/**
 * Test the Filesystem custom class.
 */
class FilesystemTest extends \PHPUnit_Framework_TestCase
{
    /**
     * test sanitizeFilename()
     */
    public function testFilesystem()
    {
    $fs = new Filesystem();

    $this->assertEquals('logo_orange.gif', $fs->sanitizeFilename('--logö  _  __   ___   [email protected]@ñ--~gé--.gif'), '::sanitizeFilename() handles complex filename with specials chars');
    $this->assertEquals('coilstack', $fs->sanitizeFilename('cOiLsTaCk'), '::sanitizeFilename() converts all characters to lower case');
    $this->assertEquals('cOiLsTaCk', $fs->sanitizeFilename('cOiLsTaCk', 'default', '_', false), '::sanitizeFilename() lower case can be desactivated, passing false as the 4th argument');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil stack'), '::sanitizeFilename() convert a white space to a separator');
    $this->assertEquals('coil-stack', $fs->sanitizeFilename('coil stack', 'default', '-'), '::sanitizeFilename() can use a different separator as the 3rd argument');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil          stack'), '::sanitizeFilename() removes successive white spaces to a single separator');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename('       coil stack'), '::sanitizeFilename() removes spaces at the beginning of the string');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil   stack         '), '::sanitizeFilename() removes spaces at the end of the string');
    $this->assertEquals('coilstack', $fs->sanitizeFilename('coil,,,,,,stack'), '::sanitizeFilename() removes non-ASCII characters');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil_stack  '), '::sanitizeFilename() keeps separators');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename(' coil________stack'), '::sanitizeFilename() converts successive separators into a single one');
    $this->assertEquals('coil_stack.gif', $fs->sanitizeFilename('cOil Stack.GiF'), '::sanitizeFilename() lower case filename and extension');
    $this->assertEquals('copy_of_coil.stack.exe', $fs->sanitizeFilename('Copy of coil.stack.exe'), '::sanitizeFilename() keeps dots before the extension');
    $this->assertEquals('default.doc', $fs->sanitizeFilename('____________.doc'), '::sanitizeFilename() returns a default file name if filename only contains special chars');
    $this->assertEquals('default.docx', $fs->sanitizeFilename('     ___ -  --_     __%%%%__¨¨¨***____      .docx'), '::sanitizeFilename() returns a default file name if filename only contains special chars');
    $this->assertEquals('logo_edition_1314352521.jpg', $fs->sanitizeFilename('logo_edition_1314352521.jpg'), '::sanitizeFilename() returns the filename untouched if it does not need to be modified');
    $userId = Rand(1, 10);
    $this->assertEquals('user_doc_'. $userId. '.doc', $fs->sanitizeFilename('亐亐亐亐亐.doc', 'user_doc_'. $userId), '::sanitizeFilename() returns the default string (the 2nd argument) if it can\'t be sanitized');
    }
}

Die Testergebnisse: (geprüft auf Ubuntu mit PHP 5.3.2 und MacOsX mit PHP 5.3.17:

All tests pass:

phpunit -c app/ src/COil/Bundle/COilCoreBundle/Tests/Unit/Helper/FilesystemTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

Configuration read from /var/www/strangebuzz.com/app/phpunit.xml.dist

.

Time: 0 seconds, Memory: 5.75Mb

OK (1 test, 17 assertions)
2
COil

Ich habe Eintragstitel mit allen möglichen seltsamen lateinischen Zeichen sowie einige HTML-Tags, die ich in ein nützliches, durch Bindestrich getrenntes Dateinamenformat übersetzen musste. Ich habe die Antwort von @ SoLoGHoST mit ein paar Elementen aus der Antwort von @ Xeoncross kombiniert und ein wenig angepasst.

    function sanitize($string,$force_lowercase=true) {
    //Clean up titles for filenames
    $clean = strip_tags($string);
    $clean = strtr($clean, 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 = strtr($clean, array('Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', 'Œ' => 'OE', 'œ' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u','—' => '-'));
    $clean = str_replace("--", "-", preg_replace("/[^a-z0-9-]/i", "", preg_replace(array('/\s/', '/[^\w-\.\-]/'), array('-', ''), $clean)));

    return ($force_lowercase) ?
        (function_exists('mb_strtolower')) ?
            mb_strtolower($clean, 'UTF-8') :
            strtolower($clean) :
        $clean;
}

Ich musste das Em-Strich-Zeichen (-) manuell zum Übersetzungsarray hinzufügen. Es mag andere geben, aber meine Dateinamen sehen bisher gut aus.

So:

Teil 1: "Žurburts" meines Vaters - sie sind (nicht) die Besten

wird:

Teil-1-meine-Väter-zurburts-theyre-nicht-der-beste

Ich füge einfach ".html" zum zurückgegebenen String hinzu.

2
cbmtrx

Dies ist ein guter Weg, um einen Upload-Dateinamen zu sichern:

$file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
2
games

Dieser Beitrag scheint unter allen, die ich gebunden habe, am besten zu funktionieren. http://gsynuh.com/php-string-dateiname-url-safe/205

1
ktamlyn

Lösung Nr. 1: Sie können PHP -Erweiterungen auf dem Server (Hosting) installieren.

Für die Transliteration von "fast jeder Sprache auf der Erde" in ASCII Zeichen.

  1. Installieren Sie zuerst PHP Intl extension. Dies ist der Befehl für Debian (Ubuntu): Sudo aptitude install php5-intl

  2. Dies ist meine fileName-Funktion (erstelle test.php und füge folgenden Code ein):

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<?php

function pr($string) {
  print '<hr>';
  print '"' . fileName($string) . '"';
  print '<br>';
  print '"' . $string . '"';
}

function fileName($string) {
  // remove html tags
  $clean = strip_tags($string);
  // transliterate
  $clean = transliterator_transliterate('Any-Latin;Latin-ASCII;', $clean);
  // remove non-number and non-letter characters
  $clean = str_replace('--', '-', preg_replace('/[^a-z0-9-\_]/i', '', preg_replace(array(
    '/\s/', 
    '/[^\w-\.\-]/'
  ), array(
    '_', 
    ''
  ), $clean)));
  // replace '-' for '_'
  $clean = strtr($clean, array(
    '-' => '_'
  ));
  // remove double '__'
  $positionInString = stripos($clean, '__');
  while ($positionInString !== false) {
    $clean = str_replace('__', '_', $clean);
    $positionInString = stripos($clean, '__');
  }
  // remove '_' from the end and beginning of the string
  $clean = rtrim(ltrim($clean, '_'), '_');
  // lowercase the string
  return strtolower($clean);
}
pr('_replace(\'~&([a-z]{1,2})(ac134/56f4315981743 8765475[]lt7ňl2ú5äňú138yé73ťž7ýľute|');
pr(htmlspecialchars('<script>alert(\'hacked\')</script>'));
pr('Álix----_Ãxel!?!?');
pr('áéíóúÁÉÍÓÚ');
pr('üÿÄËÏÖÜ.ŸåÅ');
pr('nie4č a a§ôňäääaš');
pr('Мао Цзэдун');
pr('毛泽东');
pr('ماو تسي تونغ');
pr('مائو تسه‌تونگ');
pr('מאו דזה-דונג');
pr('მაო ძედუნი');
pr('Mao Trạch Đông');
pr('毛澤東');
pr('เหมา เจ๋อตง');
?>
</body>
</html>

Diese Linie ist Kern:

  // transliterate
  $clean = transliterator_transliterate('Any-Latin;Latin-ASCII;', $clean);

Antwort basierend auf diesem Beitrag .

Lösung 2: Sie haben keine Möglichkeit, PHP -Erweiterungen auf dem Server (Hosting) zu installieren.

 enter image description here

Ziemlich gute Arbeit wird in Transliteration Module für CMS Drupal erledigt. Es unterstützt fast jede Sprache der Erde. Ich schlage vor, Plugin Repository zu überprüfen, wenn Sie wirklich vollständige Lösungsdesinfektions-Strings haben möchten.

1
Jasom Dotnet

Dies ist eine gute Funktion:

public function getFriendlyURL($string) {
    setlocale(LC_CTYPE, 'en_US.UTF8');
    $string = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $string);
    $string = preg_replace('~[^\-\pL\pN\s]+~u', '-', $string);
    $string = str_replace(' ', '-', $string);
    $string = trim($string, "-");
    $string = strtolower($string);
    return $string;
} 
1
joan16v

warum nicht einfach php's urlencode verwenden? es ersetzt "gefährliche" Zeichen mit ihrer Hex-Darstellung für URLs (d. h. %20 für ein Leerzeichen)

1
knittl

Dies ist der Code, mit dem Prestashop URLs desinfiziert:

replaceAccentedChars

wird von verwendet 

str2url

diakritiker zu entfernen

function replaceAccentedChars($str)
{
    $patterns = array(
        /* Lowercase */
        '/[\x{0105}\x{00E0}\x{00E1}\x{00E2}\x{00E3}\x{00E4}\x{00E5}]/u',
        '/[\x{00E7}\x{010D}\x{0107}]/u',
        '/[\x{010F}]/u',
        '/[\x{00E8}\x{00E9}\x{00EA}\x{00EB}\x{011B}\x{0119}]/u',
        '/[\x{00EC}\x{00ED}\x{00EE}\x{00EF}]/u',
        '/[\x{0142}\x{013E}\x{013A}]/u',
        '/[\x{00F1}\x{0148}]/u',
        '/[\x{00F2}\x{00F3}\x{00F4}\x{00F5}\x{00F6}\x{00F8}]/u',
        '/[\x{0159}\x{0155}]/u',
        '/[\x{015B}\x{0161}]/u',
        '/[\x{00DF}]/u',
        '/[\x{0165}]/u',
        '/[\x{00F9}\x{00FA}\x{00FB}\x{00FC}\x{016F}]/u',
        '/[\x{00FD}\x{00FF}]/u',
        '/[\x{017C}\x{017A}\x{017E}]/u',
        '/[\x{00E6}]/u',
        '/[\x{0153}]/u',

        /* Uppercase */
        '/[\x{0104}\x{00C0}\x{00C1}\x{00C2}\x{00C3}\x{00C4}\x{00C5}]/u',
        '/[\x{00C7}\x{010C}\x{0106}]/u',
        '/[\x{010E}]/u',
        '/[\x{00C8}\x{00C9}\x{00CA}\x{00CB}\x{011A}\x{0118}]/u',
        '/[\x{0141}\x{013D}\x{0139}]/u',
        '/[\x{00D1}\x{0147}]/u',
        '/[\x{00D3}]/u',
        '/[\x{0158}\x{0154}]/u',
        '/[\x{015A}\x{0160}]/u',
        '/[\x{0164}]/u',
        '/[\x{00D9}\x{00DA}\x{00DB}\x{00DC}\x{016E}]/u',
        '/[\x{017B}\x{0179}\x{017D}]/u',
        '/[\x{00C6}]/u',
        '/[\x{0152}]/u');

    $replacements = array(
            'a', 'c', 'd', 'e', 'i', 'l', 'n', 'o', 'r', 's', 'ss', 't', 'u', 'y', 'z', 'ae', 'oe',
            'A', 'C', 'D', 'E', 'L', 'N', 'O', 'R', 'S', 'T', 'U', 'Z', 'AE', 'OE'
        );

    return preg_replace($patterns, $replacements, $str);
}

function str2url($str)
{
    if (function_exists('mb_strtolower'))
        $str = mb_strtolower($str, 'utf-8');

    $str = trim($str);
    if (!function_exists('mb_strtolower'))
        $str = replaceAccentedChars($str);

    // Remove all non-whitelist chars.
    $str = preg_replace('/[^a-zA-Z0-9\s\'\:\/\[\]-\pL]/u', '', $str);
    $str = preg_replace('/[\s\'\:\/\[\]-]+/', ' ', $str);
    $str = str_replace(array(' ', '/'), '-', $str);

    // If it was not possible to lowercase the string with mb_strtolower, we do it after the transformations.
    // This way we lose fewer special chars.
    if (!function_exists('mb_strtolower'))
        $str = strtolower($str);

    return $str;
}
0
Armel Larcier

Es gibt zwei gute Antworten auf Ihre Daten, verwenden Sie sie https://stackoverflow.com/a/3987966/971619 oder https://stackoverflow.com/a/7610586/971619

0
newway