没有符号链接解析的绝对路径/将用户保留在主目录中

发布于 2024-11-17 19:34:45 字数 186 浏览 1 评论 0原文

PHP 中有没有一种方法可以在不实际解析符号链接的情况下确定给定相对路径的绝对路径?类似于 realpath 函数,但没有符号链接解析。

或者,是否有一些简单的方法来检查用户(使用我的浏览 PHP 脚本查看文件)是否没有意外地走出 Apache 虚拟主机的主目录? (或者不允许他在路径中使用令人讨厌的 . 和 ..)

谢谢!

Is there a way in PHP how to determine an absolute path given a relative path without actually resolving symlinks? Something like the realpath function but without symlink resolution.

Or alternatively, is there some easy way to check whether user (who uses my browsing PHP script to view files) did not accidentally step out of Apache virtual host's home directory? (or disallow him to use the nasty . and .. in paths)

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心房敞 2024-11-24 19:34:45

我不知道 PHP 本机解决方案,但这里有一个很好的绝对路径实现 PHP 文档中的注释

因为 realpath() 不适用于不存在的文件,所以我编写了一个函数。
它将(连续)出现的 /\ 替换为 DIRECTORY_SEPARATOR 中的内容,并处理 /. 和 <代码>/.. 很好。
get_absolute_path() 返回的路径在位置 0(字符串开头)或位置 -1(结尾)处不包含(反)斜杠。

函数 get_absolute_path($path) {
   $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
   $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
   $绝对值=数组();
   foreach ($parts 作为 $part) {
       if ('.' == $part) 继续;
       if ('..' == $部分) {
           array_pop($absolutes);
       } 别的 {
           $绝对[] = $部分;
       }
   }
   返回内爆(DIRECTORY_SEPARATOR,$绝对);
}

测试:

var_dump(get_absolute_path('this/is/../a/./test/.///is'));

返回:

string(14) "this/a/test/is"

如您所见,它还产生尤达语。 :)

(针对格式和拼写错误进行了编辑)

I don't know PHP native solution for this, but here's one nice absolute path implementation from a comment in the PHP docs:

Because realpath() does not work on files that do not exist, I wrote a function that does.
It replaces (consecutive) occurences of / and \ with whatever is in DIRECTORY_SEPARATOR, and processes /. and /.. fine.
Paths returned by get_absolute_path() contain no (back)slash at position 0 (beginning of the string) or position -1 (ending).

function get_absolute_path($path) {
   $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
   $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
   $absolutes = array();
   foreach ($parts as $part) {
       if ('.' == $part) continue;
       if ('..' == $part) {
           array_pop($absolutes);
       } else {
           $absolutes[] = $part;
       }
   }
   return implode(DIRECTORY_SEPARATOR, $absolutes);
}

A test:

var_dump(get_absolute_path('this/is/../a/./test/.///is'));

Returns:

string(14) "this/a/test/is"

As you can see, it also produces Yoda-speak. :)

(edited for formatting & typos)

没有伤那来痛 2024-11-24 19:34:45

您可以使用 open_basedir 设置 ini php.ini 或虚拟主机声明(如 php_admin_value 指令)。

You can use open_basedir setting ini php.ini or in virtual host declaration (as php_admin_value directive).

隐诗 2024-11-24 19:34:45

这是我模仿原始 realpath() 的方法,它还:

  1. 添加/删除 Windows 驱动器号,如 c: 中所示。
  2. 删除尾部斜杠。
  3. 将当前工作目录添加到相对路径之前。
  4. 可以选择检查文件是否存在。如果文件不存在,A) 不执行任何操作,B) 返回 FALSE 值或 C) 抛出错误。
  5. 可以选择遵循符号链接。

注意:这种方法使用正则表达式,众所周知,正则表达式的 CPU 资源消耗很大,所以我喜欢使用 http://www.php.net/manual/en/function.realpath.php#84012

// Set constants for when a file does not exist.
// 0: Check file existence, set FALSE when file not exists.
define('FILE_EXISTENCE_CHECK', 0);
// 1: Require file existence, throw error when file not exists.
define('FILE_EXISTENCE_CHECK_REQUIRE_FILE', 1);
// 2: Do not check file existence.
define('FILE_EXISTENCE_CHECK_SKIP', 2);
// Windows flag.
define('IS_WINDOWS', preg_match('#WIN(DOWS|\d+|_?NT)#i', PHP_OS));
// Directory separator shortcuts.
define('DIR_SEP', DIRECTORY_SEPARATOR);
define('PREG_DIR_SEP', preg_quote(DIR_SEP));

/**
 * The original realpath() follows symbolic links which makes it harder to check
 * their paths.
 *
 * Options
 *   file_existence_check:
 *   - FILE_EXISTENCE_CHECK_REQUIRE_FILE: Script will break if the checked
 *     file does not exist (default).
 *   - FILE_EXISTENCE_CHECK: If a file does not exist, a FALSE value will be
 *     returned.
 *   - FILE_EXISTENCE_CHECK_SKIP: File existence will not be checked at all.
 *
 *   follow_link: Resolve a symbolic link or not (default: FALSE).
 */
