应用严格的文件权限¶
应使用严格的文件权限创建文件,以防止信息泄露和代码执行等漏洞。特别是,任何可能包含机密信息的文件的访问权限应设置为仅允许所有者/服务和组访问(即,不允许世界/其他用户访问)。
在授予诸如配置文件之类的文件的写入访问权限时应谨慎,以防止包括拒绝服务和远程代码执行在内的漏洞。
错误¶
考虑一个名为“secureserv”的服务,其配置文件“secureserv.conf”存储包括密码的配置。
ls -l secureserv.conf
-rw-rw-rw- 1 secureserv secureserv 6710 Feb 17 22:00 secureserv.conf
在此,文件权限设置为 666(所有者、组和其他用户具有读写访问权限)。这将允许系统上的所有用户访问配置文件中包含的敏感信息。
在使用 Python 在 *NIX 操作系统上写入文件时,文件输出将使用应用程序设置的 umask 进行写入。根据您的操作系统配置,在将敏感数据写入文件时,这可能会过于宽松。
给定这个程序
with open('testfile.txt', 'w') as fout:
fout.write("secrets!")
文件权限将默认设置为环境设置。在此,umask 允许系统上的其他用户读取文件内容。
ls -l testfile.txt
-rw-r--r-- 1 user staff 4 Feb 19 10:59 testfile.txt
正确¶
最好为包含敏感信息的文件设置严格的权限。要修复上面的示例,您需要设置权限,使该文件仅由创建它的用户可读和可写。
chmod 0600 securesev.conf
ls -l secureserv.conf
-rw------- 1 secureserv secureserv 6710 Feb 17 22:00 secureserv.conf
以下是如何在 Python 中安全地创建仅由文件所有者可读和可写的文件的示例。
import os
flags = os.O_WRONLY | os.O_CREAT | os.EXLC
with os.fdopen(os.open('testfile.txt', flags, 0o600), 'w') as fout:
fout.write("secrets!")
请注意,验证文件的所有者和组也很重要。尤其重要的是要注意哪些其他用户是您授予访问权限的组的成员。最佳实践是,如果不需要组访问权限,则不要授予它。这就是最小权限原则。
测试指南¶
应该能够测试任何执行文件创建的操作,以确保权限设置正确。以下示例检查文件是否以预期的权限创建,如果文件已存在则引发异常。
import stat
import os
import unittest
def do_something(path, mode):
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
return os.fdopen(os.open(path, flags, 0o600), mode)
class TestFileCreation(unittest.TestCase):
def test_correct_permissions(self):
"""
Make sure that a file can be created with specific permissions
"""
test_file = "demo.txt"
with do_something(test_file, "w") as fout:
fout.write("blah blah blah")
finfo = os.stat(test_file)
assert(finfo.st_mode & stat.S_IRUSR)
assert(finfo.st_mode & stat.S_IWUSR)
assert(not finfo.st_mode & stat.S_IXUSR)
assert(not finfo.st_mode & stat.S_IRGRP)
assert(not finfo.st_mode & stat.S_IWGRP)
assert(not finfo.st_mode & stat.S_IXGRP)
assert(not finfo.st_mode & stat.S_IROTH)
assert(not finfo.st_mode & stat.S_IWOTH)
assert(not finfo.st_mode & stat.S_IXOTH)
os.remove(test_file)
def test_file_exists(self):
"""
If the file already exists an OSError should be raised.
"""
test_file = "demo2.txt"
# simulate file already placed at location by attacker
with open(test_file, "w") as fout:
fout.write("nasty attacker stuff")
# ensure that we can't open the file
with self.assertRaises(OSError):
with do_something(test_file, "w") as fout:
fout.write("this should never happen..")
os.remove(test_file)
您还可以使用 mock 来确保对 os.open 的任何调用都使用严格的权限进行。
import os
import mock
import unittest
def do_something(dummy_file):
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
with os.fdopen(os.open(dummy_file, flags, 0o600), "w") as fout:
fout.write("blah blah")
print("doing something")
class TestUsingMock(unittest.TestCase):
def test_do_something(self):
"""
Make sure that any file created is done with sufficiently secure permissions.
"""
with mock.patch('os.open') as os_open, mock.patch('os.fdopen') as os_fdopen:
# setup test call
dummy_file = "dummy.txt"
os_open.return_value = 123
do_something(dummy_file)
# ensure the file is being created with specific flags
# and permission set
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
os_open.assert_called_with(dummy_file, flags, 0o600)
os_fdopen.assert_called_with(123, "w")
后果¶
从配置文件中读取密码 - 恶意用户可以从文件中读取敏感信息(例如密码)。
设置新密码 - 恶意用户可以将新密码写入文件,从而可能授予访问权限。
代码执行 - 如果配置文件存储命令或参数,恶意用户可以篡改配置文件以实现代码执行。
拒绝服务 - 攻击者可以删除文件内容以阻止服务正常运行。