总结常见漏洞的代码审计方法

漏洞描述
HTTP响应截断是由于应用程序未对用户提交的数据进行严格过滤c检测文件是否存在,当用户恶意提交包含 CR(回车 , 即URL编码%0d或r)和 LF(换行符,即URL编码%0a或n)的HTTP请求 , 服务器可能会创建两个 HTTP 响应c检测文件是否存在,攻击者可以控制第二个响应并加载攻击 。攻击者可控制响应的内容构造XSS攻击,其中响应中包含恶意的或其它代码在用户的浏览器中执行,也有可能让用户重定向到攻击者控制的Web内容或在用户的主机上执行恶意操作 。
审计方法
检查对响应头字段是否进行安全处理 。
如果未对响应头进行任何安全处理,则为确认:
//未对响应头做任何安全处理,审计时为确认String data;if (data != null){ response.addHeader("Location", "/author.jsp?lang=" + data);}
再举一例:
//同样的未对响应头做任何安全处理,审计时为确认String author = request.getParameter(AUTHOR_PARAMETER);// ...Cookie cookie = new Cookie("author", author);response.addCookie(cookie);
如果对响应头做了响应的安全处理,则为误报:
//使用Refenence类对环境变量值进行编码,剔除特殊字符,为误报if (data != null){ String decode = Reference.decode(data); response.addHeader("Location", "/author.jsp?lang=" + decode);}
修复方案:
1、对用户的输入进行合理验证 , 对特殊字符(如、’、”等)等进行编码 。
2、创建一份安全字符白名单,只接受完全由这些受认可的字符组成的输入出现在 HTTP 响应头文件中 。3、使用源代码静态分析工具,进行自动化的检测,可以有效的发现源代码中的 HTTP 响应截断问题 。
2. 硬编码问题
漏洞描述
硬编码问题,是指将敏感数据(包括口令和加密密钥,部分账号的密码以及其他敏感信息等)硬编码在程序中 。
审计步骤
1、看扫描出的硬编码是否为常规单词(或通读代码查看是否有硬编码敏感文件)
2、如果是常规单词,则为误报,如:
//fipAddress为硬编码public class IPaddress{private String ipAddress = "172.16.254.1";public static void main(String[] args){//...}}
可以使用 javap -c命令来反编译 class 来发现其中硬编码的服务器 IP 地址,此处反
编译器的输出信息可以直接透漏服务器的明文 IP 地址为172.16.254.1
再举一例:
//SECRET_PASSWORD为硬编码private String SECRET_PASSWORD = "No fear in my heart!"; Properties props = new Properties(); props.put(Context.SECURITY_CREDENTIALS, "password");
3、如果是随机字符串,则为确认或待确认,如:
//1546272000000为硬编码byte[] sr = hBaseClient.buildRowKey(devId, "1546272000000");
再举一例:
//qafgshw1900wxxxx为硬编码private String accessKeyId = "qafgshw1900wxxxx";
4、追踪key值,如果key为硬编码则为确认,如:
//key值为硬编码byte[] key = {1, 2, 3, 4, 5, 6, 7, 8};SecretKeySpec spec = new SecretKeySpec(key, "AES");Cipher aes = Cipher.getInstance("AES");aes.init(Cipher.ENCRYPT_MODE, spec);return aesCipher.doFinal(secretData);
追踪key值 , 如果追踪不到或者为安全形式,则为误报,如
//无法再继续追踪key值 , 为误报public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {//key为密钥byte[] keyBytes = decryptBASE64(key);PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.ENCRYPT_MODE, privateKey);return cipher.doFinal(data);}
再来一栗:
//存储密钥 。KeyStore.getInstance("PKCS12")为密钥库,为误报try{KeyStore keyStore = KeyStore.getInstance("PKCS12");keyStore.load(null, null);KeyGenerator keyGen = KeyGenerator.getInstance("AES");keyGen.init(128);Key key = keyGen.generateKey();keyStore.setKeyEntry("secret", key, "password".toCharArray(), null);keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray());} catch (Exception ex){ex.printStackTrace();}
修复方案
推荐使用配置文件或者通过配置中心来下发这些敏感配置 , 密码和密钥应存储在单独的加密配置文件或密钥库中 。
3. SQL注入
漏洞描述
注入攻击的本质,是程序把用户输入的数据当做代码执行 。这里有两个关键条件:
第一是用户能够控制输入;
第二是用户输入的数据被拼接到要执行的代码中从而被执行 。
sql 注入漏洞则是程序将用户输入数据拼接到了 sql 语句中,从而攻击者即可构造、改变 sql 语义从而进行攻击 。
漏洞举例
(1) 直接通过拼接 sql
@RequestMapping("/SqlInjection/{id}")public ModelAndView SqlInjectTest(@PathVariable String id){String mysqldriver = "com.mysql.jdbc.Driver";String mysqlurl = "jdbc:mysql://127.0.0.1:3306/test?user=root&password=123456&useUnicode=true&characterEncoding=utf8&autoReconnect=true";#直接通过拼接 sqlString sql = "select * from user where id=" + id;ModelAndView mav = new ModelAndView("test2"); try{Class.forName(mysqldriver);Connection conn = DriverManager.getConnection(mysqlurl);PreparedStatement pstt = conn.prepareStatement(sql);ResultSet rs = pstt.executeQuery();
再来一例:
//没有做任何其他安全处理措施stmt = conn.createStatement();rs = stmt.executeQuery("select * from user where username = '" + username+"' and password='"+password+"'");
(2) 预编译使用有误
漏洞举例:
//只使用了占位符@RequestMapping("/SqlInjection/{id}")public ModelAndView SqlInjectTest(@PathVariable String id){String mysqldriver = "com.mysql.jdbc.Driver";String mysqlurl = "jdbc:mysql://127.0.0.1:3306/test?user=root&password=123456&useUnicode=true&characterEncoding=utf8&autoReconnect=true";String sql = "select * from user where id= ?";ModelAndView mav = new ModelAndView("test2"); try{Class.forName(mysqldriver);Connection conn = DriverManager.getConnection(mysqlurl);PreparedStatement pstt = conn.prepareStatement(sql);//pstt.setObject(1, id); //一般使用有误的是没有用这一句,编码者以为在上面的sql语句中直接使用占位符就可以了 。ResultSet rs = pstt.executeQuery();
审计步骤:查看预编译的完整性 , 关键函数定位 ()、()、()、()关联上下文搜索 set* 开头的函数 。
(3) %和_( 中模糊查询)问题
@RequestMapping("/SqlInjection/{id}")public ModelAndView SqlInjectTest(@PathVariable String id){String mysqldriver = "com.mysql.jdbc.Driver";String mysqlurl = "jdbc:mysql://127.0.0.1:3306/test?user=root&password=123456&useUnicode=true&characterEncoding=utf8&autoReconnect=true";String sql = "select * from user where id= ?";ModelAndView mav = new ModelAndView("test2"); try{Class.forName(mysqldriver);Connection conn = DriverManager.getConnection(mysqlurl);PreparedStatement pstt = conn.prepareStatement(sql);pstt.setObject(1, id); //使用预编译ResultSet rs = pstt.executeQuery();
审计步骤:定位相关 sql 语句上下文 , 查看是否有显式过滤机制 。
修复方案:上面的代码片段即使这样依然存在 sql 注入,原因是没有手动过滤% 。预编译是不能处理这个符号的,以需要手动过滤,否则会造成慢查询,造成 dos 。
(4) order by 问题
String sql = “Select * from news where title =?”+ “order by ‘” + time + “’asc”
审计步骤:定位相关 sql 语句上下文,查看是否有显式过滤机制 。
修复方案:类似上面的这种 sql 语句 order by 后面是不能用预编译处理的只能通过拼接处理,所以需要手动过滤 。
(5) 有关$符号的情况
#{}:相当于jdbc中的,传入的字符串,需要赋值后使用 , 可以有效防止sql注入
${}:是输出变量的值,传入的变量 , 直接在sql中执行,无法防止sql注入 简单的说就是#{}传过来的参数带单引号’' , 而${}传过来的参数不带单引号 。
但是是动态SQL,只能用${},用#{}会多个’ '导致sql语句失效 。此外还有一个like 语句后也需要用${} 。
//需要转义的字符串仍使用$delete from ${tableName}
修复方案:对于可以使用#{}的情况,直接使用#{}即可解决问题对于不能使用#{}的情况(如),需要增加额外的过滤逻辑,以此判断输入内容是否正常,如字段类型、字段长度等
4. maven不安全模块
漏洞描述
Maven,是一个Java开发比较常用的项目管理工具,可以对Java项目进行构建、依赖管理 。当它配置一个不安全的模块时 , 即存在安全风险 。
审计方法
查看配置的版本是否属于安全版本区间 。如果是,则确认:
【总结常见漏洞的代码审计方法】//3.9版本是存在漏洞的版本 。安全版本是3.11以上com.hazelcasthazelcast-client3.9
修复方案
配置为安全版本即可(同时应注意解决兼容性问题)
5. 服务端请求伪造(SSRF)
漏洞描述
SSRF是攻击者让服务端发起指定的请求 , 攻击的目标一般是从外网无法访问的内网系统 。SSRF形成的原因大都是由于代码中提供了从其他服务器应用获取数据的功能但没有对目标地址做过滤与限制 。比如从指定URL链接获取图片、下载等 。一般利用http协议来探测端口,利用file协议读取任意文件 。
利用场景
SSRF漏洞一般位于远程图片加载与下载、图片或文章收藏功能、URL分享、通过URL在线翻译、转码等功能点处 。
关键词/接口/类包
// JavaHttpURLConnection.getInputStreamURLConnection.getInputStreamRequest.Get.executeRequest.Post.executeURL.openStreamImageIO.readOkHttpClient.newCall.executeHttpClients.executeHttpClient.execute
审计方法
1、内网系统的SSRF直接为误报(内网系统之间互调互传)
2、检查请求的URL是否为外部可控 , 即由外部传入
3、检查请求的返回 , 是否对请求的返回数据做了安全处理
漏洞示例
//请求URL为外部可控,返回数据直接展示String url = request.getParameter("picurl");StringBuffer response = new StringBuffer();URL pic = new URL(url);HttpURLConnection con = (HttpURLConnection) pic.openConnection();con.setRequestMethod("GET");con.setRequestProperty("User-Agent", "Mozilla/5.0");//发起请求,触发漏洞BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));String inputLine;while ((inputLine = in.readLine()) != null) { response.append(inputLine);}in.close();modelMap.put("resp",response.toString());return "getimg.htm";
再举一例:
//HttpClients函数的SSRF漏洞代码,审计时为确认:String url = request.getParameter("url");CloseableHttpClient client = HttpClients.createDefault();HttpGet httpGet = new HttpGet(url);HttpResponse httpResponse = client.execute(httpGet); //发起请求
修复方案
本文到此结束,希望对大家有所帮助 。