簡介
java程式是跨平臺的,可以執行在windows也可以執行在linux。但是平臺不同,平臺中的檔案許可權也是不同的。windows大家經常使用,並且是視覺化的許可權管理,這裡就不多講了。
本文主要講講linux下面的檔案的許可權和安全性問題,並且探討一下如何在java程式中考慮檔案的安全性。
linux下的檔案基本許可權
chmod是linux下面的許可權管理命令,我們可以通過chmod來對檔案的許可權進行修改。
普通檔案的許可權有三種,rwx分別是讀,寫和執行。再加上三個使用者分組:owner,group,other 我們可以很方便的使用三個0-7的數字來表示一個檔案的許可權。
舉個例子,我們建立一個檔案:
touch test.log
看一下預設的檔案許可權:
ll test.log
-rw-r--r-- 1 flydean wheel 0B 8 16 10:36 test.log
預設的檔案許可權是644,也就是說owner許可權是讀寫,group許可權是讀,其他許可權是讀。
我們可以使用chmod命令對其進行修改,比如:
chmod 777 test.log
ll test.log
-rwxrwxrwx 1 flydean wheel 0B 8 16 10:36 test.log
可以看出許可權被修改成為777。
linux檔案的特殊許可權
講完普通許可權,我們接下來講一下linux檔案中的特殊許可權。
Set UID 和 Set GID
考慮一個常用的修改密碼的例子,修改密碼呼叫的是/usr/bin/passwd,看下這個檔案的許可權:
ll /usr/bin/passwd
-rwsr-xr-x. 1 root root 27832 Jun 10 2014 /usr/bin/passwd
可以看到有個很奇怪的s許可權。這個s是什麼意思呢?s實際是x的變種,是一種特殊的可執行許可權。
特殊在哪裡呢?passwd是修改使用者的密碼,密碼檔案實際上是存放在 /etc/shadow中的。
我們看下/etc/shadow的許可權:
ll /etc/shadow
---------- 1 root root 707 Jan 2 2020 /etc/shadow
/etc/shadow的owner是root,只有root才許可權強行寫入這個檔案。
那麼問題來了,普通使用者呼叫passwd是怎麼修改的/etc/shadow呢?
這就是s的妙用,s表示Set UID,簡稱為SUID,這個UID表示User的ID,而User表示這個程式(/usr/bin/passwd)的擁有者(root),那麼我們在呼叫passwd的過程時候,就會暫時擁有passwd owner的許可權,也就是root許可權。
注意,SUID只能用在二進位制檔案中,它是對x許可權的一個替換,並且SUID對目錄是無效的。
同樣的,我們也可以給group設定UID許可權,也就是Set GID。
不同的是SGID可以使用在檔案和目錄兩個地方。
用在檔案中是和SUID一樣的,用在目錄中的意思是在該目錄中所建的檔案或目錄的使用者組都和該目錄的使用者組是一樣的。
Sticky Bit
Sticky Bit表示的是特殊的other許可權,用t來表示。
/tmp目錄就是一個Sticky Bit的例子: drwxrwxrwt 。
SBit對目錄的作用是:“在具有SBit的目錄下,使用者若在該目錄下具有w及x許可權,則當使用者在該目錄下建立檔案或目錄時,只有檔案擁有者與root才有權力刪除”
這個特性就是為了保護我們在共享目錄下的檔案不被別人刪除。
SUID/SGID/SBIT許可權設定
怎麼設定這些許可權呢?
普通許可權我們是用3個數字來表示的,我們可以在3個數字之前再加上一個數字表示SUID/SGID/SBIT許可權。
和普通許可權一樣,我們用4來表示SUID,2表示SGID,1表示SBIT。
舉個例子:
touch test
chmod 6755 test
ll test
-rwsr-sr-x 1 crawler crawler 0 Aug 16 11:43 test
注意,有時候我們會遇到大寫的S與T的情況,這種情況出現在user、group以及others都沒有x這個可執行的標誌,所以大寫的S和T表示為空。
檔案隱藏屬性
有些linux系統提供了chattr命令來設定檔案隱藏屬性。
我們看下chattr的使用:
Usage: chattr [-RVf] [-+=aAcCdDeijsStTu] [-v version] files...
引數:
- : 增加某個特殊引數,其他原本存在的引數不動。
- : 刪除某個特殊引數,其他原本存在的引數不動。
= : 設定一定,且僅有後面接的引數
A : 當設定了A屬性時,這個檔案(或目錄)的存取時間atime(access)將不可被修改,可避免例如手提電腦有磁碟I/O錯誤的情況發生。
S : 這個功能有點類似sync.就是將資料同步寫入磁碟中。可以有效地避免資料流失。
a : 設定a之後,這個檔案將只能增加資料,而不能刪除,只有root才能設定這個屬性。
c : 這個屬性設定之後,將會自動將此檔案“壓縮”,在讀取的時候將會自動解壓縮,但在儲存的時候,將會先進行壓縮後再儲存(對於大檔案有用)。
d : 當執行dump(備份)程式的時候,設定d屬性將可使該檔案(或目錄)具有轉儲功效。
i : i的作用很大。它可以讓一個檔案“不能被刪除、改名、設定連線,也無法寫入或新增資料”。對於系統安全性有相當大的幫助。
j : 當使用ext3檔案系統格式時,設定j屬性將會使檔案在寫入時先記錄在journal中。但是,當檔案系統設定引數為data=journalled時,由於已經設定日誌了,所以這個屬性無效。
s : 當檔案設定了s引數時,它將會從這個硬碟空間完全刪除。
u : 與s相反,當使用u來設定檔案時,則資料內容其實還存在磁碟中,可以用來還原刪除。
特殊檔案
linux中還有一些特殊的檔案,比如連結檔案和裝置檔案。
在處理連結檔案的時候,我們需要注意判斷連結檔案的真實指向。
而裝置檔案我們需要注意不合理的授權訪問。
java中在共享目錄中使用檔案要注意的問題
共享目錄中因為所有人都有操作檔案的許可權,所以,我們需要特別注意在java中共享目錄中檔案的操作。
根據java的規範, java.nio.channels.FileLock可以用來表示檔案的鎖定。
通常來講,鎖定有兩種,一種是排他鎖,一種是共享鎖。
共享鎖可防止其他同時執行的程式獲取重疊的排他鎖,但確實允許它們獲取重疊的共享鎖。排他鎖可防止其他程式獲取任一型別的重疊鎖。
共享鎖支援來自多個程式的併發讀取訪問;獨佔鎖支援獨佔寫訪問。
但是,加鎖是否真正的阻塞其他程式對該檔案的訪問,實際是取決於作業系統。
在使用中,我們需要對使用者使用者傳入的檔案進行一些必要的校驗,比如是否是常規檔案:
String filename = /* Provided by user */;
Path path = new File(filename).toPath();
try {
BasicFileAttributes attr = Files.readAttributes(
path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
// Check
if (!attr.isRegularFile()) {
System.out.println("Not a regular file");
return;
}
// Other necessary checks
// Use
try (InputStream in = Files.newInputStream(path)) {
// Read file
};
} catch (IOException x) {
// Handle error
}
上面的例子中,我們通過獲取File的屬性,來判斷這個屬性是否regularFile,注意,我們在讀取檔案屬性的時候,傳入了一個LinkOption.NOFOLLOW_LINKS,表示的是不要follow連結。
雖然我們首先判斷了file的許可權,然後再對其進行操作,但是上面的例子還是會有問題的。因為存在時間差的問題,如果惡意使用者在判斷之後將檔案替換成了惡意的連結檔案,就會出現問題。
安全目錄
為了保證使用者的檔案操作安全性,我們引入一個安全目錄的概念,所謂安全目錄就是目錄除了使用者本身和超級管理員之外,沒有其他使用者的寫訪問許可權,並且給定檔案的父目錄不會被除了系統管理員之外的其他任何使用者刪除或重新命名。
在下方的原始碼連結中,我提供了一個檢視安全目錄的class,大家可以自行檢視。
本文的程式碼:
learn-java-base-9-to-20/tree/master/security
本文已收錄於 http://www.flydean.com/java-security-code-line-file-security/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!