部分Fortify代码扫描高风险解决方案

部分Fortify代码扫描高风险解决方案

一、Category: Access Control: Database

问题描述:

​ Database access control 错误在以下情况下发生:
​ 1.数据从一个不可信赖的数据源进入程序。
​ 2.这个数据用来指定 SQL 查询中主键的值。

官方案例:
示例 1:
	以下代码使用可转义元字符并防止出现 SQL 注入漏洞的参数化语句,以构建和执行用于搜索与指定标识符
相匹配的清单的 SQL 查询。您可以从与当前被授权用户有关的所有清单中选择这些标识符。
	id = Integer.decode(request.getParameter("invoiceID")); String query = "SELECT * FROM invoices WHERE id = ?";
	PreparedStatement stmt = conn.prepareStatement(query); stmt.setInt(1, id); 			ResultSet results = stmt.execute();
问题在于开发者没有考虑到所有可能出现的 id 值。虽然接口生成了一个当前用户的标识符清单,但是攻击者可以绕过
这个接口,从而获取所需的任何清单。因为此例中的代码没有执行检查,确保用户有权访问需要的清单,所以代码会
显示所有清单,即使这些清单并不属于当前用户。
有些人认为在移动世界中,典型的 Web 应用程序漏洞(如 Database access control 错误)是无意义的 -- 为什么用户要攻
击自己?但是,谨记移动平台的本质是从各种来源下载并在相同设备上运行的应用程序。恶意软件在银行应用程序附
近运行的可能性很高,它们会强制扩展移动应用程序的攻击面(包括跨进程通信)。

示例 2:
	以下代码对示例 1 进行调整,使其适用于 Android 平台。
	String id = this.getIntent().getExtras().getString("invoiceID"); String query = "SELECT * FROM invoices WHERE id = ?";
	SQLiteDatabase db = this.openOrCreateDatabase("DB", MODE_PRIVATE, null); Cursor c = db.rawQuery(query, newObject[]{id});
	许多现代 Web 框架都提供对用户输入执行验证的机制。其中包括 Struts 和 Spring MVC。为了突出显示未经验证的输入源,该规则包会降低 HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)报告的问题被利用的可能性,并在使用框架验证机制时提供相应的依据,以动态重新调整问题优先级。我们将这种功能称之为上下文敏感排序。为了进一步帮助 HPE Security Fortify 用户执行审计过程,HPE Security Fortify 软件安全研究团队提供了数据验证项目模板,该模板会根据应用于输入源的验证机制,将问题分组到多个文件夹中。
	
例 3:
	以下代码实施了与例 1 相同的功能,但是附加了一个限制,即为当前被授权的用户指定某一特定的获取清单的方
式。
	userName = ctx.getAuthenticatedUserName();
	id = Integer.decode(request.getParameter("invoiceID"));
	String query ="SELECT * FROM invoices WHERE id = ? AND user = ?";
	PreparedStatement stmt = conn.prepareStatement(query);
	stmt.setInt(1, id);
	stmt.setString(2, userName);
	ResultSet results = stmt.execute();
	
Recommendations:
	与其靠表示层来限制用户输入的值,还不如在应用程序和数据库层上进行 access control。任何情况下都不允许用户在没有取得相应权限的情况下获取或修改数据库中的记录。每个涉及数据库的查询都必须遵守这个原则,这可以通过把当前被授权的用户名作为查询语句的一部分来实现。
实际解决方案:

​ 这个问题出现的地方大多数是数据返回值上,其实在返回值return的时候判空返回相应的基本类型就OK了

​ 或者是出现在不明所以的地方,简单粗暴直接删除问题代码行。

	例如:
		return grayanologdao.getGrayAnoLogList(map);
		改为:
			if(map != null){
                return map;
			}

二、Category: Null Dereference

问题描述:

​ Category: Null Dereference:
​ 当违反程序员的一个或多个假设时,通常会出现 null 指针异常。如果程序明确将对象设置为 null,但稍后却间接引用该对象,则将出现 dereference-after-store 错误。此错误通常是因为程序员在声明变量时将变量初始化为 null。
​ 大部分空指针问题只会引起一般的软件可靠性问题,但如果攻击者能够故意触发空指针间接引用,攻击者就有可能利用引发的异常绕过安全逻辑,或致使应用程序泄漏调试信息,这些信息对于规划随后的攻击十分有用。

官方案例:
示例:
	在下列代码中,程序员将变量 foo 明确设置为 null。稍后,程序员间接引用 foo,而未检查对象是否为 null 值。
	Foo foo = null;
		...
	foo.setBar(val);
		...
}

