文件名字符串消毒剂

我正在寻找一个 php 函数,它将清理一个字符串,并使其准备用于文件名。有人知道一个方便的吗?

(我可以写一个,但是我担心我会忽略一个角色!)

编辑: 用于在 WindowsNTFS 文件系统上保存文件。

192323 次浏览

下面的表达式创建了一个漂亮、干净和可用的字符串:

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

今日财经: 账单变成 今日财务账单

preg_replace("[^\w\s\d\.\-_~,;:\[\]\(\]]", '', $file)

添加/删除更多有效字符,具体取决于系统允许使用哪些字符。

或者,您可以尝试创建文件,然后返回一个错误,如果它是坏的。

与其担心忽略字符——不如使用一个你乐于使用的字符白名单?例如,您可以只允许 ol’a-z0-9_和一个句点的单个实例(.)。这显然比大多数文件系统限制更多,但应该可以保证您的安全。

Tempnam 会帮你的。

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

但这创造了一个全新的名字。

要清除现有的字符串,只需限制用户可以输入的内容,并将其设置为字母、数字、句点、连字符和下划线,然后使用简单的正则表达式清除。检查哪些字符需要转义,否则可能会得到错误的肯定。

$sanitized = preg_replace('/[^a-zA-Z0-9\-\._]/','', $filename);

/..在用户提供的文件名中可能是有害的。所以你应该通过这样的方式来摆脱它们:

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

对 Tor Valamo 的解决方案做一个小小的调整来解决 Dominic Rodger 注意到的问题,你 可以使用:

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

单行道

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


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


}
echo sanitize($string,$bad);

$fname = str _ place (’/’,”,$fname) ;

由于用户可能使用斜杠来分隔两个单词,所以最好用破折号替换,而不是 NULL

对 Sean Vieira 的解决方案做一个小小的调整,允许单个点,你可以使用:

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

使用 rawurlencode ()怎么样? Http://www.php.net/manual/en/function.rawurlencode.php

下面是一个甚至可以对中文字符进行消毒的函数:

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

这是解释

  1. 剥离 HTML 标签
  2. 删除断开/标签/返回运输
  3. 删除文件夹和文件名的非法字符
  4. 把字符串放在小写的地方
  5. 通过将其转换为 html 实体,删除诸如 Éàû 之类的外国口音,然后删除代码并保留字母。
  6. 用破折号替换空格
  7. 编码特殊的字符,这些字符可以通过前面的步骤并在 server.ex 上输入冲突文件名
  8. 将“%”替换为破折号,以确保在查询文件时浏览器不会重写该文件的链接。

好吧,有些文件名是不相关的,但在大多数情况下它会工作。

原名: something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something something jpg

输出名称: “-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-9D-E1-83-92-E1-83-A0-E1-83-90-E1-83-A4-E1-83-98-E1-83-A3-E1-83-E1-83-9A-E1-83-98. jpg”

这样总比404错误好。

希望这对你有帮助。

卡尔。

这些可能有点沉重,但是它们足够灵活,可以将任何字符串消毒成“安全”的 en样式的文件名或文件夹名(或者,如果你弯曲它,甚至擦洗的蛞蝓和其他东西)。

1)构建一个完整的文件名(在输入完全截断的情况下使用备用名称) :

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

2)或者只使用 filter util 而不构建完整的文件名(严格模式 true不允许在文件名中使用[]或()) :

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

3)这些功能如下:

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

假设一些用户输入是: .....&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) საბეჭდი-და-ტიპოგრაფიული

我们想把它转换成更友好的形式来创建一个 tar.gz 文件名长度为255个字符。下面是一个示例使用。注意: 这个例子包含一个格式不正确的 tar.gz 扩展作为概念验证,在根据您的白名单构建字符串之后,您仍然应该过滤文本。

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

输出将是: _wei_gbel_file_name_dcor_._._._z_._y_._x_._this_name_is_462_not_that_grrrreat_][09]()1234747)_.tar.gz

你可以在这里玩: https://3v4l.org/iSgi8

或者一个要点: https://gist.github.com/dhaupin/b109d3a8464239b7754a

编辑: 更新了 &nbsp;的脚本过滤器而不是空格,更新了3v4l 链接

今天我所知道的最好的是来自 Nette 框架的静态方法 字符串: : webalize

