Мне нравится писать систему шаблонов в 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
является False, он должен быть безопасным, я думаю. Конечно, разрешение символических ссылок сделало бы его небезопасным, если пользователь сможет создавать такие ссылки.
ОБНОВЛЕНИЕ - Решение
Приведенный выше код не работает, если промежуточные каталоги являются символическими ссылками.
Чтобы предотвратить это, вы должны использовать realpath
вместо abspath
.
UPDATE: добавление каталога trailing/to для решения проблемы с помощью commonprefix(), который указал Reorx.
Это также делает ненужным 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