Recommendations:
	在间接引用可能为 null 值的对象之前,请务必仔细检查。如有可能,在处理资源的代码周围的包装器中纳入 null 检查,确保在所有情况下均会执行 null 检查,并最大限度地减少出错的地方。
实际解决方案:

​ 错误多发生在流关闭等地方,程序员没有判空就关闭流,就引起了这个错误,解决起来也蛮方便,就是在流关闭的时候增加判空条件就ok了。

例如:
		} finally {
		try {
			pstmt.close();
		} catch (SQLException e) {
			e.printStackTrace();
			}
		}
	}
	改为:
		} finally {
		if(pstmt != null){
           	try {
				pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
				}
			} 
		}
	}

三、Category: Password Management: Password in Configuration File

问题描述:

​ 在配置文件中存储明文密码会使所有能够访问该文件的人都能访问那些用密码保护的资源。程序员有时候认为,他们不可能阻止应用程序被那些能够访问配置文件的攻击者入侵,但是这种想法会导致攻击者发动攻击变得更加容易。健全的 password management 方针从来不会允许以明文形式存储密码。

官方案例:
Recommendations:
	绝不能采用明文的形式存储密码。相反,应在系统启动时,由管理员输入密码。如果这种方法不切实际,一个安全性较差、但通常都比较恰当的解决办法是将密码模糊化,并把这些去模糊化的资源分散到系统各处,因此,要破译密码,攻击者就必须取得并正确合并多个系统资源。
有些第三方产品宣称可以采用更加安全的方式管理密码。例如,WebSphere Application Server 4.x 用简单的异或加密算法加密数值,但是请不要对诸如此类的加密方式给予完全的信任。WebSphere 以及其他一些应用服务器通常都只提供过期的且相对较弱的加密机制,这对于安全性敏感的环境来说是远远不够的。较为安全的解决方法是由用户自己创建一个新机制,而这也是如今唯一可行的方法。
Tips:
	1. HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)会从配置文件中搜索那些用于密码属性的常用名称。当发现密码条目中包含明文时,就会将其标记为问题。
	2. 如果配置文件中包含一个默认密码条目,除了需要在配置文件中将其模糊化以外,还需要对其进行修改。
实际解决方案:

​ 尝试过对password进行加密操作,但是还是会被扫描出来,扫描好像不能出现 password=XXXX 不能出现等号情况,因此最简单粗暴的方法就是把这行错误删掉。

四、Category: Unreleased Resource: Database

问题描述:

​ 程序可能无法释放某个数据库连接。
​ 资源泄露至少有两种常见的原因:
​ - 错误状况及其他异常情况。
​ - 未明确程序的哪一部份负责释放资源。
​ 大部分 Unreleased Resource 问题只会导致一般的软件可靠性问题,但如果攻击者能够故意触发资源泄漏,该攻击者就有可能通过耗尽资源池的方式发起 denial of service 攻击。

官方案例:
示例:
	在正常条件下,以下代码会执行数据库查询指令,处理数据库返回的结果,并关闭已分配的指令对象。但如果
在执行 SQL 或是处理结果时发生异常,指令对象将不会关闭。如果这种情况频繁出现,数据库将用完所有可用的指针,且不能再执行任何 SQL 查询。
	Statement stmt = conn.createStatement();
	ResultSet rs = stmt.executeQuery(CXN_SQL);
	harvestResults(rs);
	stmt.close();
	Recommendations:
	
	1. 请不要依赖 finalize() 回收资源。为了使对象的 finalize() 方法能被调用,垃圾收集器必须确认对象符合垃圾回收的条件。但是垃圾收集器只有在 JVM 内存过小时才会使用。因此,无法保证何时能够调用该对象的 finalize() 方法。垃圾收集器最终运行时,可能出现这样的情况,即在短时间内回收大量的资源,这种情况会导致“突发”性能,并降低总体系统通过量。随着系统负载的增加,这种影响会越来越明显。最后,如果某一资源回收操作被挂起(例如该操作需要通过网络访问数据库),那么执行 finalize() 方法的线程也将被挂起。

	2. 在 finally 代码段中释放资源。示例中的代码可按以下方式改写:
           public void execCxnSql(Connection conn) {
            Statement stmt;
           try {
            stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(CXN_SQL);
            ...
            }
            finally {
            if (stmt != null) {
            safeClose(stmt);
            		}
            	}
            }
           public static void safeClose(Statement stmt) {
            if (stmt != null) {
           try {
            stmt.close();
            } catch (SQLException e) {
            log(e);
            }
           }
          }
	以上方案使用了一个助手函数,用以记录在尝试关闭指令时可能产生的异常。该助手函数大约会在需要关闭指令时重新使用。同样,execCxnSql 方法不会将 stmt 对象初始化为 null。而是进行检查,以确保调用 safeClose() 之前,stmt 不是 null。如果没有检查 null,Java 编译器会报告 stmt 可能没有进行初始化。编译器做出这一判断源于 Java 可以检测未初始化的变量。如果用一种更加复杂的方法将 stmt 初始化为 null,那么编译器就无法检测 stmt 未经初始化便使用的情况。

