用Python编写具有特定权限的文件
我试图创build一个只有用户可读和可写的文件( 0600
)。
是通过使用os.open()
如下所示的唯一方法?
import os fd = os.open('/path/to/file', os.O_WRONLY, 0o600) myFileObject = os.fdopen(fd) myFileObject.write(...) myFileObject.close()
理想情况下,我想能够使用with
关键字,以便我可以自动closures对象。 有没有更好的方式来做我在上面做的事情?
有什么问题? 即使使用os.open()
打开文件, file.close()
也会closures该文件。
with os.fdopen(os.open('/path/to/file', os.O_WRONLY | os.O_CREAT, 0o600), 'w') as handle: handle.write(...)
这个回答解决了vartec的多个问题,特别是umask
关注。 它也考虑到user465139的反馈 。
import os import stat # Define file params fname = '/tmp/myfile' flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL # Refer to "man 2 open". mode = stat.S_IRUSR | stat.S_IWUSR # This is 0o600 in octal and 384 in decimal. # For security, remove file with potentially elevated mode try: os.remove(fname) except OSError: pass # Open file descriptor umask_original = os.umask(0) try: fdesc = os.open(fname, flags, mode) finally: os.umask(umask_original) # Open file handle and write to file with os.fdopen(fdesc, 'w') as fout: fout.write('something\n')
如果所需的模式是0600
,则可以更清楚地指定为八进制数0o600
。 更好的是,使用stat
模块。
即使旧文件首先被删除,竞争条件仍然是可能的。 在标志中包含os.O_EXCL
和os.O_CREAT
将防止文件由于竞争条件而存在。 这是一个必要的辅助安全措施,以防止打开一个可能已经存在的可能提升mode
。 在Python 3中,如果文件存在,则引发FileExistsError
和[Errno 17]。
无法首先将umask
设置为0
可能会导致由os.open
设置不正确的mode
(权限)。 这是因为默认的umask
通常不是0
,它可能会被应用到指定的mode
。 例如,如果我原来的umask
是2
,而我指定的模式是0o222
,如果我不能首先设置umask
为0
,那么生成的文件可能会改为0o220
的mode
,这不是我想要的。 每人man 2 open
,创build的文件的mode & ~umask
是mode & ~umask
。
umask
尽快恢复到原始值。 这个获取和设置不是线程安全的,必要时使用threading.Lock
。
请注意,即使暂时将umask设置为0也是一个值得商榷的做法。 有关相关背景,请参阅此主题 。
更新民间,虽然我感谢你在这里upvotes,我自己不得不反对我最初提出的解决scheme下面。 原因就是这样做,会有一定的时间,无论文件是否存在,并且没有适当的权限 – 这会留下广泛的攻击方式,甚至是越野行为。
当然,首先创build具有正确权限的文件就是要走的路 – 违背正确性,使用Python with
仅仅是一些糖果。
所以请把这个答案作为“不该做什么”的例子。
原来的职位
你可以使用os.chmod
来代替:
>>> import os >>> name = "eek.txt" >>> with open(name, "wt") as myfile: ... os.chmod(name, 0o600) ... myfile.write("eeek") ... >>> os.system("ls -lh " + name) -rw------- 1 gwidion gwidion 4 2011-04-11 13:47 eek.txt 0 >>>
(请注意,在Python中使用八进制的方式是明确的 – 在“ 0o600
”中加上“ 0o
”前缀,而在Python 2.x中它只能编写0600
,但这是误导性的,也不推荐使用)。
但是,如果您的安全性至关重要,则您可能应该使用os.open
创build它,并使用os.fdopen
从os.open
返回的os.open
检索Python File对象。
问题是关于设置权限,以确保文件不会是世界可读的( 只能为当前用户读/写 )。
不幸的是,它自己的代码是:
fd = os.open('/path/to/file', os.O_WRONLY, 0o600)
并不保证权限将被拒绝给世界。 它确实保证文件将有r / w 的当前用户 ,就是这样!
在两个完全不同的testing系统上,这段代码使用-rw-r -r-创build一个带有默认umask的文件,使用umask(0)创build-rw-rw-rw-这个文件绝对不是我们想要的(和姿势严重的安全风险)。
如果你想确保文件没有设置组和世界的位,你必须首先掩饰这些位(记住 – umask是否认权限):
os.umask(0o177)
此外,为了100%确定文件不是以不同的权限存在的,你必须先chmod / delete(删除更安全,因为你可能没有目标目录的写权限 – 如果你有安全问题,您不想在不允许的情况下写入文件!),否则如果黑客在您之前使用世界范围的r / w权限创build文件,则可能会遇到安全问题。 在这种情况下,os.open将会打开文件而不设置它的权限,而你只剩下一个世界上的密钥文件。
所以你需要:
import os if os.path.isfile(file): os.remove(file) original_umask = os.umask(0o177) # 0o777 ^ 0o600 try: handle = os.fdopen(os.open(file, os.O_WRONLY | os.O_CREAT, 0o600), 'w') finally: os.umask(original_umask)
这是确保创build-rw ——-文件的安全方式,无论您的环境和configuration如何。 当然你也可以根据需要来处理IOErrors。 如果在目标目录中没有写入权限,则不能创build该文件,如果已经存在,则删除将失败。
我想build议对ABB的优秀答案进行修改,使问题更加清晰。 主要的好处是,在实际写入文件的过程中,可以处理在打开文件描述符时发生的exception与其他问题分开的情况。
外部try ... finally
块在打开文件描述符的同时处理权限和umask
问题。 内部with
块处理可能的exception,同时处理Python文件对象(因为这是OP的愿望):
try: oldumask = os.umask(0) fdesc = os.open(outfname, os.O_WRONLY | os.O_CREAT, 0o600) with os.fdopen(fdesc, "w") as outf: # ...write to outf, closes on success or on exceptions automatically... except IOError, ... : # ...handle possible os.open() errors here... finally: os.umask(oldumask)
如果你想追加到文件而不是写入,那么文件描述符应该像这样打开:
fdesc = os.open(outfname, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
和这样的文件对象:
with os.fdopen(fdesc, "a") as outf:
当然,所有其他通常的组合都是可能的。