使用fdopen對python進程產生的文件進行權限最小化配置

需求背景

用python進行文件的創建和讀寫操作時,我們很少關注所創建的文件的權限配置。對於一些安全性較高的系統,如果我們創建的文件權限其他用戶或者同一用戶組裡的其他用戶有可讀權限的話,有可能導致不必要的信息泄漏的風險。因此,除了創建一個更加安全和隱私的個人環境之外(如容器環境等),我們還可以對生成的文件的配置進行權限最小化處理。

常用方法及其缺陷分析

常用的python文件創建和讀寫方法,是直接通過內置的open函數創建一個文件。這裡如果是使用with語法來創建的,結束語句後會自動關閉被打開的對象。而如果是直接使用open函數來定義一個對象,則需要在任務結束時手動的執行close操作。以下演示內置函數open的用法及其文件操作屬性,首先創建一個名為file-test.py的文件:

# file-test.py

with open('test1.txt', 'w') as file:
    file.write('hello world!')

該任務的內容為:在當前目錄下創建一個名為test1.txt的文件,清空該文件的內容後,在文件中寫入hello world!這個字符串。接下來用python3執行該文件:

[dechin@dechin-manjaro os_security]$ python3 file-test.py 
[dechin@dechin-manjaro os_security]$ ll
總用量 8
-rw-r--r-- 1 dechin dechin 83  1月 25 13:43 file-test.py
-rw-r--r-- 1 dechin dechin 12  1月 25 13:43 test1.txt

這裡我們發現,在執行之後成功產生了test1.txt這個文件,其權限配置為644,與前面創建的file-test.py保持一致。在不清楚內置函數open的實現原理時,原本以為這個產生的文件權限配置是與當前的py文件保持一致的。然而經過進一步的測試,將py文件的權限配置為440之後再重新執行該文件:

[dechin@dechin-manjaro os_security]$ chmod 440 file-test.py 
[dechin@dechin-manjaro os_security]$ ll
總用量 8
-r--r----- 1 dechin dechin 83  1月 25 13:43 file-test.py
-rw-r--r-- 1 dechin dechin 12  1月 25 13:43 test1.txt
[dechin@dechin-manjaro os_security]$ rm test1.txt 
[dechin@dechin-manjaro os_security]$ python3 file-test.py 
[dechin@dechin-manjaro os_security]$ ll
總用量 8
-r--r----- 1 dechin dechin 83  1月 25 13:43 file-test.py
-rw-r--r-- 1 dechin dechin 12  1月 25 13:44 test1.txt

這裡從測試結果我們可以看出,python的內置函數open產生的文件類型是與源py文件無關的。關於這裡py文件的執行是否需要可執行權限,可以參考這篇博客

改進後的python文件創建方法

通過fdopen這個庫以及特殊的權限指定,我們可以設置生成文件的訪問權限,以下直接展示一個python代碼案例:

# fdopen-test.py

import os
import stat

file_name = 'test2.txt'
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
mode = stat.S_IRUSR | stat.S_IWUSR

with os.fdopen(os.open(file_name, flags, mode), 'w') as file:
    file.write('hello world!')

執行之後我們可以發現,當前目錄下生成了一個名為test2.txt的文件,其權限配置為600, 對照於我們在代碼中設置的mode = stat.S_IRUSR | stat.S_IWUSR。這裡我們先對其中的一些參數作一個解釋:os.O_WRONLY表示以只寫的方式打開,os.O_CREAT表示創建並打開一個新文件,os.O_EXCL表示如果文件已存在則報錯,“

我們也可以嘗試將此處的mode作一個調整,比如添加一個可執行權限變為700

# fdopen-test.py

import os
import stat

file_name = 'test3.txt'
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR

with os.fdopen(os.open(file_name, flags, mode), 'w') as file:
    file.write('hello world!')

又或者,我們需要為用戶組裡的其他用戶添加可訪問權限,比如640權限:

# fdopen-test.py

import os
import stat

file_name = 'test4.txt'
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP

with os.fdopen(os.open(file_name, flags, mode), 'w') as file:
    file.write('hello world!')

甚至我們也可以寫出系統原生的644文件權限:

# fdopen-test.py

import os
import stat

file_name = 'test5.txt'
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH

with os.fdopen(os.open(file_name, flags, mode), 'w') as file:
    file.write('hello world!')

最後,讓我們一起看下上面這些python示例執行後得到的結果:

[dechin@dechin-manjaro os_security]$ ll
總用量 28
-rw-r--r-- 1 dechin dechin 269  1月 25 14:58 fdopen-test.py
-r--r----- 1 dechin dechin  84  1月 25 14:11 file-test.py
-rw-r--r-- 1 dechin dechin  12  1月 25 13:44 test1.txt
-rw------- 1 dechin dechin  12  1月 25 14:44 test2.txt
-rwx------ 1 dechin dechin  12  1月 25 14:48 test3.txt
-rw-r----- 1 dechin dechin  12  1月 25 14:56 test4.txt
-rw-r--r-- 1 dechin dechin  12  1月 25 14:58 test5.txt

從結果中我們可以看出,所有產生的文件test*.txt都按照我們預期的文件權限配置生成,到這裡我們就完成了所有預期的目標。

總結概要

使用python進行文件的創建和讀寫時,常規的內置函數open得到的結果會是一個644權限的文件,這不一定能夠滿足很多對安全性需求較高的執行環境的要求。因此我們可以通過fdopen來對所創建的文件進行進一步的權限約束,具體的操作方法可以在mode中定義一系列的權限配置,比如帶有USR的表示當前用來執行python文件的用戶,帶有GRP的表示用來執行python文件的整個用戶組,而OTH則表示其他的所有的用戶。這當中尤其是OTH這個選項往往是不必要開放的權限,我們也可以根據具體的場景需求對創建的文件權限進行配置。這裡還有一點補充介紹的是,os.O_EXCL這個指令的開啟表示如果存在同名文件就無法創建,需要先使用os.remove操作刪除原文件後再進行新的文件操作,避免文件權限被覆蓋或者重用,從而導致創建的新文件權限配置與我們所預期的不符合。

參考鏈接

  1. fdopen使用方法的問答
  2. os.open()各配置參數解釋

版權聲明

本文首發鏈接為://www.cnblogs.com/dechinphy/p/fdopen.html
作者ID:DechinPhy
更多原著文章請參考://www.cnblogs.com/dechinphy/