Tips:
	请注意,关闭数据库连接可能会自动释放与连接对象关联的其他资源,也可能不会自动释放。如果应用程序使用连接池,则最好在关闭连接后,明确关闭其他资源。如果应用程序未使用连接池,则数据库连接关闭后,其他资源也将自动关闭。在这种情况下,此漏洞无效。
实际解决方案:

​ 就是对将数据库连接对象先进行判空再关闭就ok,但是注意可能还需要再进行try/catch一下

例如:
	rs = st.executeQuery(sql);
		在finally中添加finally{
		try{
		if(rs != null){
	        rs.close;
			}
		}catch(Exception e){
	        e.printStackTrace();
		}
		}

五、Category: Unreleased Resource: Streams

问题描述:

​ 程序可能无法成功释放某一项系统资源。
​ 资源泄露至少有两种常见的原因:
​ - 错误状况及其他异常情况。
​ - 未明确程序的哪一部份负责释放资源。
​ 大部分 Unreleased Resource 问题只会导致一般的软件可靠性问题,但如果攻击者能够故意触发资源泄漏,该攻击者就有可能通过耗尽资源池的方式发起 denial of service 攻击。

官方案例:
示例:
	下面的方法绝不会关闭它所打开的文件句柄。FileInputStream 中的 finalize() 方法最终会调用 close(),但是不能确定何时会调用 finalize() 方法。在繁忙的环境中,这会导致 JVM 用尽它所有的文件句柄。
	private void processFile(String fName) throws FileNotFoundException, IOException {
	FileInputStream fis = new FileInputStream(fName);
	int sz;
	byte[] byteArray = new byte[BLOCK_SIZE];
		while ((sz = fis.read(byteArray)) != -1) {
	processBytes(byteArray, sz);
		}
	}

Recommendations:
1. 请不要依赖 finalize() 回收资源。为了使对象的 finalize() 方法能被调用,垃圾收集器必须确认对象符合垃圾回收的条件。但是垃圾收集器只有在 JVM 内存过小时才会使用。因此,无法保证何时能够调用该对象的 finalize() 方法。垃圾收集器最终运行时,可能出现这样的情况,即在短时间内回收大量的资源,这种情况会导致“突发”性能,并降低总体系统通过量。随着系统负载的增加,这种影响会越来越明显。最后,如果某一资源回收操作被挂起(例如该操作需要通过网络访问数据库),那么执行 finalize() 方法的线程也将被挂起。

2. 在 finally 代码段中释放资源。示例中的代码可按以下方式改写:
		public void processFile(String fName) throws FileNotFoundException, IOException {
		FileInputStream fis;
	try {
		fis = new FileInputStream(fName);
		int sz;
		byte[] byteArray = new byte[BLOCK_SIZE];
			while ((sz = fis.read(byteArray)) != -1) {
		processBytes(byteArray, sz);
		}
	}
		finally {
		if (fis != null) {
		safeClose(fis);
				}
			}
		}
	public static void safeClose(FileInputStream fis) {
		if (fis != null) {
	try {
		fis.close();
		} catch (IOException e) {
			log(e);
			}
		}
	}
	以上方案使用了一个助手函数,用以记录在尝试关闭流时可能发生的异常。该助手函数大约会在需要关闭流时重新使用。同样,processFile 方法不会将 fis 对象初始化为 null。而是进行检查,以确保调用 safeClose() 之前,fis 不是 null。如果没有检查 null,Java 编译器会报告 fis 可能没有进行初始化。编译器做出这一判断源于 Java 可以检测未初始化的变量。如果用一种更加复杂的方法将 fis 初始化为 null,那么编译器就无法检测 fis 未经初始化便使用的情况。
实际解决方案:

​ 1.和上面问题的解决方案相似,单纯的判空关闭就ok。
​ 2.值得注意的是,有时候关闭对象也会报错,这时候就要考虑 先将流对象放在全局变量中,再进行关闭操作。

