У меня есть rootPath
, которому я доверяю, и relativePath
, которого я не знаю. Я хочу объединить их таким образом, чтобы я мог быть уверен, что результат находится под rootPath
и что пользователь не может использовать ..
, чтобы вернуться к исходной точке. Я делать хочу относительный путь, чтобы такие вещи, как: hello\..\world
== world
Безопасная версия Path.Combine
ответ
Чтобы развернуть: используйте Path.Combine, затем вызовите GetFullPath на результат и убедитесь, что этот результат начинается с rootPath.
Он не защитит вас от жестких ссылок, но он должен поймать простые вещи, такие как двойные точки.
выше, как код:
string Resolve(string fileName)
{
string root = FileRoot();
string ret = Path.GetFullPath(Path.Combine(root, fileName));
if (ret.StartsWith(root.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar)) return ret;
throw new ArgumentException("path resolved to out of accesable directroy");
}
Одна вещь, которую вы можете сделать, это посчитать количество слэш (\
) и двойных точек (..
), и убедитесь, что число двойных точек меньше числа обратных косых черт. Чтобы переместиться выше rootPath
в структуру папок, вам понадобится по крайней мере столько обратных косых черт, что и двойные точки - таким образом, если вы разрешите только relativePath
с по меньшей мере еще одной обратной косой чертой, вы должны быть в безопасности.
Вы можете просто позвонить Path.GetFullPath()
и проверить, если он начинается с вашим доверенным rootPath
. Если вы склонны к паранойи, проверьте также, что у rootPath
.
public Boolean IsPathSafe(String rootPath, String relativePath)
{
return rootPath.EndsWith(Path.DirectorySeparatorChar.ToString()) &&
Path.IsPathRooted(rootPath) &&
Patch.Combine(rootPath, relativePath).GetFullPath().StartsWith(rootPath);
}
Для объяснения первого теста см. Комментарий Алексея Мартелли относительно ответа технофила.
мне нравится: D – BCS
Однако, если корень не заканчивается с \, это все еще можно играть в ограниченный трюк - в конечном итоге, в другом каталоге, в другом корневом, который начинается с этого в качестве префикса. Например, root может быть \ zip \ zop, относительный путь .. \ zopper \ zup, вы попадаете в \ zip \ zopper \ zup, за пределами дерева, внедренного в \ zip \ zop, но все еще удовлетворяющего тесту StartsWith. Небольшой риск, но не ноль. –
Хороший угловой футляр ... собирается добавить это к коду. –