2011-10-09 72 views
3

我們知道,使用字符串連接形成SQL查詢會呈現容易受SQL注入攻擊的程序。我通常使用我提供的任何數據庫軟件的API提供的參數功能來解決這個問題。如何安全地解析字符串?

但我還沒有聽說過這是常規系統編程中的一個問題。考慮下面的代碼作爲程序的一部分,該程序允許用戶只寫入他私人目錄中的文件。

Scanner scanner = new Scanner(System.in); 
String directoryName = "Bob"; 
String filePath = null; 
String text = "some text"; 

System.out.print("Enter a file to write to: "); 
filePath = scanner.nextLine(); 

// Write to the file in Bob's personal directory for this program (i.e. Bob/textfile.txt) 
FileOutputStream file = new FileOutputStream(directoryName + "/" + filePath); 
file.write(text.getBytes()); 

第二最後一行是漏洞嗎?如果是這樣,程序如何更安全(特別是在Java,C++和C#中)?一種方法是驗證轉義字符的輸入。還要別的嗎?

+0

@HovercraftFullOfEels:我認爲這是我一直在尋找的術語。Java官方教程似乎表明,準備好的語句對於SQL來說是特殊的。它們是否可以在通用環境中應用? – InvalidBrainException

回答

3

這裏最簡單的解決方案是有一個可接受字符的白名單。修改原來的代碼(包括Java約定,既然你說你是新的......)

package javawhitelist; 
import java.io.File; 
import java.io.FileNotFoundException; 
import java.io.FileWriter; 
import java.io.IOException; 
import java.util.Scanner; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

public class JavaWhiteListExample { 

    public static void main(String[] args) throws IOException { 

     Scanner scanner = new Scanner(System.in); 
     String directoryName = "Bob"; 
     String filePath = null; 
     FileWriter stream = null; 
     String text = "some text"; 
     System.out.print("Enter a file to write to: "); 
     filePath = scanner.nextLine(); 
     String WHITELIST = "[^0-9A-Za-z]+"; 
     Pattern p = Pattern.compile(WHITELIST); 
     Matcher m = p.matcher(filePath); 

     //You need to do m.find() because m.matches() looks for COMPLETE match 
     if(m.find()){ 
      //reject input. 
      System.out.println("Invalid input."); 
     }else{ 
      // Write to the file in Bob's home directory (i.e. Bob/textfile.txt) 
      try{ 
       File toWrite = new File(directoryName + File.separator + filePath); 

       if(toWrite.canWrite()){ 
        stream = new FileWriter(toWrite); 
        stream.write(text); 
       } 
      }catch(FileNotFoundException e){ 
       e.printStackTrace(); 
      }catch(IOException e){ 
       e.printStackTrace(); 
      }finally{ 
       if(stream != null){ 
        stream.close(); 
       } 
      } 

     } 
    } 
} 

任何JVM的默認實現與用戶的所有訪問權限運行。使用File.canWrite()方法將有助於確保用戶不會覆蓋他/她沒有權限的文件。最安全的解決方案(指定的確切位置的文件會)是使用 com.sun.security.auth.module.UnixSystem.getName() 並用它來建立目錄名的 /home/$USER 一部分。有些解決方案可能會告訴您使用 System.getProperty("user.home"): 或其他一些方法,但這些解決方案依賴易於更改的環境變量。

我試圖徹底,我希望這有助於。

+0

這確實很徹底,謝謝你的解釋。 – InvalidBrainException

+0

我從來沒有使用模式和匹配器類,所以這是我要消化的東西。 作爲Java新手,看到幾乎每種情況都有一個類,我希望有一個過濾用戶輸入的標準解決方案。 – InvalidBrainException

+0

在我使用過的任何語言中,用戶輸入驗證一直使用正則表達式實現。解決方案1總是白名單,解決方案2是黑名單。 https://www.owasp.org/index.php/Category:OWASP_Java_Project 欲瞭解更多Java特定材料。 – avgvstvs

1

您可以通過System.getProperty("user.home")獲取用戶目錄。如果您的程序在該用戶下運行,並且用戶權限得到正確管理,則不會出現問題。您也可以使用另一個屬性獲取路徑分隔符 - file.separator。最後,有方法File.canRead()File.canWrite()

+0

謝謝,我不知道。但實際上我並沒有談論OS創建的用戶目錄。我正在製作一個小程序,如果您以Bob的身份登錄,與Bob有關的所有數據都將存儲在C:\ SomeFolder \ Bob \中,因此我無法享受操作系統提供的權限管理功能。 – InvalidBrainException

+1

然後,使用文件名的正則表達式。 http://regexlib.com/Search.aspx?k=file+name – madhead

+0

該網站看起來像一個非常有用的。在所有這些年裏,我不得不處理正則表達式,我希望我遇到過這種情況。謝謝。 – InvalidBrainException

3

來自用戶的任何輸入應該算是「可疑

你的情況,我們假定你是該文件的路徑爲某個位置,用戶應該寫。

用戶可以傳遞任何文件路徑並修改(如果程序有權限)一個你沒有想到的文件。

所以,是該行:

FileOutputStream file = new FileOutputStream(directoryName + "/" + filePath); 

確實是一個漏洞

這個概念適用於C++以及

+0

那麼這個概念適用於絕對的任何編程語言;) – Voo

