Мне нравится писать систему шаблонов на Python, которая позволяет включать файлы.
, например
This is a template
You can safely include files with safe_include`othertemplate.rst`
Как вы знаете, включение файлов может быть опасным. Например, если я использую систему шаблонов в веб-приложении, которое позволяет пользователям создавать свои собственные шаблоны, они могут сделать что-то вроде
I want your passwords: safe_include`/etc/password`
Поэтому я должен ограничить включение файлов в файлы, которые находятся, например, в определенном подкаталоге (например, /home/user/templates
)
Вопрос теперь: как я могу проверить, находится ли /home/user/templates/includes/inc1.rst
в подкаталоге /home/user/templates
?
Будет ли следующий код работать и быть безопасным?
import os.path
def in_directory(file, directory, allow_symlink = False):
#make both absolute
directory = os.path.abspath(directory)
file = os.path.abspath(file)
#check whether file is a symbolic link, if yes, return false if they are not allowed
if not allow_symlink and os.path.islink(file):
return False
#return true, if the common prefix of both is equal to directory
#e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b
return os.path.commonprefix([file, directory]) == directory
Пока allow_symlink
Неверно, я думаю, это должно быть безопасно. Разумеется, использование символических ссылок может сделать небезопасным, если пользователь сможет создавать такие ссылки.
ОБНОВЛЕНИЕ - Решение
Приведенный выше код не работает, если промежуточные каталоги являются символическими ссылками.
Чтобы предотвратить это, вы должны использовать realpath
вместо abspath
.
ОБНОВЛЕНИЕ: добавление конечного / в каталог для решения проблемы с commonprefix (), на который указал Реоркс.
Это также делает allow_symlink
ненужным, так как символические ссылки расширяются до их реального назначения
import os.path
def in_directory(file, directory):
#make both absolute
directory = os.path.join(os.path.realpath(directory), '')
file = os.path.realpath(file)
#return true, if the common prefix of both is equal to directory
#e.g. /a/b/c/d.rst and directory is /a/b, the common prefix is /a/b
return os.path.commonprefix([file, directory]) == directory