顺便说一句,这将所有符号翻译成它们的基本符号. . = > s ü = > u ß = > ss 等等。

对于文件名,您必须添加点“。”到允许的字符参数。

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

这就是您如何按要求清除文件系统的文件名的方法

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

文件系统中允许任何其他内容,因此这个问题得到了完美的回答... ..。

... 但是如果你稍后在不安全的 HTML 上下文中使用它,那么在文件名中允许单引号 '就是危险的,因为这个文件名是绝对合法的:

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

成为 XSS 洞:

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

正因为如此,流行的 CMS 软件 WordPress删除了它们,但是它们只在一些 更新之后覆盖了所有相关的字符:

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

最后,他们的列表现在包含了 URI 保留-字符URL 不安全字符列表中的大部分字符。

当然,您可以简单地在 HTML 输出上编码所有这些字符,但是大多数开发人员和我也遵循惯用语 “小心驶得万年船”并提前删除它们。

最后,我建议使用以下方法:

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://www.rfc-editor.org/rfc/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;
}

其他所有不会对文件系统造成问题的东西都应该是附加功能的一部分:

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

此时,如果结果为空,并且可以决定是否要编码 UTF-8字符,则需要生成文件名。但是你不需要,因为 UTF-8是允许在所有的文件系统中使用的网络托管上下文。

你要做的唯一一件事就是使用 urlencode()(希望你所有的 URL 都是这样) ,这样文件名 საბეჭდი_მანქანა.jpg就变成了这个 URL 作为你的 <img src>或者 <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 可以做到这一点,所以我可以像用户那样发布这个链接:
Http://www.maxrev.de/html/img/საბეჭდი_მანქანა.jpg

这是一个完整的合法文件名 没问题作为 @ SequenceDigitale.com

PHP 提供了一个将文本消毒为不同格式的函数

过滤,过滤,消毒

如何:

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

引用 LoremIpsumhasbeentheindustry's

解决方案1 -简单有效

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

  • Strtolower ()保证文件名是小写的(因为大小写在 URL 中不重要,但在 NTFS 文件名中重要)
  • [^a-z0-9]+将确保,文件名只保留字母和数字
  • '-'替换无效字符可以保持文件名的可读性

例如:

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

解决方案2 -对于非常长的 URL

您想要缓存 URL 内容,只需要有唯一的文件名即可。 我将使用这个函数:

$file_name = md5( strtolower( $url ) )

这将创建一个具有固定长度的文件名。在大多数情况下,MD5散列对于这种使用来说是惟一的。

例如:

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

似乎这一切都取决于这个问题,是否有可能创建一个文件名,可以用来黑客入侵服务器(或做一些-这样的其他损害)。如果没有,那么看起来简单的答案是尝试创建文件,无论它最终在哪里被使用(因为这将是操作系统的选择,毫无疑问)。让操作系统自己解决。如果它抱怨,将该抱怨作为验证错误移植回用户。

这样做还有一个额外的好处,那就是可以可靠地移植,因为所有(我很确定)操作系统都会抱怨文件名的格式不适合该操作系统。

如果 可以使用文件名做一些邪恶的事情,那么在常驻操作系统上测试文件名之前,也许可以应用一些措施——这些措施没有文件名的完整“卫生”那么复杂。

安全: 将 NOT“ a-zA-Z0-9 _-”的每个序列替换为一个破折号; 自己加一个延期。

$name = preg_replace('/[^a-zA-Z0-9_-]+/', '-', strtolower($name)).'.'.$extension;

所以一个 PDF 文件

"This is a grüte test_service +/-30 thing"

变成了

"This-is-a-gr-te-test_service-30-thing.pdf"
function sanitize_file_name($file_name) {
// case of multiple dots
$explode_file_name =explode('.', $file_name);
$extension =array_pop($explode_file_name);
$file_name_without_ext=substr($file_name, 0, strrpos( $file_name, '.') );
// replace special characters
$file_name_without_ext = preg_quote($file_name_without_ext);
$file_name_without_ext = preg_replace('/[^a-zA-Z0-9\\_]/', '_', $file_name_without_ext);
$file_name=$file_name_without_ext . '.' . $extension;
return $file_name;
}

使用这个函数只接受字符串中的 Words (Unicode 支持,例如 utf-8)和“ .”、“-”和“ _”:

$sanitized = preg_replace('/[^\w\-\._]/u','', $filename);