+0

Java通常以用戶權限運行,所以它應該只能寫入用戶被允許。然而,有一個非常流行的桌面操作系統,直到最近默認允許用戶做任何事情。 – 2013-09-18 00:50:33

2

既然有文件名中的幾個reserved characters你可能要搜索的路徑由用戶給出。您可能還想檢查該字符串是否包含../:/等,這會讓用戶篡改「主目錄」路徑。我建議使用正則表達式來驗證給定的字符串,然後再使用它。如果驗證失敗,則簡單地中止操作並讓用戶知道輸入有問題,而不是嘗試修復它。

如果其他人不知道自己在做什麼,並且角色不是唯一的問題,那麼文件結構可能相當複雜,正如其他答案中所述。在各種文件系統中,哪些文件名有效是不同的。舊的FAT系統有最多8個字符的限制,而Windows使用的較新的NTFS最多允許255個字符。

更新回答提供更多清晰度。

+0

所以我想一個有用的驗證是檢查斜槓和反斜槓並拒絕這種輸入。逃避像退格這樣的字符也是一種安全風險?在Java和C#中是否有某種輸入驗證類以優雅的方式處理它? – InvalidBrainException

+1

而且我們已經在你的代碼中出現了一個安全漏洞。因爲用'../'開始是不夠的。 'foo /../../../ privateStuff'是一條非常好的路徑。 「C:/ Windows」也是如此。取決於你如何解決它(只需要用'../'替換掉什麼?),你會遇到其他問題,例如。 '... /。/ doh'。然後有像NTFS上的ADS(不知道如果Java允許這樣做?)等等。所以最好的想法是不要自己修復它。 – Voo

+0

其實我並沒有打算解決這個問題,而是讓用戶知道驗證是否失敗。當然,檢查字符串的開始是不夠的;我的錯。 – Marcus

2

此問題與SQL注入問題完全不同。在SQL注入問題中,可以使用惡意輸入的參數在特權安全上下文中執行命令,因爲執行命令的用戶通常具有carte blanche訪問權限以寫入數據庫中的行。

在您提供的示例中,關鍵問題是「作爲Java代碼將執行的用戶?」。如果您將此代碼作爲例如CGI腳本執行,則Web服務器進程的用戶可以寫入的任何文件或目錄都易受攻擊。如果你只是簡單地從命令行運行它,那真正取決於操作系統(而不是Java代碼)來保護用戶不應該寫入的文件/目錄。

如果你的意圖是隻允許代碼寫入用戶的目錄,那麼提供的其他答案是正確的。但是,我可以想象很多情況下,情況可能並非如此。例如也許你正在編寫一些代碼來自動編輯/ etc目錄中的文件。

簡而言之,您想要考慮執行代碼的上下文以及該上下文提供的安全性以及在該上下文中您需要在自己的代碼中提供的安全性。

PS - 您通常不希望假設「/」是您的目錄分隔符。 Java爲此提供了常量File.separator

+0

'File.separator'是指定文件分隔符的技術上正確的方法。但是,I/O層將自動更改/和\以便爲底層平臺正確。儘管這更像是一個樣式問題,但我更喜歡'File.separator'。 – 2013-09-18 00:52:48

2

如果您看到類似的代碼,請運行。

的一些問題:

目錄遍歷攻擊:傳統上,文件系統混淆UI和API。我們已經用文件路徑獲得了這種語言,但沒有辦法清楚地說明特定的名稱。在典型的操作系統..將允許向上移動目錄結構(不一定在路徑的開始處)。還請注意,多個字符可能充當目錄分隔符。

鏈接:目錄中的文件系統鏈接可能鏈接到別處。

NUL字符:如果試圖指定後綴(例如作爲文件擴展名),零字節將截斷路徑。

Shell轉義:您可能會發現嘗試解釋文件路徑的shell代碼問題,無論是在創建之前還是稍後再來。

現有文件:如果文件存在,會發生什麼?

光盤使用情況:如果數據是用戶提供的,您是否檢查它不是很大?

因此,儘量避免使用局外人創建的文件名。如果你真的需要,我建議應用一個緊密的字符白名單。