----------------------------------------wb对象-------------------------------------- 
@RequestMapping("uploadImportQueryFile")
    @ResponseBody
    public Map<String, Object> uploadImportQueryFile(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) {
        Map<String, Object> result = new HashMap<String, Object>();
        Workbook wb = null;
        try {
            int index = file.getOriginalFilename().lastIndexOf(".");
            if (index > -1 && "xlsx".equalsIgnoreCase(file.getOriginalFilename().substring(index + 1))) {
                wb = new XSSFWorkbook(file.getInputStream());
                Sheet sheet = wb.getSheetAt(0);
                int rows = sheet.getLastRowNum();

                List<String> numberList = new ArrayList<String>();
                for (int i = 1; i <= rows; i++) {
                    Cell cell = sheet.getRow(i).getCell(0);
                    cell.setCellType(Cell.CELL_TYPE_STRING);
                    String number = cell.getStringCellValue();
                    if (StringUtils.isNotBlank(number)) {
                        number = number.trim();
                        if (StringUtils.isNotBlank(number)) {
                            numberList.add(number);
                        }
                    }
                }
                file.getInputStream().close();
                request.getSession().setAttribute(IMPORT_QUERY_SESSION_KEY, numberList);
                request.getSession().removeAttribute(IMPORT_QUERY_RESULT_SESSION_KEY);
                result.put("success", true);
                result.put("message", "导入成功");
                result.put("data", numberList);
            } else {
                result.put("success", false);
                result.put("message", "文件格式有误");
            }
        } catch (IOException e) {
            e.printStackTrace();
            result.put("success", false);
            result.put("message", "程序异常:" + e.getMessage());
        } finally {
			if (wb!=null) {
				try {
					wb.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
        return result;
    }

六、Category: SQL Injection

问题描述:

SQL injection 错误在以下情况下发生:
​ 1.数据从一个不可信赖的数据源进入程序。
​ 2.数据用于动态地构造一个 SQL 查询。

官方案例:
例 1:
	以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有
者与被授予权限的当前用户一致的条目。
	...
	String userName = ctx.getAuthenticatedUserName();
	String itemName = request.getParameter("itemName");
	String query = "SELECT * FROM items WHERE owner = '"
	+ userName + "' AND itemname = '"
	+ itemName + "'";
	ResultSet rs = stmt.execute(query);
	...
这一代码所执行的查询遵循如下方式:
	SELECT * FROM items
	WHERE owner = 
	AND itemname = ;
但是,由于这个查询是动态构造的,由一个不变的基查询字符串和一个用户输入字符串连接而成,因此只有在
itemName 不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name' OR 'a'='a”,那么查询就会变成:
	SELECT * FROM items
	WHERE owner = 'wiley'
	AND itemname = 'name' OR 'a'='a';
附加条件 OR 'a'='a' 会使 where 从句永远评估为 true,因此该查询在逻辑上将等同于一个更为简化的查询:SELECT * FROM items;
这种查询的简化会使攻击者绕过查询只返回经过验证的用户所拥有的条目的要求;而现在的查询则会直接返回所有储
存在 items 表中的条目,不论它们的所有者是谁。

例 2:
	这个例子指出了将不同的恶意数值传递给在例 1 中构造和执行的查询时所带来的各种影响。如果一个用户名为
wiley 在 itemName 中输入字符串“name'; DELETE FROM items; --”,则该查询将会变为以下两个:
	SELECT * FROM items
	WHERE owner = 'wiley'
	AND itemname = 'name';
	DELETE FROM items;
	--'
众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。注意成对的连字符 (--);这在大多数数据库服务器上都表示下面的语句将作为注释使用,而不能加以执行 [4]。在这种情况下,注释字符的作用就是删除修改的查询指令中遗留的最后一个单引号。而在那些不允许这样加注注释的数据库中,通常攻击者可以如例 1 那样来攻击。如果攻击者输入字符串“name'); 
	DELETE FROM items; SELECT * FROM items
	WHERE 'a'='a”就会创建如下三个有效指令:
	SELECT * FROM items
	WHERE owner = 'wiley'
	AND itemname = 'name';
	DELETE FROM items;
	SELECT * FROM items WHERE 'a'='a';
有些人认为在移动世界中,典型的 Web 应用程序漏洞(如 SQL injection)是无意义的 -- 为什么用户要攻击自己?但是,谨记移动平台的本质是从各种来源下载并在相同设备上运行的应用程序。恶意软件在银行应用程序附近运行的可能性很高,它们会强制扩展移动应用程序的攻击面(包括跨进程通信)。

示例 3:以下代码对示例 1 进行调整,使其适用于 Android 平台。
	...
	PasswordAuthentication pa = authenticator.getPasswordAuthentication();
	String userName = pa.getUserName();
	String itemName = this.getIntent().getExtras().getString("itemName");
	String query = "SELECT * FROM items WHERE owner = '"
	+ userName + "' AND itemname = '"
	+ itemName + "'";
	SQLiteDatabase db = this.openOrCreateDatabase("DB", MODE_PRIVATE, null);
	Cursor c = db.rawQuery(query, null);
	...
避免 SQL injection 攻击的传统方法之一是,把它作为一个输入合法性检查的问题来处理,只接受列在白名单中的字符,或者识别并避免那些列在黑名单中的恶意数据。白名单方法是一种非常有效方法,它可以强制执行严格的输入检查规则,但是参数化的 SQL 指令所需维护更少,而且能提供更好的安全保障。而对于通常采用的列黑名单方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL injection 威胁。例如,攻击者可以:
	— 把没有被黑名单引用的值作为目标
	— 寻找方法以绕过对某一转义序列元字符的需要
	— 使用存储过程来隐藏注入的元字符
手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。
防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但
是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。

Recommendations:
	造成 SQL injection 攻击的根本原因在于攻击者可以改变 SQL 查询的上下文,使程序员原本要作为数据解析的数值,被篡改为命令了。当构造一个 SQL 查询时,程序员应当清楚,哪些输入的数据将会成为命令的一部分,而哪些仅仅是作为数据。参数化 SQL 指令可以防止直接窜改上下文,避免几乎所有的 SQL injection 攻击。参数化 SQL 指令是用常规的SQL 字符串构造的,但是当需要加入用户输入的数据时,它们就需要使用捆绑参数,这些捆绑参数是一些占位符,用来存放随后插入的数据。换言之,捆绑参数可以使程序员清楚地分辨数据库中的数据,即其中有哪些输入可以看作命令的一部分,哪些输入可以看作数据。这样,当程序准备执行某个指令时,它可以详细地告知数据库,每一个捆绑参数所使用的运行时的值,而不会被解析成对该命令的修改。
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setString(1, itemName);
stmt.setString(2, userName);
ResultSet results = stmt.execute();
...
下面是 Android 的等同内容:
...
PasswordAuthentication pa = authenticator.getPasswordAuthentication();
String userName = pa.getUserName();
String itemName = this.getIntent().getExtras().getString("itemName");
String query = "SELECT * FROM items WHERE itemname=? AND owner=?";
SQLiteDatabase db = this.openOrCreateDatabase("DB", MODE_PRIVATE, null);
Cursor c = db.rawQuery(query, new Object[]{itemName, userName});
...
	更加复杂的情况常常出现在报表生成代码中,因为这时需要通过用户输入来改变 SQL 指令的命令结构,比如在
WHERE 条件子句中加入动态的约束条件。不要因为这一需求,就无条件地接受连续的用户输入,从而创建查询语句字
符串。当必须要根据用户输入来改变命令结构时,可以使用间接的方法来防止 SQL injection 攻击:创建一个合法的字符串集合,使其对应于可能要加入到 SQL 指令中的不同元素。在构造一个指令时,可使用来自用户的输入,以便从应用程序控制的值集合中进行选择。

Tips:
	1. 使用参数化 SQL 指令的一个常见错误是使用由用户控制的字符串来构造 SQL 指令。这显然背离了使用参数化 SQL 指令的初衷。如果不能确定用来构造参数化指令的字符串是否由应用程序控制,请不要因为它们不会直接作为 SQL 指令执行,就假定它们是安全的。务必彻底地检查 SQL 指令中使用的所有由用户控制的字符串,确保它们不会修改查询的含意。

	2. 许多现代 Web 框架都提供对用户输入执行验证的机制。其中包括 Struts 和 Spring MVC。为了突出显示未经验证的输入源,HPE Security Fortify 安全编码规则包会降低 HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)报告的问题被利用的可能性,并在使用框架验证机制时提供相应的依据,以动态重新调整问题优先级。我们将这种功能称之为上下文敏感排序。为了进一步帮助 HPE Security Fortify 用户执行审计过程,HPE Security Fortify 软件安全研究团队提供了数据验证项目模板,该模板会根据应用于输入源的验证机制,将问题分组到多个文件夹中。
实际解决方案:

​ 说白了就是攻击者可以用SQL语句可以操作数据库,这个问题我查了资料也没解决,不知道问题是出在错误这行还是在SQL那里添加什么限制,总之没有解决,所有就直接将这句扫描出错误的代码给删除掉了,再扫描也没有出现这个问题。

七、Category: Privacy Violation: Autocomplete

问题描述:

​ 启用自动完成功能后,某些浏览器会保留会话中的用户输入,以便随后使用该计算机的用户查看之前提交的信息。

官方案例:
Recommendations:
	对于表单或敏感输入,显式禁用自动完成功能。通过禁用自动完成功能,之前输入的信息不会在用户输入时以明文形式显示。这也会禁用大多数主要浏览器的“记住密码”功能。
例 1:
	在 HTML 表单中,通过在 form 标签上将 autocomplete 属性的值显式设置为 off,禁用所有输入字段的自动完成功能。
	
Address: Password:
例 2: 通过在相应的标签上将 autocomplete 属性的值显式设置为 off,禁用特定输入字段的自动完成功能。
Address: Password:
请注意,autocomplete 属性的默认值为 on。因此,处理敏感输入时请不要忽略该属性。
实际解决方案:

​ 官方给出的2种解决方案我读试过,就是在表单上标签上添加 autocomplete=“off” 属性,但是第二次扫描依然会扫描出来这个错误,所以也是简单粗暴的直接将这个表单删除了。

八、Category: Mass Assignment: Insecure Binder Configuration

问题描述:

​ 为便于开发和提高生产率,现代框架允许自动实例化一个对象,并使用名称与要绑定的类的属性相匹配的 HTTP 请求参数填充该对象。对象的自动实例化和填充加快了开发速度,但如果不谨慎实施,会导致严重的问题。绑定类或嵌套类中的任何属性都将自动绑定到 HTTP 请求参数。因此,恶意用户能够将值分配给绑定类或嵌套类中的任意属性,即使这些属性未通过 Web 表单或 API 合约暴露给客户端也是如此。

官方案例:
例 1:
	只需使用 Spring MVC 而无需进行额外配置,以下控制器方法即可将 HTTP 请求参数绑定到 User 或 Details 类中的任何属性:
@RequestMapping(method = RequestMethod.POST)
	public String registerUser(@ModelAttribute("user") User user, BindingResult result, SessionStatus status) {
	if (db.save(user).hasErrors()) {
		return "CustomerForm";
	} else {
		status.setComplete();
		return "CustomerSuccess";
	}
	}
	其中,User 类定义为:
	public class User {
		private String name;
		private String lastname;
		private int age;
		private Details details;
		// Public Getters and Setters
		...
	}
	Details 类定义为:
		public class Details {
		private boolean is_admin;
		private int id;
		private Date login_date;
		// Public Getters and Setters
		...
	}
	
Recommendations:
	当使用提供自动模型绑定功能的框架时,最佳做法是控制要绑定到模型对象的属性,这样,即使攻击者能够确定该模型或嵌套类的其他未暴露属性,他们也无法绑定 HTTP 请求参数的任意值。
根据所使用的框架,可以采用不同的方法控制模型绑定过程:
Spring MVC:可以控制绑定过程中要使用和忽略的 HTTP 请求参数。

例 2:
	在使用注释的 Spring 应用程序上,可以对方法进行 @InitBinder 注释,以便框架注入对 Spring 模型绑定器的引用。
Spring 模型绑定器可以配置 setAllowedFields 和 setDisallowedFields 以控制要绑定的属性:
	final String[] DISALLOWED_FIELDS = new String[]{"details.role", "details.age", "is_admin"};
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		binder.setDisallowedFields(DISALLOWED_FIELDS);
	}
	
例 3:
	扩展 BaseCommandController 的 Spring 应用程序可以替代 initBinder(HttpServletRequest request,ServletRequestDataBinder binder) 方法,以获取对 Spring 模型绑定器的引用:
	@Override
	protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
	binder.setDisallowedFields(new String[]{"details.role", "details.age", "is_admin"});
	}