function _realpath($path = NULL, $options = array()) {
  // Merge default options with user options.
  $options = array_merge(array(
    'file_existence_check' => FILE_EXISTENCE_CHECK_REQUIRE_FILE,
    'follow_link' => FALSE,
  ), $options);

  // Use current working directory if path has not been defined.
  $path = $path ? $path : getcwd();
  // Replace slashes with OS specific slashes.
  $path = preg_replace('#[\\\/]#', DIR_SEP, $path);

  // Handle `./`. Another great approach using arrays can be found at:
  // @link p://php.net/manual/en/function.realpath.php#84012
  $path = preg_replace('#' . PREG_DIR_SEP . '(\.?' . PREG_DIR_SEP . ')+#', DIR_SEP, $path);
  // Handle `../`.
  while (preg_match('#^(.*?)' . PREG_DIR_SEP . '[^' . PREG_DIR_SEP . ']+' . PREG_DIR_SEP . '\.\.($|' . PREG_DIR_SEP . '.*)#', $path, $m)) {
    $path = $m[1] . $m[2];
  }
  // Remove trailing slash.
  $path = rtrim($path, DIR_SEP);

  // If we are on Windows.
  if (IS_WINDOWS) {
    // If path starts with a lowercase drive letter.
    if (preg_match('#^([a-z]:)(.*)#', $path, $m)) {
      $path = strtoupper($m[1]) . $m[2];
    }
    // If path starts with a slash instead of a drive letter.
    elseif ($path[0] === DIR_SEP) {
      // Add current working directory's drive letter, ie. "D:".
      $path = substr(getcwd(), 0, 2) . $path;
    }
  }
  else {
    // Remove drive letter.
    if (preg_match('#^[A-Z]:(' . PREG_DIR_SEP . '.*)#i', $path, $m)) {
      $path = $m[1];
    }
  }

  // If path is relative.
  if (!preg_match('#^([A-Z]:)?' . PREG_DIR_SEP . '#', $path)) {
    // Add current working directory to path.
    $path = getcwd() . DIR_SEP . $path;
  }

  // If file existence has to be checked and file does not exist.
  if ($options['file_existence_check'] !== DSC_FILE_EXISTENCE_CHECK_SKIP && !file_exists($path)) {
    // Return FALSE value.
    if ($options['file_existence_check'] === DSC_FILE_EXISTENCE_CHECK) {
      return FALSE;
    }
    // Or throw error.
    else {
      dsc_print_error('File does not exist: ' . $path);
    }
  }

  // Follow sybmolic links, but only if the file exists.
  if (!empty($options['follow_link']) && file_exists($path)) {
    $path = readlink($path);
  }

  return $path;
}

Here is my approach mimicking the original realpath() which also:

  1. Adds / removes Windows drive letters as in c:.
  2. Removes trailing slashes.
  3. Prepends current working directory to relative paths.
  4. Optionally checks file existence. In case of an non existing file A) do nothing, B) return a FALSE value or C) throw an error.
  5. Optionally follows symbolic links.

NB: This approach uses regular expressions which are known to be CPU expensive, so I like the method using arrays from http://www.php.net/manual/en/function.realpath.php#84012.

// Set constants for when a file does not exist.
// 0: Check file existence, set FALSE when file not exists.
define('FILE_EXISTENCE_CHECK', 0);
// 1: Require file existence, throw error when file not exists.
define('FILE_EXISTENCE_CHECK_REQUIRE_FILE', 1);
// 2: Do not check file existence.
define('FILE_EXISTENCE_CHECK_SKIP', 2);
// Windows flag.
define('IS_WINDOWS', preg_match('#WIN(DOWS|\d+|_?NT)#i', PHP_OS));
// Directory separator shortcuts.
define('DIR_SEP', DIRECTORY_SEPARATOR);
define('PREG_DIR_SEP', preg_quote(DIR_SEP));

/**
 * The original realpath() follows symbolic links which makes it harder to check
 * their paths.
 *
 * Options
 *   file_existence_check:
 *   - FILE_EXISTENCE_CHECK_REQUIRE_FILE: Script will break if the checked
 *     file does not exist (default).
 *   - FILE_EXISTENCE_CHECK: If a file does not exist, a FALSE value will be
 *     returned.
 *   - FILE_EXISTENCE_CHECK_SKIP: File existence will not be checked at all.
 *
 *   follow_link: Resolve a symbolic link or not (default: FALSE).
 */
