Sim*_*mon 38 python filesystems security validation
我喜欢用Python编写一个模板系统,它允许包含文件.
例如
This is a template You can safely include files with safe_include`othertemplate.rst`
如您所知,包含文件可能很危险.例如,如果我在允许用户创建自己的模板的Web应用程序中使用模板系统,他们可能会做类似的事情
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
Run Code Online (Sandbox Code Playgroud)
allow_symlink
我认为,只要是假的,就应该是安全的.如果用户能够创建这样的链接,那么允许符号链接当然会使其不安全.
更新 - 解决方案
如果中间目录是符号链接,则上述代码不起作用.为了防止这种情况,你必须使用realpath
而不是abspath
.
更新:添加一个尾随/目录以解决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
Run Code Online (Sandbox Code Playgroud)
jme*_*jme 36
Python 3的pathlib
模块使用其Path.parents属性使其变得简单.例如:
from pathlib import Path
root = Path('/path/to/root')
child = root / 'some' / 'child' / 'dir'
other = Path('/some/other/path')
Run Code Online (Sandbox Code Playgroud)
然后:
>>> root in child.parents
True
>>> other in child.parents
False
Run Code Online (Sandbox Code Playgroud)
Tom*_*ull 15
如果您要使用字符串比较或os.path.commonprefix
方法测试目录父项,则这些路径或相对路径会出现错误.例如:
/path/to/files/myfile
将被显示为/path/to/file
使用许多方法的子路径./path/to/files/../../myfiles
不会被/path/myfiles/myfile
许多方法显示为父母.事实上,它是.在以前的答案罗布丹尼斯提供了一个很好的方法来比较路径血统,而不会遇到这些问题.Python 3.4添加了pathlib
可以以更复杂的方式执行这些路径操作的模块,可选地不引用底层操作系统.jme在另一个先前的答案中描述了如何使用pathlib
以便准确地确定一条路径是否是另一条路径的孩子.如果您不想使用pathlib
(不确定原因,那就太棒了),那么Python 3.5引入了一种新的基于操作系统的方法os.path
,允许您以类似的准确和无错误的方式执行路径父子检查码.
Python 3.5引入了该功能os.path.commonpath
.这是一种特定于运行代码的操作系统的方法.您可以使用commonpath
以下方式准确确定路径父母:
def path_is_parent(parent_path, child_path):
# Smooth out relative path names, note: if you are concerned about symbolic links, you should use os.path.realpath too
parent_path = os.path.abspath(parent_path)
child_path = os.path.abspath(child_path)
# Compare the common path of the parent and child path with the common path of just the parent path. Using the commonpath method on just the parent path will regularise the path name in the same way as the comparison that deals with both paths, removing any trailing path separator
return os.path.commonpath([parent_path]) == os.path.commonpath([parent_path, child_path])
Run Code Online (Sandbox Code Playgroud)
您可以将整个批次合并到Python 3.5中的单行if语句中.这很难看,它包含了不必要的重复调用os.path.abspath
,它肯定不适合PEP 8 79字符行长指南,但是如果你喜欢这样的话,那么:
if os.path.commonpath([os.path.abspath(parent_path_to_test)]) == os.path.commonpath([os.path.abspath(parent_path_to_test), os.path.abspath(child_path_to_test)]):
# Yes, the child path is under the parent path
Run Code Online (Sandbox Code Playgroud)
jgo*_*ers 12
def is_subdir(path, directory):
path = os.path.realpath(path)
directory = os.path.realpath(directory)
relative = os.path.relpath(path, directory)
return not relative.startswith(os.pardir + os.sep)
Run Code Online (Sandbox Code Playgroud)
Tom*_*ull 12
pathlib
PurePath
有一个被调用的新方法is_relative_to
直接执行此功能。您可以阅读有关如何工作的python 文档is_relative_to
,或使用以下示例:
from pathlib import Path
child_path = Path("/path/to/file")
if child_path.is_relative_to("/path"):
print("/path/to/file is a child of /path") # This prints
if child_path.is_relative_to("/anotherpath"):
print("/path/to/file is a child of /anotherpath") # This does not print
Run Code Online (Sandbox Code Playgroud)
os.path.realpath(path):返回指定文件名的规范路径,消除路径中遇到的任何符号链接(如果操作系统支持它们).
在目录和子目录名称上使用它,然后检查后者从前者开始.
所以,我需要这个,并且由于对commonprefx的批评,我采取了不同的方式:
def os_path_split_asunder(path, debug=False):
"""
http://stackoverflow.com/a/4580931/171094
"""
parts = []
while True:
newpath, tail = os.path.split(path)
if debug: print repr(path), (newpath, tail)
if newpath == path:
assert not tail
if path: parts.append(path)
break
parts.append(tail)
path = newpath
parts.reverse()
return parts
def is_subdirectory(potential_subdirectory, expected_parent_directory):
"""
Is the first argument a sub-directory of the second argument?
:param potential_subdirectory:
:param expected_parent_directory:
:return: True if the potential_subdirectory is a child of the expected parent directory
>>> is_subdirectory('/var/test2', '/var/test')
False
>>> is_subdirectory('/var/test', '/var/test2')
False
>>> is_subdirectory('var/test2', 'var/test')
False
>>> is_subdirectory('var/test', 'var/test2')
False
>>> is_subdirectory('/var/test/sub', '/var/test')
True
>>> is_subdirectory('/var/test', '/var/test/sub')
False
>>> is_subdirectory('var/test/sub', 'var/test')
True
>>> is_subdirectory('var/test', 'var/test')
True
>>> is_subdirectory('var/test', 'var/test/fake_sub/..')
True
>>> is_subdirectory('var/test/sub/sub2/sub3/../..', 'var/test')
True
>>> is_subdirectory('var/test/sub', 'var/test/fake_sub/..')
True
>>> is_subdirectory('var/test', 'var/test/sub')
False
"""
def _get_normalized_parts(path):
return os_path_split_asunder(os.path.realpath(os.path.abspath(os.path.normpath(path))))
# make absolute and handle symbolic links, split into components
sub_parts = _get_normalized_parts(potential_subdirectory)
parent_parts = _get_normalized_parts(expected_parent_directory)
if len(parent_parts) > len(sub_parts):
# a parent directory never has more path segments than its child
return False
# we expect the zip to end with the short path, which we know to be the parent
return all(part1==part2 for part1, part2 in zip(sub_parts, parent_parts))
Run Code Online (Sandbox Code Playgroud)
def is_in_directory(filepath, directory):
return os.path.realpath(filepath).startswith(
os.path.realpath(directory) + os.sep)
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
26021 次 |
最近记录: |