Apache Struts:Struts 1 和 2 仅将 HTTP 请求参数绑定到具有公共 setter 访问器的 Actions 或 ActionForms 属性。如果某个属性不应绑定到请求,则应将其 setter 设置为私有。

例 4:
	配置私有 setter 以便 Struts 框架不会自动绑定任何 HTTP 请求参数:
	private String role;
	private void setRole(String role) {
		this.role = role;
	}
REST 框架:
	大多数 REST 框架不会自动将内容类型为 JSON 或 XML 的 HTTP 请求正文绑定到模型对象。根据用于 JSON和 XML 处理的库,可以采用不同的方法控制绑定过程。
	以下是 JAXB (XML) 和 Jackson (JSON) 的一些示例:

例 5:
	使用 Oracle 的 JAXB 库从 XML 文档绑定的模型可以通过不同的注释控制绑定过程,例如 @XmlAccessorType、@XmlAttribute、@XmlElement 和 @XmlTransient。可以告诉绑定器默认不绑定任何属性,具体方法为:使用值为XmlAccessType.NONE 的 @XmlAccessorType 注释对模型进行注释,然后使用 @XmlAttribute 和 @XmlElement 注释选择应绑定的字段:@XmlRootElement@XmlAccessorType(XmlAccessType.NONE)
	public class User {
		private String role;
		private String name;
	@XmlAttribute
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
	
例 6:
	使用 FastXML 的 Jackson 库从 JSON 文档绑定的模型可以通过不同的注释控制绑定过程,例如 @JsonIgnore、@JsonIgnoreProperties、@JsonIgnoreType 和 @JsonInclude。可以通过对某些属性进行 @JsonIgnore 注释,告诉绑定器忽略这些属性。
	public class User {
	@JsonIgnore
		private String role;
		private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
防止出现批量分配漏洞的另一方法是使用将 HTTP 请求参数绑定到 DTO 对象的分层体系结构。DTO 对象仅用于该目的,仅暴露 Web 表单或 API 合约中定义的属性,然后将这些 DTO 对象映射到域对象,在这里,可以定义剩余的私有属性。

Tips:
	此漏洞类别可以归类为设计缺陷,因为准确查找这些问题需要了解应用程序体系结构,这超出了静态分析的功能范
围。因此,如果将应用程序设计为使用特定 DTO 对象进行 HTTP 请求绑定,则可能根本不需要将绑定器配置为排除任何属性。
实际解决方案:

​ 实际没有解决,因为这个错误出现的并不多,高风险也允许有万分之三的存在,所以,哪位大佬解决了这个问题还请麻烦评论一下,大家交流交流。

九、Category: Password Management: Hardcoded Password

问题描述:

​ 使用硬编码方式处理密码绝非好方法。这不仅是因为所有项目开发人员都可以使用通过硬编码方式处理的密码,而且还会使解决这一问题变得极其困难。一旦代码投入使用,除非对软件进行修补,否则您再也不能改变密码了。如果帐户中的密码保护减弱,系统所有者将被迫在安全性和可行性之间做出选择。

官方案例:

例 1:
以下代码用 hardcoded password 来连接数据库:
	...
	DriverManager.getConnection(url, "scott", "tiger");
	...
该代码可以正常运行,但是任何有该代码权限的人都能得到这个密码。一旦程序发布,将无法更改数据库用户“scott”
和密码“tiger”,除非是要修补该程序。雇员可以利用手中掌握的信息访问权限入侵系统。更糟的是,如果攻击者能够
访问应用程序的字节代码,那么他们就可以利用 javap -c 命令访问已经过反汇编的代码,而在这些代码中恰恰包含着用户使用过的密码值。我们可以从以下看到上述例子的执行结果:
javap -c ConnMngr.class
22: ldc #36; //String jdbc:mysql://ixne.com/rxsql 24: ldc #38; //String scott 26: ldc #17; //String tiger
在移动世界中,由于设备丢失的几率较高,因此密码管理是一个非常棘手的问题。

例 2:
以下代码可使用硬编码的用户名和密码设置身份验证,从而使用 Android WebView 查看受保护页面。
	...
	webview.setWebViewClient(new WebViewClient() { 
		public void onReceivedHttpAuthRequest(WebView view,
	HttpAuthHandler handler, String host, String realm) { handler.proceed("guest", "allow"); } });
	...
与例 1 相似,该代码可以正常运行,但是任何有该代码权限的人都能得到这个密码。

Recommendations:
	绝不能对密码进行硬编码。通常情况下,应对密码加以模糊化,并在外部资源文件中进行管理。在系统中采用明文的形式存储密码,会造成任何有充分权限的人读取和无意中误用密码。至少,密码要先经过 hash 处理再存储。
有些第三方产品宣称可以采用更加安全的方式管理密码。例如,WebSphere Application Server 4.x 用简单的异或加密算法加密数值,但是请不要对诸如此类的加密方式给予完全的信任。WebSphere 以及其他一些应用服务器通常都只提供过期的且相对较弱的加密机制,这对于安全性敏感的环境来说是远远不够的。一般较为安全的解决方法是采用由用户创建的所有者机制,而这似乎也是目前最好的方法。
	对于 Android 以及其他任何使用 SQLite 数据库的平台来说,SQLCipher 是一个好选择 -- 对 SQLite 数据库的扩展为数据库文件提供了透明的 256 位 AES 加密。因此,凭证可以存储在加密的数据库中。

例 3:
	以下代码演示了在将所需的二进制码和存储凭证下载到数据库文件后,将 SQLCipher 集成到 Android 应用程序中的方法。
	import net.sqlcipher.database.SQLiteDatabase;
	...
	SQLiteDatabase.loadLibs(this); 
	File dbFile = getDatabasePath("credentials.db");
    dbFile.mkdirs(); dbFile.delete(); 
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, "credentials", null); 
    db.execSQL("create table credentials(u, p)");
	db.execSQL("insert into credentials(u, p) values(?, ?)", new Object[]{username, password});
	...
请注意,对 android.database.sqlite.SQLiteDatabase 的引用可以使用 net.sqlcipher.database.SQLiteDatabase 代替。
要在 WebView 存储上启用加密,需要使用 sqlcipher.so 库重新编译 WebKit。

Tips:
	1. 可使用 Fortify Java Annotations、FortifyPassword 和 FortifyNotPassword 来指示哪些字段和变量代表密码。
	2. 识别 null password、empty password 和 hardcoded password 时,默认规则只会考虑包含 password 字符的字段和变量。但是,HPE Security Fortify Custom Rules Editor(HPE Security Fortify 自定义规则编辑器)会提供 Password Management 向导,让您轻松创建能够从自定义名称的字段和变量中检测出 password management 问题的规则。
实际解决方案:

​ 如果有条件再写加密算法,加密类的话,写了调用是最好的,但是有时候调用加密之后还是会扫描出来。
​ 其次当然了,也可以直接删掉扫描出问题的代码行。

十、Category: Password Management: Empty Password in Configuration File

问题描述:

​ 在任何时候使用空字符串作为密码都是不恰当的。因为它太容易被猜中了。

官方案例:
Recommendations:
	必须使用非常难以猜测的密码来保护所有的帐户和系统资源。参照以下内容来帮助建立适当密码管理方针。
Tips:
	HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)会从配置文件中搜索那些用于密码属性的常用名称。当发现这样的密码条目时,就会将其标记为问题。
