diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsChecker.java index 136ee9d..3ba3571 100644 --- a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsChecker.java +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsChecker.java @@ -7,7 +7,15 @@ package com.keyware.sonar.java.rules.checkers; import org.sonar.check.Rule; +import org.sonar.java.ast.parser.ArgumentListTreeImpl; +import org.sonar.java.model.expression.LiteralTreeImpl; +import org.sonar.java.model.expression.MemberSelectExpressionTreeImpl; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.semantic.Symbol; +import org.sonar.plugins.java.api.semantic.Type; +import org.sonar.plugins.java.api.tree.Arguments; +import org.sonar.plugins.java.api.tree.ExpressionTree; +import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.Tree; import javax.annotation.Nonnull; @@ -22,11 +30,15 @@ import java.util.List; */ @Rule(key = "SessionCacheParamsChecker") public class SessionCacheParamsChecker extends IssuableSubscriptionVisitor { + + //违规集合 private static final List HIDED_PARAMS = List.of( - "id", - "token" + "userid", + "token", + "url", + "userpassword" ); - + private static final String requestType = "javax.servlet.http.HttpServletRequest"; @Override public List nodesToVisit() { @@ -35,8 +47,44 @@ public class SessionCacheParamsChecker extends IssuableSubscriptionVisitor { @Override public void visitNode(@Nonnull Tree tree) { - + // ((MemberSelectExpressionTree)methodInvocationTree.methodSelect()).expression().symbolType() + MethodInvocationTree methodInvocationTree = (MethodInvocationTree) tree; + Symbol.MethodSymbol methodSymbol = methodInvocationTree.methodSymbol(); + if(methodSymbol.isMethodSymbol()) { + //获取参数 + ExpressionTree expressionTree = methodInvocationTree.methodSelect(); + if(expressionTree instanceof MemberSelectExpressionTreeImpl){ + MemberSelectExpressionTreeImpl selectExpressionTree = (MemberSelectExpressionTreeImpl) expressionTree; + //获取参数类型 + Type type = selectExpressionTree.expression().symbolType(); + if(type != null){ + //判断参数类型和调用方法符不符合要求 + if(requestType.equals(type.fullyQualifiedName()) + && ("getParameter".equals(methodSymbol.name()) || "getCookies".equals(methodSymbol.name()))) { + //如果是getCookies方法直接抛错 + if("getCookies".equals(methodSymbol.name())){ + reportIssue(methodInvocationTree, "页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取"); + }else { + //获取参数 + Arguments arguments = methodInvocationTree.arguments(); + if(arguments instanceof ArgumentListTreeImpl){ + for (ExpressionTree argument : (ArgumentListTreeImpl) arguments) { + if(argument instanceof LiteralTreeImpl){ + LiteralTreeImpl literalTree = (LiteralTreeImpl) argument; + //获取参数值 + String name = literalTree.token().text().replace("\"", "").toLowerCase(); + //判断是否是违规项 + if(HIDED_PARAMS.contains(name)){ + reportIssue(methodInvocationTree, "页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取"); + break; + } + } + } + } + } + } + } + } + } } - - } diff --git a/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UserStatusVerifyChecker.java b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UserStatusVerifyChecker.java new file mode 100644 index 0000000..40e953a --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/UserStatusVerifyChecker.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:信息安全性设计准则检查插件 + * 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ + +package com.keyware.sonar.java.rules.checkers; + +import org.sonar.check.Rule; +import org.sonar.java.ast.parser.ArgumentListTreeImpl; +import org.sonar.java.model.declaration.VariableTreeImpl; +import org.sonar.java.model.expression.LiteralTreeImpl; +import org.sonar.java.model.expression.MemberSelectExpressionTreeImpl; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.tree.*; + +import java.util.List; + +/** + * 对用户进行身份鉴别并建立一个新的会话时让原来的会话失效 + * + * @author RenFengJiang + * @date 2024/1/24 + */ +@Rule(key = "UserStatusVerifyChecker") +public class UserStatusVerifyChecker extends IssuableSubscriptionVisitor { + @Override + public List nodesToVisit() { + return List.of(Tree.Kind.METHOD); + } + + @Override + public void visitNode(Tree tree) { + //将Tree强转成MethodTree + MethodTree methodTree = (MethodTree) tree; + //判断是否是doFilter或doFilter方法 + boolean boo = verifyMethod(methodTree); + if(boo){ + //内部实现类 + VisitMethod visitMethod = new VisitMethod(this); + //调用内部实现类 + methodTree.block().accept(visitMethod); + //判断是否满足4个条件 + if(visitMethod.booOne && visitMethod.booTwo && visitMethod.booThree && visitMethod.booFour){ + + }else{ + context.reportIssue(this, methodTree.simpleName(), "对用户进行身份鉴别并建立一个新的会话时让原来的会话失效"); + } + + }else { + context.reportIssue(this, methodTree.simpleName(), "对用户进行身份鉴别并建立一个新的会话时让原来的会话失效"); + } + } + + public static boolean verifyMethod(MethodTree methodTree) { + //判断是否是doFilter或doFilter方法 + if ("doFilter".equals(methodTree.simpleName().name()) || "doFilter".equals(methodTree.simpleName().name())) { + //获取参数 + List parameters = methodTree.parameters(); + for (VariableTree variable : parameters) { + if (variable instanceof VariableTreeImpl) { + //判断参数是否有ServletRequest + VariableTreeImpl variableTree = (VariableTreeImpl) variable; + if (variableTree.type().toString().endsWith("ServletRequest")) { + return true; + } + } + } + } + return false; + } + + static class VisitMethod extends BaseTreeVisitor { + private final UserStatusVerifyChecker checker; + + private boolean booOne = false; + private boolean booTwo = false; + private boolean booThree = false; + private boolean booFour = false; + + + public VisitMethod(UserStatusVerifyChecker checker) { + this.checker = checker; + } + + //方法调用节点 + @Override + public void visitMethodInvocation(MethodInvocationTree tree) { + ExpressionTree expressionTree = tree.methodSelect(); + if(expressionTree instanceof MemberSelectExpressionTreeImpl){ + //判断是否调用指定的方法 + MemberSelectExpressionTreeImpl selectExpressionTree = (MemberSelectExpressionTreeImpl) expressionTree; + if("getParameter".equals(selectExpressionTree.identifier().name()) || "getSession".equals(selectExpressionTree.identifier().name())){ + //获取到调用方法的参数 + Arguments arguments = tree.arguments(); + //调用判断参数的方法 + verifyParam(arguments); + }else if ("invalidate".equals(selectExpressionTree.identifier().name())){ + booFour = true; + } + } + } + + public void verifyParam(Arguments arguments){ + if(arguments instanceof ArgumentListTreeImpl){ + for (ExpressionTree argument : (ArgumentListTreeImpl) arguments) { + if(argument instanceof LiteralTreeImpl){ + LiteralTreeImpl literalTree = (LiteralTreeImpl) argument; + String strName = literalTree.token().text().replace("\"", "").toLowerCase(); + if("username".equals(strName)){ + booOne = true; + }else if("password".equals(strName)){ + booTwo = true; + }else if("false".equals(strName)){ + booThree = true; + } + } + } + } + } + + } +} diff --git a/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/SessionCacheParamsChecker.html b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/SessionCacheParamsChecker.html new file mode 100644 index 0000000..cd92f6f --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/SessionCacheParamsChecker.html @@ -0,0 +1,16 @@ + + +

页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取

+

页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取

+
+
+
+

合规解决方案

+
+
+
diff --git a/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/SessionCacheParamsChecker.json b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/SessionCacheParamsChecker.json new file mode 100644 index 0000000..164cbab --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/SessionCacheParamsChecker.json @@ -0,0 +1,13 @@ +{ + "title": "页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "28suo" + ], + "defaultSeverity": "Minor" +} \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/UserStatusVerifyChecker.html b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/UserStatusVerifyChecker.html new file mode 100644 index 0000000..9fbb8fb --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/UserStatusVerifyChecker.html @@ -0,0 +1,16 @@ + + +

对用户进行身份鉴别并建立一个新的会话时让原来的会话失效

+

对用户进行身份鉴别并建立一个新的会话时让原来的会话失效

+
+
+
+

合规解决方案

+
+
+
\ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/UserStatusVerifyChecker.json b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/UserStatusVerifyChecker.json new file mode 100644 index 0000000..a0b1bb0 --- /dev/null +++ b/sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/UserStatusVerifyChecker.json @@ -0,0 +1,13 @@ +{ + "title": "对用户进行身份鉴别并建立一个新的会话时让原来的会话失效", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "28suo" + ], + "defaultSeverity": "Minor" +} \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/test/files/SessionCacheParamsChecker.java b/sonar-keyware-plugins-java/src/test/files/SessionCacheParamsChecker.java index 34f4d6a..55bac41 100644 --- a/sonar-keyware-plugins-java/src/test/files/SessionCacheParamsChecker.java +++ b/sonar-keyware-plugins-java/src/test/files/SessionCacheParamsChecker.java @@ -1,22 +1,31 @@ -package com.example; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.*; + public class ExampleServlet extends HttpServlet { + private static final long serialVersionUID = 1391640560504378168L; + public void doGet(HttpServletRequest request, HttpServletResponse response) { // 直接从request获取参数 - String param = request.getParameter("userId"); // Noncompliant {{建议将页面隐藏域字段、Cookie、URL等关键参数缓存到服务器端的会话中,并通过会话获取}} - + String param = request.getParameter("userId"); // Noncompliant {{页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取}} + request.getParameter("userpassword");// Noncompliant {{页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取}} + request.getParameter("token");// Noncompliant {{页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取}} + request.getParameter("url");// Noncompliant {{页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取}} // 直接从request获取Cookies - Cookie[] cookies = request.getCookies(); + Cookie[] cookies = request.getCookies();// Noncompliant {{页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取}} // 将参数存储到session HttpSession session = request.getSession(); session.setAttribute("sessionParam", param); // 其他代码... } - private void get(HttpServletRequest request, HttpServletResponse response){ + @RestController + class TestController{ + @GetMapping("/get") + public void get(HttpServletRequest request){ + String userId = request.getParameter("userId");// Noncompliant {{页面隐藏域字段、Cookie、URL等关键参数不能直接获取,应缓存到服务器端的会话中并通过会话获取}} + } } } \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/test/files/UserStatusVerifyChecker.java b/sonar-keyware-plugins-java/src/test/files/UserStatusVerifyChecker.java new file mode 100644 index 0000000..df914f4 --- /dev/null +++ b/sonar-keyware-plugins-java/src/test/files/UserStatusVerifyChecker.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:信息安全性设计准则检查插件 + * 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ + +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; + +public class ExampleServlet { + private static final long serialVersionUID = 1391640560504378168L; + + public class AuthenticationFilter implements Filter { + public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {// Noncompliant {{对用户进行身份鉴别并建立一个新的会话时让原来的会话失效}} +// HttpServletRequest request = (HttpServletRequest) req; +// boolean isValidUser = false; +// String username = request.getParameter("username"); +// String password = request.getParameter("password"); +// // 这里应通过相关业务逻辑来验证用户名和密码的准确性 + isValidUser = UserService.validate(username, password); + if (isValidUser) { + HttpSession oldSession = request.getSession(false); + if (oldSession != null) { + oldSession.invalidate(); + } + HttpSession newSession = request.getSession(true); + newSession.setMaxInactiveInterval(30 * 60); + newSession.setAttribute("username", username); + chain.doFilter(req, resp); // 继续执行下一个过滤器或请求 + } else { + request.getRequestDispatcher("/login.jsp").forward(request, resp); // 跳转到登录页面 + } + } + } + + public class AuthenticationInterceptor extends HandlerInterceptorAdapter { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// Noncompliant {{对用户进行身份鉴别并建立一个新的会话时让原来的会话失效}} + boolean isValidUser = false; + String username = request.getParameter("username"); + String password = request.getParameter("password"); + isValidUser = UserService.validate(username, password); + if (isValidUser) { + HttpSession oldSession = request.getSession(false); + if (oldSession != null) { +// oldSession.invalidate(); + } + HttpSession newSession = request.getSession(true); + newSession.setMaxInactiveInterval(30 * 60); + newSession.setAttribute("username", username); + return true; // 继续下一个拦截器或请求处理器 + } else { + response.sendRedirect("/login.jsp"); // 跳转到登录页面 + return false; + } + } + } +} \ No newline at end of file diff --git a/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsCheckerTest.java b/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsCheckerTest.java new file mode 100644 index 0000000..6a17a9a --- /dev/null +++ b/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/SessionCacheParamsCheckerTest.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:信息安全性设计准则检查插件 + * 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ + +package com.keyware.sonar.java.rules.checkers; + +import com.keyware.sonar.java.utils.FilesUtils; +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +/** + * TODO SessionCacheParamsCheckerTest + * + * @author RenFengJiang + * @date 2024/1/24 + */ +public class SessionCacheParamsCheckerTest { + @Test + public void test() { + CheckVerifier.newVerifier() + .onFile("src/test/files/SessionCacheParamsChecker.java") + .withCheck(new SessionCacheParamsChecker()) + .withClassPath(FilesUtils.getClassPath("target/test-jars")) + .verifyIssues(); + } +} diff --git a/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/UserStatusVerifyCheckerTest.java b/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/UserStatusVerifyCheckerTest.java new file mode 100644 index 0000000..1b02cfe --- /dev/null +++ b/sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/UserStatusVerifyCheckerTest.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:信息安全性设计准则检查插件 + * 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ + +package com.keyware.sonar.java.rules.checkers; + +import com.keyware.sonar.java.utils.FilesUtils; +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +/** + * 对用户进行身份鉴别并建立一个新的会话时让原来的会话失效 + * + * @author RenFengJiang + * @date 2024/1/24 + */ +public class UserStatusVerifyCheckerTest { + + @Test + void detected() { + + CheckVerifier.newVerifier() + .onFile("src/test/files/UserStatusVerifyChecker.java") + .withCheck(new UserStatusVerifyChecker()) + .withClassPath(FilesUtils.getClassPath("target/test-jars")) + .verifyIssues(); + } +}