zoukankan      html  css  js  c++  java
  • 【Java编码准则】の #01限制内存中敏感数据的生命周期

         当竞争对手的应用程序与我们的应用程序执行在同一个系统上时,我们的应用程序在内存中的敏感数据是非常easy被竞争对手获取的。假设我们的应用程序符合以下几种情况之中的一个,那么竞争对手能够获取到我们应用的敏感数据:

    1)应用程序使用对象来存储敏感数据,并且在对象使用完后。对象的内容没有被清除或者对象没有被垃圾回收;

    2)在操作系统执行内存管理任务或者执行休眠等功能时。应用程序的内存分页将被置换到磁盘上保存;

    3)持有存储了操作系统缓存或者内存中敏感数据的buffer对象(比如BufferedReader)。

    4)基于反射的控制流。使得竞争对手能够使用规避措施逃过敏感变量生命周期的限制;

    5)在调试信息/日志文件/环境变量或者线程和core dumps等方面泄漏了敏感数据。

         包括敏感数据的内存在数据使用后没有及时清空,非常easy导致敏感数据的泄漏。

    为了限制这样的风险。应用程序必须尽量减小敏感数据的生命周期。

         完美的防止内存数据的泄漏要求底层操作系统和Java虚拟机的支持。比如假设将内存数据置换到磁盘上是存在安全问题的,那么一个安全的操作系统就须要禁用数据的置换和系统的休眠。


    [不符合安全要求的代码演示样例]

         以下代码从控制台读取用户的username和password并将password保存在一个String对象中,这导致在垃圾回收器回收String所占用的内存之前,password处于泄漏状态。

    import java.io.Console;
    import java.io.IOException;
    
    public class Password {
    	
    	public static void main(String[] args) throws IOException {
    		Console console = System.console();
    		if (console == null) {
    			System.out.println("No Console.");
    			System.exit(1);
    		}
    		
    		String username = console.readLine("Enter your user name:");
    		String password = console.readLine("Enter your password:");
    		if (!verify(username, password)) {
    			throw new SecurityException("Invalid Credentials");
    		}
    		
    	}
    	
    	// dummy verify method, always returns true
    	private static final boolean verify(String username, String password) {
    		return true;
    	}
    
    }

    [符合安全要求的解决方式]

         这个解决方式使用Console.readPassword()函数从控制台中获取password

    import java.io.Console;
    import java.io.IOException;
    import java.util.Arrays;
    
    public class Password {
    	
    	public static void main(String[] args) throws IOException {
    		Console console = System.console();
    		if (console == null) {
    			System.out.println("No Console.");
    			System.exit(1);
    		}
    		
    		String username = console.readLine("Enter your user name:");
    		char[] password = console.readPassword("Enter your password:");
    		if (!verify(username, password)) {
    			throw new SecurityException("Invalid Credentials");
    		}
    		
    		// Clear the password
    		Arrays.fill(password, ' ');
    		
    	}
    	
    	// dummy verify method, always returns true
    	private static final boolean verify(String username, char[] password) {
    		return true;
    	}
    
    }

         函数Console.readPassword()使得返回的数据是字节数组类型,而不是String对象。因此。开发人员能够在数组数据使用完了之后清空数据,这个函数同一时候禁止了password在控制台上面的显示。


    [不符合安全要求的代码演示样例]

         以下的代码使用BufferedReader对象包装InputStreamReader对象,这导致敏感数据能够直接在文件里读取到。

    	void readData() throws IOException {
    		BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("file")));
    		
    		// read from file
    		String data = br.readLine();
    	}

         BufferdReader.readLine()函数将敏感数据以String对象的形式返回,这个对象在敏感数据用完之后还会存活非常长的时间。

    而BufferedReader.read(char[], int, int)函数能够将敏感数据读取到一个char数组中。只是这须要开发人员在使用完敏感数据后手动清除。类似的,假设使用BufferedReader来包装FileReader对象。也会存在相同的安全问题。


    [符合安全要求的解决方式]

         以下的代码使用直接分配的NIO buffer来从文件里读取敏感数据。在数据使用完后。能够马上清除它,并且敏感数据不会缓存在多个位置,仅存在于系统内存中。

    	void readData() {
    		ByteBuffer bb = ByteBuffer.allocateDirect(16*1024);
    		try (FileChannel rdr = (new FileInputStream("file")).getChannel()) {
    			while(rdr.read(bb) > 0) {
    				// do something with the buffer
    				bb.clear();
    			}
    		} catch(Throwable e) {
    			// Handle error
    		}
    	}
         须要注意的是,必须手动清除buffer里面的数据,由于这部分数据垃圾回收器是不会进行回收的。


    ——欢迎转载,请注明出处 http://blog.csdn.net/asce1885 。未经本人允许请勿用于商业用途。谢谢——


  • 相关阅读:
    Java-运算符
    Java-类型转化
    Java-数组
    Java-循环结构(for,while)
    Java-选择结构(if-else)
    Java-数据类型(引用类型)
    HDFS JournalNode 故障
    Grok patterns 汇总
    HBase 查询导致RegionServer OOM故障复盘
    【翻译】Spark 调优 (Tuning Spark) 中文版
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/6977845.html
Copyright © 2011-2022 走看看