实际解决方案:

​ 和上面的解决方案一样,写加密类或者直接删除问题代码行。

十一、Category: Insecure Randomness

问题描述:

​ 在对安全性要求较高的环境中,使用一个能产生可预测数值的函数作为随机数据源,会产生 Insecure Randomness 错误。电脑是一种具有确定性的机器,因此不可能产生真正的随机性。伪随机数生成器 (PRNG) 近似于随机算法,始于一个能计算后续数值的种子。
​ PRNG 包括两种类型:统计学的 PRNG 和密码学的 PRNG。
​ 统计学的 PRNG 可提供有用的统计资料,但其输出结果很容易预测,因此数据流容易复制。若安全性取决于生成数值的不可预测性,则此类型不适用。
​ 密码学的 PRNG 通过可产生较难预测的输出结果来应对这一问题。为了使加密数值更为安全,必须使攻击者根本无法、或极不可能将它与真实的随机数加以区分。
​ 通常情况下,如果并未声明 PRNG 算法带有加密保护,那么它有可能就是一个统计学的 PRNG,不应在对安全性要求较高的环境中使用,其中随着它的使用可能会导致严重的漏洞(如易于猜测的密码、可预测的加密密钥、会话劫持攻击和 DNS 欺骗)。

官方案例:
示例: 
	下面的代码可利用统计学的 PRNG 为购买产品后仍在有效期内的收据创建一个 URL。
		function genReceiptURL (baseURL){
			var randNum = Math.random();
			var receiptURL = baseURL + randNum + ".html";
			return receiptURL;
		}
	这段代码使用 Math.random() 函数为它所生成的收据页面生成独特的标识符。因为 Math.random() 是一个统计学的PRNG,攻击者很容易猜到由它所生成的字符串。尽管收据系统的底层设计也存在错误,但如果使用了一个不生成可预测收据标识符的随机数生成器(如密码学的 PRNG),会更安全一些。

