静态代码扫描——PMD自定义规则实践(Log日志文件中不要输出敏感信息)
来源:互联网 发布:女寝还魂知乎 编辑:程序博客网 时间:2024/05/18 02:15
上篇文章用一个简单的规则举例,帮助大家快速了解如何自定义规则。接下来我们开始使用比较复杂的例子进行剖析撰写规则的过程。
我还是以自定义规则的思路逐步讲解,但是不会和上篇一样解释的非常详细,很多细节希望大家能够进行思考后获得恍然大悟的感觉:
1. 明确想要自定义的规则。
需要自定义的规则大多来自开发的需求。这次举例的规则是:
Log日志文件中不要输出敏感信息。根据过往的代码样本总结出可定义为敏感信息的字段包括
- pid
- uid
- imei
- classname
- getLocalClassName
- getPackageCodePath
- getPackagePath
- android.os.Process.myPid
- android.os.Process.myUid
- android.os.Process.getUidForName
其中有些是字符串,有些是获取特定字段的方法,所涉及的主要是手机相关信息、类名、代码路径等。
2. 列举会触犯这种规则的所有不同的写法。
错误样例的代码:
public class TestLog{ static Logger Log = Logger.getLogger("log"); static boolean DEBUG = true; static boolean DEBUG1 = false; public static void main(String []args){ Context cont = activity.getApplicationContext(); String classname = activity.getLocalClassName(); String pcodeName = cont.getPackageCodePath(); int id= android.os.Process.myPid(); String pid =String.valueOf(id); int uicd= android.os.Process.myUid(); String uid = String.valueOf(uicd); int idname= android.os.Process.getUidForName("pay"); String imei = ((TelephonyManager)getSystemService(TELEPHONY_SERVICE)).getDeviceId(); int bbq=activity.getLocalClassName(); Log.i("classname", classname);//触发规则 Log.i("pcodeName", pcodeName);//触发规则 Log.i("pid", pid);//触发规则 Log.i("uid", uid);//触发规则 Log.i("imei", imei); //触发规则 Log.i("imei", imei.length); Log.i("imei", imei.size()); Log.i("imei:", activity.getLocalClassName());//触发规则 Log.i("imei:", MYUUID); Log.i("imei:", imei.toString());//触发规则 Log.i("imei:", ab.imei.toString());//触发规则 Log.i("imei:", bbq);//触发规则 Log.i("imei:", idname);//触发规则 Log.i("imei:", id);//触发规则 Log.i("imei:", uicd);//触发规则 Log.i("imei:", pcodeName);//触发规则 Log.i("imei:", 101); if (DEBUG) { Log.i("imei", imei);//触发规则 } if (DEBUG1) { Log.i("imei", imei); } } }
我在代码中标记出了触发规则
的代码,大家可以根据代码前后文理解这些代码语句输出了什么敏感信息。
3. 使用designer.bat分析所有写法的抽象语法树的特点。
在语法树分析阶段,就得把这些触发错误的各种代码进行归类。
a. 在日志中直接输出敏感信息字符串。
b. 日志中直接输出敏感信息字符串做了处理。
c. 日志中输出的变量在前文中被赋值敏感信息。
d. 日志被日志开关包裹,开关为true时,才会输出敏感信息。
4. 编写规则代码捕捉这种特点。
package net.sourceforge.pmd.lang.java.rule.androidredline;import java.util.ArrayList;import java.util.HashSet;import java.util.List;import java.util.Set;import org.jaxen.JaxenException;import net.sourceforge.pmd.RuleContext;import net.sourceforge.pmd.lang.ast.Node;import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;import net.sourceforge.pmd.lang.java.ast.ASTExpression;import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;import net.sourceforge.pmd.lang.java.ast.ASTName;import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;import net.sourceforge.pmd.lang.java.ast.ASTType;import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;import net.sourceforge.pmd.lang.java.ast.ASTVariableInitializer;import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;/** * @author yuanwei * @category log中敏感信息泄露检测 */public class LogBlockRule extends AbstractJavaRule { private static Set<String> SensitiveStrings = new HashSet<String>(); private List<ASTName> astNamewithLog =(List<ASTName>)new ArrayList<ASTName>(); private List<ASTName> SASTNames =(List<ASTName>)new ArrayList<ASTName>(); private List<ASTVariableDeclaratorId> SensitiveVariables =(List<ASTVariableDeclaratorId>)new ArrayList<ASTVariableDeclaratorId>(); private List<String> BooleanStrings=new ArrayList<String>(); static { SensitiveStrings.add("classname"); SensitiveStrings.add("pid"); SensitiveStrings.add("uid"); SensitiveStrings.add("imei"); SensitiveStrings.add("getLocalClassName"); SensitiveStrings.add("getPackageCodePath"); SensitiveStrings.add("getPackagePath"); SensitiveStrings.add("android.os.Process.myPid"); SensitiveStrings.add("android.os.Process.myUid"); SensitiveStrings.add("android.os.Process.getUidForName"); } @Override public Object visit(ASTCompilationUnit node, Object data) { checkLogRule(node,data); return super.visit(node, data); } @SuppressWarnings("unchecked") private void checkLogRule(Node node,Object data) { //long start = System.currentTimeMillis(); String xpathBoolean = ".//FieldDeclaration/VariableDeclarator/VariableInitializer/Expression/PrimaryExpression" +"/PrimaryPrefix/Literal/BooleanLiteral[@True='true']"; pickUpLogMethods(node); //遍历找出源代码中的log.*代码 if(!this.astNamewithLog.isEmpty()){ try { List<ASTBooleanLiteral> xpathBooleanStringNames = (List<ASTBooleanLiteral>) node.findChildNodesWithXPath(xpathBoolean); if(xpathBooleanStringNames.size()>0){ for(ASTBooleanLiteral booleanLiteral:xpathBooleanStringNames){ ASTVariableDeclarator variableDeclarator = booleanLiteral.getFirstParentOfType(ASTVariableDeclarator.class); ASTVariableDeclaratorId variableDeclaratorId = variableDeclarator.getFirstChildOfType(ASTVariableDeclaratorId.class); this.BooleanStrings.add(variableDeclaratorId.getImage()); } } List<ASTName> xpathLogNames = this.astNamewithLog; if(xpathLogNames.size()>0){ for(ASTName name:xpathLogNames){ String imageString = name.getImage(); if(imageString!=null && imageString.contains("Log.")){ ASTIfStatement ifStatement=name.getFirstParentOfType(ASTIfStatement.class); ASTBlockStatement blockStatement=name.getFirstParentOfType(ASTBlockStatement.class); List<ASTName> names2=(List<ASTName>) blockStatement.findDescendantsOfType(ASTName.class); if(names2.size()>0){ for(ASTName name2:names2){ if(name2!=null){ String imageString2=name2.getImage(); boolean sflag=CheckIsSensitiveString(imageString2); //当前没发现包含敏感信息,把该ASTName节点存储后续解析 if (!sflag) { this.SASTNames.add(name2); } //当前发现包含敏感信息,确认是否被if包围 if (sflag) { //被if判断包围,确认判断条件是否为true if(ifStatement!=null){ ASTExpression astExpression=ifStatement.getFirstDescendantOfType(ASTExpression.class); ASTName astName =astExpression.getFirstDescendantOfType(ASTName.class); if(astName!=null){ String astNameString=astName.getImage(); //判断条件的值为true if(this.BooleanStrings.size()>0 && this.BooleanStrings.contains(astNameString)){ addViolation(data, name2); } } }else { //没有被if判断包围,触发规则 addViolation(data, name2); } } } } } } } } //第二层敏感信息监测 List<ASTVariableDeclaratorId> variableDeclaratorIds = node.findDescendantsOfType(ASTVariableDeclaratorId.class); //找出定义的所有变量 if (variableDeclaratorIds.size()>0) { for(ASTVariableDeclaratorId variableDeclaratorId:variableDeclaratorIds){ ASTType type = variableDeclaratorId.getTypeNode(); if(!(type.jjtGetParent() instanceof ASTFormalParameter)){ ASTName astName=variableDeclaratorId.getFirstParentOfType(ASTVariableDeclarator.class).getFirstDescendantOfType(ASTName.class); if(astName!=null){ if (CheckIsSensitiveString(astName.getImage())) { this.SensitiveVariables.add(variableDeclaratorId); } } } } if(this.SensitiveVariables.size()>0){ for(ASTVariableDeclaratorId SensitiveVariable:this.SensitiveVariables){ for(ASTName SecondastName:this.SASTNames){ String astNameimage=SecondastName.getImage(); if(!(hasNullInitializer(SensitiveVariable)) && astNameimage!=null&&SensitiveVariable.getImage().equalsIgnoreCase(astNameimage)){ //被if判断包围 ASTIfStatement ifStatement=SecondastName.getFirstParentOfType(ASTIfStatement.class); if(ifStatement!=null){ ASTExpression astExpression=ifStatement.getFirstDescendantOfType(ASTExpression.class); ASTName astName3 =astExpression.getFirstDescendantOfType(ASTName.class); if(astName3!=null){ String astNameString=astName3.getImage(); //判断条件的值为true if(this.BooleanStrings.size()>0 && this.BooleanStrings.contains(astNameString)){ addViolation(data, SecondastName); } } //没有被if判断包围,触发规则 }else{ addViolation(data, SecondastName); } } } } } } } catch (JaxenException e) { e.printStackTrace(); }finally{ this.astNamewithLog.clear(); this.SASTNames.clear(); this.BooleanStrings.clear(); this.SensitiveVariables.clear(); } } } //判断是否包含敏感信息 private boolean CheckIsSensitiveString(String imageString2) { for(String SensitiveString:SensitiveStrings){ if(imageString2.equalsIgnoreCase(SensitiveString)){ return true; } if (imageString2!=null&&imageString2.contains(".")) { String[] partStrings=imageString2.split("\\."); int LastIndex=partStrings.length-1; if (partStrings[LastIndex].equals("length")||partStrings[LastIndex].equals("size")) { return false; }else { for (int i = 0; i < partStrings.length; i++) { String partString = partStrings[i]; if (partString.equalsIgnoreCase(SensitiveString)) { return true; } } } } } return false; } //判断变量是否为null,如果初始化为null则剔除 private boolean hasNullInitializer(ASTVariableDeclaratorId var) { ASTVariableInitializer init = var.getFirstDescendantOfType(ASTVariableInitializer.class); if (init != null) { try { List<?> nulls = init.findChildNodesWithXPath("Expression/PrimaryExpression/PrimaryPrefix/Literal/NullLiteral"); return !nulls.isEmpty(); } catch (JaxenException e) { return false; } } return false; } private void pickUpLogMethods(Node node){ //遍历找出源代码中的log.*代码 List<ASTStatementExpression> pexs = node.findDescendantsOfType(ASTStatementExpression.class); for(ASTStatementExpression ast : pexs){ ASTPrimaryPrefix primaryPrefix = ast.jjtGetChild(0).getFirstDescendantOfType(ASTPrimaryPrefix.class); if (primaryPrefix != null) { ASTName name = primaryPrefix.getFirstChildOfType(ASTName.class); if (name != null) { String imageString = name.getImage(); if(imageString.startsWith("Log.")){ astNamewithLog.add(name); } } } } }}
为了方便大家的理解,代码加了注释。
5. 创建自己的xml规则文件,内容包括规则的相关信息。
在上一篇文章中已经详细的介绍过,不再赘述。
6. 运行PMD扫描错误代码,验证是否能触发自定义规则。
特别需要注意的一点,规则完成后,使用样例代码验证只是确认自己撰写的逻辑是否正确。后续一定要经过大量实际代码扫描的验证,逐渐的提高规则的准确性,降低误报率。
- 静态代码扫描——PMD自定义规则实践(Log日志文件中不要输出敏感信息)
- 静态代码扫描——PMD自定义规则入门
- 静态代码扫描——FindBugs自定义规则入门
- Android静态代码扫描-自定义规则
- PMD 自定义规则实践入门样例
- 在后台日志中写入信息-输出信息到log
- Java静态代码扫描 - CheckStyle/FindBugs/PMD/JTest
- tomcat日志配置,log信息如何输出到同一个文件
- log4j自定义级别并将新级别日志信息输出到指定带日期格式的log文件
- log4j自定义级别并将新级别日志信息输出到指定带日期格式的log文件
- 自定义log日志输出到文件中保存
- 自定义log日志输出到文件中保存
- MyEclipse集成PMD代码检测插件自定义规则配置文件
- android中超实用自定义log日志输出工具类
- NS3中日志信息输出到文件
- 编写自定义的PMD规则
- 编写自定义的PMD规则
- 编写自定义的PMD规则
- ubuntu16.04安装KDE
- Javascript正则表达式
- Java的finally语句到底是在return之前还是之后执行?
- 《图解HTTP》读书笔记
- shiro控制不同模块登录地址
- 静态代码扫描——PMD自定义规则实践(Log日志文件中不要输出敏感信息)
- Linux top命令的用法详细详解
- 自定义标签TLD文件中,rtexprvalue属性是什么意思
- OSError: libcudart.so.7.5: cannot open shared object file: No such file or directory
- maven 不能设置为web3.0人解决方法
- java图片验证码
- Codeigniter框架的更新事务(transaction)BUG及解决方法
- Android JS交互
- 主键、外键