2011-10-09 83 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代码问题,无论是在创建之前还是稍后再来。

现有文件:如果文件存在,会发生什么?

光盘使用情况:如果数据是用户提供的,您是否检查它不是很大?

因此,尽量避免使用局外人创建的文件名。如果你真的需要,我建议应用一个紧密的字符白名单。