Recommendations:
	当不可预测性至关重要时,如大多数对安全性要求较高的环境都采用随机性,这时可以使用密码学的 PRNG。不管选择了哪一种 PRNG,都要始终使用带有充足熵的数值作为该算法的种子。(诸如当前时间之类的数值只提供很小的熵,因此不应该使用。)在 JavaScript 中,常规的建议是使用 Mozilla API 中的window.crypto.random() 函数。但这种方法在多种浏览器中都不起作用,包括 Mozilla Firefox 的最新版本。目前没有适用于功能强大的密码学 PRNG 的跨浏览器解决方案。此时应考虑在JavaScript 之外处理任意 PRNG 功能。
实际解决方案:

​ 在网上看过几篇文章,说是将 Math.random().toString().substring(2) 的相关数值扩大NNN倍;亦或是将random改成SorcreRandom
​ 但是我这是采用自定义random的方法来替换常规的random,在第二次扫描之后,问题就解决了。

以js为例添加自定义的random方法:
 function  randnum() {
     var seed = (new Date()).getTime();
     seed = (seed*9301+49297)%233280;
     return seed/(233280.0);
 }
 
将其替换
return Math.random().toString().substring(2)  -->   randnum().toString().substring(2)

你可能感兴趣的:(工作猎及---Java)