function _realpath($path = NULL, $options = array()) {
  // Merge default options with user options.
  $options = array_merge(array(
    'file_existence_check' => FILE_EXISTENCE_CHECK_REQUIRE_FILE,
    'follow_link' => FALSE,
  ), $options);

  // Use current working directory if path has not been defined.
  $path = $path ? $path : getcwd();
  // Replace slashes with OS specific slashes.
  $path = preg_replace('#[\\\/]#', DIR_SEP, $path);

  // Handle `./`. Another great approach using arrays can be found at:
  // @link p://php.net/manual/en/function.realpath.php#84012
  $path = preg_replace('#' . PREG_DIR_SEP . '(\.?' . PREG_DIR_SEP . ')+#', DIR_SEP, $path);
  // Handle `../`.
  while (preg_match('#^(.*?)' . PREG_DIR_SEP . '[^' . PREG_DIR_SEP . ']+' . PREG_DIR_SEP . '\.\.($|' . PREG_DIR_SEP . '.*)#', $path, $m)) {
    $path = $m[1] . $m[2];
  }
  // Remove trailing slash.
  $path = rtrim($path, DIR_SEP);

  // If we are on Windows.
  if (IS_WINDOWS) {
    // If path starts with a lowercase drive letter.
    if (preg_match('#^([a-z]:)(.*)#', $path, $m)) {
      $path = strtoupper($m[1]) . $m[2];
    }
    // If path starts with a slash instead of a drive letter.
    elseif ($path[0] === DIR_SEP) {
      // Add current working directory's drive letter, ie. "D:".
      $path = substr(getcwd(), 0, 2) . $path;
    }
  }
  else {
    // Remove drive letter.
    if (preg_match('#^[A-Z]:(' . PREG_DIR_SEP . '.*)#i', $path, $m)) {
      $path = $m[1];
    }
  }

  // If path is relative.
  if (!preg_match('#^([A-Z]:)?' . PREG_DIR_SEP . '#', $path)) {
    // Add current working directory to path.
    $path = getcwd() . DIR_SEP . $path;
  }

  // If file existence has to be checked and file does not exist.
  if ($options['file_existence_check'] !== DSC_FILE_EXISTENCE_CHECK_SKIP && !file_exists($path)) {
    // Return FALSE value.
    if ($options['file_existence_check'] === DSC_FILE_EXISTENCE_CHECK) {
      return FALSE;
    }
    // Or throw error.
    else {
      dsc_print_error('File does not exist: ' . $path);
    }
  }

  // Follow sybmolic links, but only if the file exists.
  if (!empty($options['follow_link']) && file_exists($path)) {
    $path = readlink($path);
  }

  return $path;
}
别在捏我脸啦 2024-11-24 19:34:45

@Karolis 提供了可接受的答案,但这并不完全符合我所读到的问题。例如,以下脚本:

<?php
include('Karolis-answer') ;
chdir('/home/username') ;
printf("%s\n", get_absolute_path('some/folder/below') ;

执行时,将生成:

some/folder/below

... 这不是绝对路径。在他的辩护中,Karolis 在他的回答中提到了这一点,他说“...在位置 0 处不包含(反)斜杠”

正确的答案是:

/home/username/some/folder/below

对 @Karolis 答案的以下轻微修改是:

function absolutePath(string $path, $startingFolder = null) : string {
        // Credit to Karolis at https://stackoverflow.com/questions/6519010/absolute-path-without-symlink-resolution-keep-user-in-home-directory
        if (is_null($startingFolder))
            $startingFolder = getcwd() ;
        $path = str_replace( [ '/', '\\' ], DIRECTORY_SEPARATOR, $path) ;
        if (substr($path, 0, 1) != DIRECTORY_SEPARATOR)
            $path = $startingFolder . DIRECTORY_SEPARATOR . $path ;
            $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen') ;
        $absolutes = [] ;
        foreach ($parts as $part) {
            if ( $part == '.' )
                continue ;
            if ( $part == '..')
                array_pop($absolutes) ;
            else
                $absolutes[] = $part ;
            }
        return DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $absolutes) ;
        }

这仍然无法解决符号链接,但将解析传递值的“相对”部分。

@Karolis provided the accepted answer, but that does not quite do what I read the question to be. For example, the following script:

<?php
include('Karolis-answer') ;
chdir('/home/username') ;
printf("%s\n", get_absolute_path('some/folder/below') ;

when executed, will produce:

some/folder/below

... which is not an absolute path. In his defense, Karolis alluded to this in his answer, saying "... contain no (back)slash at position 0"

The correct answer would be:

/home/username/some/folder/below

The following slight modification to @Karolis' answer would be:

function absolutePath(string $path, $startingFolder = null) : string {
        // Credit to Karolis at https://stackoverflow.com/questions/6519010/absolute-path-without-symlink-resolution-keep-user-in-home-directory
        if (is_null($startingFolder))
            $startingFolder = getcwd() ;
        $path = str_replace( [ '/', '\\' ], DIRECTORY_SEPARATOR, $path) ;
        if (substr($path, 0, 1) != DIRECTORY_SEPARATOR)
            $path = $startingFolder . DIRECTORY_SEPARATOR . $path ;
            $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen') ;
        $absolutes = [] ;
        foreach ($parts as $part) {
            if ( $part == '.' )
                continue ;
            if ( $part == '..')
                array_pop($absolutes) ;
            else
                $absolutes[] = $part ;
            }
        return DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $absolutes) ;
        }

This still does not resolve symbolic links, but will resolve the "relative" portion of the passed value.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文