Java代码审计手册(2)

此文为转载翻译文章(可能会出现错误)

目录

潜在的XPath注入(XPATH_INJECTION)
找到Struts 1端点(STRUTS1_ENDPOINT)
找到Struts 2端点(STRUTS2_ENDPOINT)
找到了Spring端点(SPRING_ENDPOINT)
禁用Spring CSRF保护(SPRING_CSRF_PROTECTION_DISABLED)
Spring CSRF无限制请求映射(SPRING_CSRF_UNRESTRICTED_REQUEST_MAPPING)
潜在注入(自定义)(CUSTOM_INJECTION)
潜在的SQL注入(SQL_INJECTION)
使用Turbine的潜在SQL注入(SQL_INJECTION_TURBINE)
潜在的SQL / HQL注入(休眠)(SQL_INJECTION_HIBERNATE)
潜在的SQL / JDOQL注入(JDO)(SQL_INJECTION_JDO)
潜在的SQL / JPQL注入(JPA)(SQL_INJECTION_JPA)
潜在的JDBC注入(Spring JDBC)(SQL_INJECTION_SPRING_JDBC)
潜在的JDBC注入(SQL_INJECTION_JDBC)
潜在的Scala光滑注入(SCALA_SQL_INJECTION_SLICK)
潜在的Scala主动脉注射(SCALA_SQL_INJECTION_ANORM)
Vert.x Sql Client(SQL_INJECTION_VERTX)的潜在SQL注入
潜在的Android SQL注入(SQL_INJECTION_ANDROID)
潜在的LDAP注入(LDAP_INJECTION)
使用脚本引擎时可能的代码注入(SCRIPT_ENGINE_INJECTION)
使用Spring Expression时可能的代码注入(SPEL_INJECTION)
使用表达式语言(EL)时可能的代码注入(EL_INJECTION)
Seam记录调用中的潜在代码注入(SEAM_LOG_INJECTION)
使用OGNL表达式时可能的代码注入(OGNL_INJECTION)
使用GroovyShell(GROOVY_SHELL)时可能的代码注入
潜在的HTTP响应拆分(HTTP_RESPONSE_SPLITTING)
日志的潜在CRLF注入(CRLF_INJECTION_LOGS)
潜在的外部控制配置(EXTERNAL_CONFIG_CONTROL)
错误的十六进制串联(BAD_HEXA_CONVERSION)
Hazelcast对称加密(HAZELCAST_SYMMETRIC_ENCRYPTION)
NullCipher不安全(NULL_CIPHER)
未加密的套接字(UNENCRYPTED_SOCKET)
未加密的服务器套接字(UNENCRYPTED_SERVER_SOCKET)
DES不安全(DES_USAGE)
DESede不安全(TDES_USAGE)
没有填充的RSA不安全(RSA_NO_PADDING)
硬编码密码(HARD_CODE_PASSWORD)
硬编码键(HARD_CODE_KEY)
不安全的哈希等于(UNSAFE_HASH_EQUALS)
没有输入验证的Struts表单(STRUTS_FORM_VALIDATION)
XSSRequestWrapper是弱XSS保护(XSS_REQUEST_WRAPPER)
带快捷键的河豚用法(BLOWFISH_KEY_SIZE)
短键(RSA_KEY_SIZE)的RSA使用
未经验证的重定向(UNVALIDATED_REDIRECT)
未经验证的重定向(播放框架)(PLAY_UNVALIDATED_REDIRECT)
春季未验证的重定向(SPRING_UNVALIDATED_REDIRECT)
意外的财产泄漏(ENTITY_LEAK)
批量分配(ENTITY_MASS_ASSIGNMENT)

潜在的XPath注入

错误模式:XPATH_INJECTION

XPath注入风险类似于SQL注入。如果XPath查询包含不受信任的用户输入,则可能会暴露完整的数据源。这可能使攻击者可以访问未经授权的数据或恶意修改目标XML。

参考文献
WASC-39:XPath注入
OWASP:排名前10的2013-A1-注入
CWE-643:XPath表达式内数据的不正确中和(“ XPath注入”)
CERT:IDS09-J。防止XPath注入(存档)
Black Hat Europe 2012:入侵XPath 2.0
Balisage.net:XQuery注入

找到Struts 1端点

错误模式:STRUTS1_ENDPOINT

此类是Struts 1 Action。

将请求路由到此控制器后,将自动实例化包含HTTP参数的Form对象。应检查这些参数的使用,以确保安全使用它们。

找到Struts 2端点

错误模式:STRUTS2_ENDPOINT

在Struts 2中,端点是普通旧Java对象(PO​​JO),这意味着不需要实现/扩展接口/类。

当请求被路由到其控制器(如选定的类)时,提供的HTTP参数将自动映射到该类的设置器。因此,即使表单不包含这些值,此类的所有设置方法也应视为不受信任的输入。攻击者可以简单地在请求中提供其他值,并且只要该对象具有这样的设置器,就可以在对象中设置它们。应检查这些参数的使用,以确保安全使用它们。

找到Spring端点

错误模式:SPRING_ENDPOINT

此类是Spring控制器。带注释的所有方法 RequestMapping (以及它的快捷方式的注释 GetMapping, PostMapping, PutMapping, DeleteMapping,和 PatchMapping)可达远程。应该分析此类,以确保可以将远程公开的方法安全地暴露给潜在的攻击者。

Spring CSRF保护已禁用

错误模式:SPRING_CSRF_PROTECTION_DISABLED

禁用Spring Security的CSRF保护对于标准Web应用程序是不安全的。

禁用此保护的有效用例是公开状态更改操作的服务,该服务只能由非浏览器客户端使用。

不安全的配置:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
    }
}

参考资料
Spring Security官方文档:何时使用CSRF保护
OWASP:跨站点请求伪造
OWASP:CSRF预防备忘单
CWE-352:跨站点请求伪造(CSRF)

Spring CSRF无限制RequestMapping

错误模式:SPRING_CSRF_UNRESTRICTED_REQUEST_MAPPING

带注解的方法 RequestMapping 默认情况下映射到所有HTTP请求方法。然而,Spring Security的CSRF保护默认情况下不为HTTP请求方法激活 GET, HEAD, TRACE,和 OPTIONS (因为这可能导致令牌被泄露)。因此,状态改变注释的方法,与 RequestMapping 和不缩小映射到的HTTP请求方法 POST, PUT, DELETE,或 PATCH 易受CSRF攻击。

脆弱代码:

@Controller
public class UnsafeController {

    @RequestMapping("/path")
    public void writeData() {
        // State-changing operations performed within this method.
    }
}

解决方案(Spring Framework 4.3及更高版本):

@Controller
public class SafeController {

    /**
     * For methods without side-effects use @GetMapping.
     */
    @GetMapping("/path")
    public String readData() {
        // No state-changing operations performed within this method.
        return "";
    }

    /**
     * For state-changing methods use either @PostMapping, @PutMapping, @DeleteMapping, or @PatchMapping.
     */
    @PostMapping("/path")
    public void writeData() {
        // State-changing operations performed within this method.
    }
}
 

解决方案(在Spring Framework 4.3之前):

@Controller
public class SafeController {

    /**
     * For methods without side-effects use either
     * RequestMethod.GET, RequestMethod.HEAD, RequestMethod.TRACE, or RequestMethod.OPTIONS.
     */
    @RequestMapping(value = "/path", method = RequestMethod.GET)
    public String readData() {
        // No state-changing operations performed within this method.
        return "";
    }

    /**
     * For state-changing methods use either
     * RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE, or RequestMethod.PATCH.
     */
    @RequestMapping(value = "/path", method = RequestMethod.POST)
    public void writeData() {
        // State-changing operations performed within this method.
    }
}

参考资料
Spring Security官方文档:使用正确的HTTP动词(CSRF保护)
OWASP:跨站点请求伪造
OWASP:CSRF预防备忘单
CWE-352:跨站点请求伪造(CSRF)

潜在注入(定制)

错误模式:CUSTOM_INJECTION

所确定的方法易于注射。输入应经过验证并正确转义。

易受攻击的代码示例:

SqlUtil.execQuery("select * from UserEntity t where id = " + parameterInput);

有关如何配置自定义签名的详细说明,请参考在线Wiki。

参考文献
WASC-19:SQL注入
OWASP:十大2013-A1注入
OWASP:SQL注入预防速查表
OWASP:查询参数化速查表
CAPEC-66:SQL注入
CWE-89:对SQL命令中使用的特殊元素进行不正确的中和( ‘SQL注入’)

潜在的SQL注入

错误模式:SQL_INJECTION

SQL查询中包含的输入值需要安全地传递。准备好的语句中的绑定变量可用于轻松减轻SQL注入的风险。另外,准备语句时,可以手动转义每个参数。

脆弱代码:

createQuery("select * from User where id = '"+inputId+"'");

解决方案:

import org.owasp.esapi.Encoder;

createQuery("select * from User where id = '"+Encoder.encodeForSQL(inputId)+"'");

参考资料(SQL注入)
WASC-19:SQL注入
CAPEC-66:SQL注入
CWE-89:对SQL命令中使用的特殊元素进行不正确中和(“ SQL注入”)
OWASP:前10名2013-A1-注入
OWASP:SQL注入预防备忘单
OWASP:查询参数备忘单

使用涡轮的潜在SQL注入

错误模式:SQL_INJECTION_TURBINE

SQL查询中包含的输入值需要安全地传递。准备好的语句中的绑定变量可用于轻松减轻SQL注入的风险。Turbine API提供DSL以使用Java代码构建查询。

脆弱代码:

List BasePeer.executeQuery( "select * from Customer where id=" + inputId );

解决方案(使用标准DSL):

Criteria c = new Criteria();
c.add( CustomerPeer.ID, inputId );

List customers = CustomerPeer.doSelect( c );

解决方案(使用专门方法):

Customer customer = CustomerPeer.retrieveByPK( new NumberKey( inputId ) );

解决方案(使用OWASP编码器):

import org.owasp.esapi.Encoder;

BasePeer.executeQuery("select * from Customer where id = '"+Encoder.encodeForSQL(inputId)+"'");

参考(Turbine)
Turbine文档:Criteria Howto
参考(SQL注入)
WASC-19:SQL注入
CAPEC-66:SQL注入
CWE-89:对SQL命令中使用的特殊元素(SQL注入)的不正确中和
OWASP:前10名2013-A1-注入
OWASP:SQL注入预防速查表
OWASP:查询参数化速查表

潜在的SQL / HQL注入(休眠)

错误模式:SQL_INJECTION_HIBERNATE

SQL查询中包含的输入值需要安全地传递。准备好的语句中的绑定变量可用于轻松减轻SQL注入的风险。另外,准备语句时,可以使用休眠标准。

脆弱代码:

Session session = sessionFactory.openSession();
Query q = session.createQuery("select t from UserEntity t where id = " + input);
q.execute();

解决方案:

Session session = sessionFactory.openSession();
Query q = session.createQuery("select t from UserEntity t where id = :userId");
q.setString("userId",input);
q.execute();

动态查询的解决方案(使用Hibernate Criteria):

Session session = sessionFactory.openSession();
Query q = session.createCriteria(UserEntity.class)
    .add( Restrictions.like("id", input) )
    .list();
q.execute();

参考资料(Hibernate)
Hibernate文档:查询条件
Hibernate Javadoc:
渗透测试者的查询对象HQL:测试可疑代码是否可利用的指南。
参考资料(SQL注入)
WASC-19:SQL注入
CAPEC-66:SQL注入
CWE-89:对SQL命令中使用的特殊元素进行不正确中和(“ SQL注入”)
OWASP:前10名2013-A1-注入
OWASP:SQL注入预防备忘单
OWASP:查询参数备忘单

潜在的SQL / JDOQL注入(JDO)

错误模式:SQL_INJECTION_JDO

SQL查询中包含的输入值需要安全地传递。准备好的语句中的绑定变量可用于轻松减轻SQL注入的风险。

脆弱代码:

PersistenceManager pm = getPM();

Query q = pm.newQuery("select * from Users where name = " + input);
q.execute();

解决方案:

PersistenceManager pm = getPM();

Query q = pm.newQuery("select * from Users where name = nameParam");
q.declareParameters("String nameParam");
q.execute(input);

引用(JDO)
JDO:对象检索
引用(SQL注入)
WASC-19:SQL注入
CAPEC-66:SQL注入
CWE-89:对SQL命令(“ SQL注入”)中使用的特殊元素进行不正确的中和
OWASP:2013年排名前10 -A1-注入
OWASP:SQL注入预防速查表
OWASP:查询参数化速查表

潜在的SQL / JPQL注入(JPA)

错误模式:SQL_INJECTION_JPA

SQL查询中包含的输入值需要安全地传递。准备好的语句中的绑定变量可用于轻松减轻SQL注入的风险。

脆弱代码:

EntityManager pm = getEM();

TypedQuery q = em.createQuery(
    String.format("select * from Users where name = %s", username),
    UserEntity.class);

UserEntity res = q.getSingleResult();

解决方案:

TypedQuery q = em.createQuery(
    "select * from Users where name = usernameParam",UserEntity.class)
    .setParameter("usernameParam", username);

UserEntity res = q.getSingleResult();

参考(JPA)
Java EE 6教程:使用Java持久性查询语言创建查询
参考(SQL注入)
WASC-19:SQL注入
CAPEC-66:SQL注入
CWE-89:对SQL命令中使用的特殊元素进行不正确的中和( ‘SQL注入’)
OWASP:十大2013-A1-注入
OWASP:SQL注入预防速查表
OWASP:查询参数化速查表

潜在的JDBC注入(Spring JDBC)

错误模式:SQL_INJECTION_SPRING_JDBC

SQL查询中包含的输入值需要安全地传递。准备好的语句中的绑定变量可用于轻松减轻SQL注入的风险。

脆弱代码:

JdbcTemplate jdbc = new JdbcTemplate();
int count = jdbc.queryForObject("select count(*) from Users where name = '"+paramName+"'", Integer.class);

解决方案:

JdbcTemplate jdbc = new JdbcTemplate();
int count = jdbc.queryForObject("select count(*) from Users where name = ?", Integer.class, paramName);

参考(Spring JDBC)
Spring官方文档:使用JDBC
参考的数据访问(SQL注入)
WASC-19:SQL注入
CAPEC-66:SQL注入
CWE-89:对SQL命令中使用的特殊元素进行不正确的中和(“ SQL注入”)
OWASP:2013年A1注入排名前10位
OWASP:SQL注入预防速查表
OWASP:查询参数化速查表

潜在的JDBC注入

错误模式:SQL_INJECTION_JDBC

SQL查询中包含的输入值需要安全地传递。准备好的语句中的绑定变量可用于轻松减轻SQL注入的风险。

脆弱代码:

Connection conn = [...];
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("update COFFEES set SALES = "+nbSales+" where COF_NAME = '"+coffeeName+"'");

解决方案:

Connection conn = [...];
conn.prepareStatement("update COFFEES set SALES = ? where COF_NAME = ?");
updateSales.setInt(1, nbSales);
updateSales.setString(2, coffeeName);

参考(JDBC)
Oracle文档:Java教程>预准备语句
参考(SQL注入)
WASC-19:SQL注入
CAPEC-66:SQL注入
CWE-89:对SQL命令中使用的特殊元素(“ SQL注入”)进行不正确的中和
OWASP:2013年A1注入排名前10位
OWASP:SQL注入预防速查表
OWASP:查询参数化速查表

潜在的Scala光滑注射

错误模式:SCALA_SQL_INJECTION_SLICK

SQL查询中包含的输入值需要安全地传递。准备好的语句中的绑定变量可用于轻松减轻SQL注入的风险。

脆弱代码:

db.run {
  sql"select * from people where name = '#$value'".as[Person]
}

解决方案:

db.run {
  sql"select * from people where name = $value".as[Person]
}

参考资料(SQL注入)
WASC-19:SQL注入
CAPEC-66:SQL注入
CWE-89:对SQL命令中使用的特殊元素进行不正确中和(“ SQL注入”)
OWASP:前10名2013-A1-注入
OWASP:SQL注入预防备忘单
OWASP:查询参数备忘单

潜在的Scala主动脉注射

错误模式:SCALA_SQL_INJECTION_ANORM

SQL查询中包含的输入值需要安全地传递。准备好的语句中的绑定变量可用于轻松减轻SQL注入的风险。

脆弱代码:

val peopleParser = Macro.parser[Person]("id", "name", "age")

DB.withConnection { implicit c =>
  val people: List[Person] = SQL("select * from people where name = '" + value + "'").as(peopleParser.*)
}

解决方案:

val peopleParser = Macro.parser[Person]("id", "name", "age")

DB.withConnection { implicit c =>
  val people: List[Person] = SQL"select * from people where name = $value".as(peopleParser.*)
}

参考资料(SQL注入)
WASC-19:SQL注入
CAPEC-66:SQL注入
CWE-89:对SQL命令中使用的特殊元素进行不正确中和(“ SQL注入”)
OWASP:前10名2013-A1-注入
OWASP:SQL注入预防备忘单
OWASP:查询参数备忘单

Vert.x Sql Client的潜在SQL注入

错误模式:SQL_INJECTION_VERTX

SQL查询中包含的输入值需要安全地传递。准备好的语句中的绑定变量可用于轻松减轻SQL注入的风险。Vert.x Sql Client API提供了DSL以使用Java代码构建查询。

脆弱代码:

SqlClient.query( "select * from Customer where id=" + inputId ).execute(ar -> ...);

解决方案(使用准备好的语句):

client
    .preparedQuery( "SELECT * FROM users WHERE id=$1" )
    .execute(Tuple.of("julien"))
    .onSuccess(rows -> ...)
    .onFailure(err -> ...);

参考(Vert.x Sql客户端)
Vertx数据库访问文档
参考(SQL注入)
WASC-19:SQL注入
CAPEC-66:SQL注入
CWE-89:对SQL命令(“ SQL注入”)
OWASP中使用的特殊元素进行不正确的中和:前十大2013-A1-Injection
OWASP:SQL注入预防速查表
OWASP:查询参数化速查表

潜在的Android SQL注入

错误模式:SQL_INJECTION_ANDROID

SQL查询中包含的输入值需要安全地传递。准备好的语句中的绑定变量可用于轻松减轻SQL注入的风险。

脆弱代码:

String query = "SELECT * FROM  messages WHERE uid= '"+userInput+"'" ;
Cursor cursor = this.getReadableDatabase().rawQuery(query,null);

解决方案:

String query = "SELECT * FROM  messages WHERE uid= ?" ;
Cursor cursor = this.getReadableDatabase().rawQuery(query,new String[] {userInput});

参考资料(Android SQLite)
InformIT.com:在SQLite中构建安全Android数据库的实用建议
Packtpub.com:了解SQL注入攻击并保护我们的Android应用程序不受
Android数据库支持(企业Android:为企业编程Android数据库应用程序)
Suragch
引用提供的插入,选择,更新和删除查询的安全示例(SQL注入)
WASC-19:SQL注入
CAPEC-66:SQL注入
CWE-89:对SQL命令中使用的特殊元素进行不正确的中和(“ SQL注入”) )
OWASP:2013年A1注入排名前10位
OWASP:SQL注入预防速查表
OWASP:查询参数化速查表

潜在的LDAP注入

错误模式:LDAP_INJECTION

与SQL一样,传递给LDAP查询的所有输入都必须安全传递。不幸的是,LDAP没有像SQL这样的预备语句接口。因此,针对LDAP注入的主要防御措施是在将任何不可信数据包括在LDAP查询中之前对其进行强大的输入验证。

代码有风险:

NamingEnumeration answers = context.search("dc=People,dc=example,dc=com",
        "(uid=" + username + ")", ctrls);

参考文献
WASC-29:LDAP注入
OWASP:前10名2013-A1-注入
CWE-90:LDAP查询(“ LDAP注入”)中使用的特殊元素的不适当中和
LDAP注入指南:了解如何检测LDAP注入并提高LDAP安全性

使用脚本引擎时可能的代码注入

错误模式:SCRIPT_ENGINE_INJECTION

动态代码正在评估中。应仔细分析代码构造。恶意代码执行可能导致数据泄漏或操作系统受损。

如果要评估用户代码,则应应用适当的沙箱(请参阅参考资料)。

代码有风险:

public void runCustomTrigger(String script) {
    ScriptEngineManager factory = new ScriptEngineManager();
    ScriptEngine engine = factory.getEngineByName("JavaScript");

    engine.eval(script); //Bad things can happen here.
}

解决方案:
使用“ Cloudbees Rhino Sandbox”库对JavaScript代码进行安全评估。

public void runCustomTrigger(String script) {
    SandboxContextFactory contextFactory = new SandboxContextFactory();
    Context context = contextFactory.makeContext();
    contextFactory.enterContext(context);
    try {
        ScriptableObject prototype = context.initStandardObjects();
        prototype.setParentScope(null);
        Scriptable scope = context.newObject(prototype);
        scope.setPrototype(prototype);

        context.evaluateString(scope,script, null, -1, null);
    } finally {
        context.exit();
    }
}

参考资料
Cloudbees Rhino沙箱:使用Rhino创建沙箱的实用程序(阻止对所有类的访问)
CodeUtopia.net:在Java
远程代码执行中将Rhino沙箱设计为…:恶意负载示例。给出的样本可用于测试沙盒规则。
CWE-94:代码生成的不当控制(“代码注入”)
CWE-95:动态评估代码中的指令不当中和(“评估注入”)

使用Spring Expression时可能的代码注入

错误模式:SPEL_INJECTION

Spring表达式是使用动态值构建的。值的来源应经过验证,以避免未过滤的值落入此风险代码评估中。

代码有风险:

public void parseExpressionInterface(Person personObj,String property) {

        ExpressionParser parser = new SpelExpressionParser();

        //Unsafe if the input is control by the user..
        Expression exp = parser.parseExpression(property+" == 'Albert'");

        StandardEvaluationContext testContext = new StandardEvaluationContext(personObj);
        boolean result = exp.getValue(testContext, Boolean.class);
[...]

参考文献
CWE-94:代码生成的不当控制(“代码注入”)
CWE-95:动态评估代码(“ Eval注入”)
Spring表达式语言(SpEL)中指令的不正确中和-官方文档,
安全性:表达式语言注入
远程代码执行…通过设计:恶意负载的示例。给出的样本可用于测试沙盒规则。
Spring Data-Commons:(CVE-2018-1273)
Spring OAuth2:CVE-2018-1260

使用表达式语言(EL)时的潜在代码注入

错误模式:EL_INJECTION

使用动态值构建表达式。值的来源应经过验证,以避免未过滤的值落入此风险代码评估中。

代码有风险:

public void evaluateExpression(String expression) {
    FacesContext context = FacesContext.getCurrentInstance();
    ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory();
    ELContext elContext = context.getELContext();
    ValueExpression vex = expressionFactory.createValueExpression(elContext, expression, String.class);
    return (String) vex.getValue(elContext);
}

参考资料
安全性:滥用EL来执行OS命令
Java EE 6教程:表达式语言
CWE-94:代码生成控制不当(“代码注入”)
CWE-95:动态评估代码中指令的不适当中和(“代码注入”)’)
敏锐的安全性:表达式语言注入
Dan Amodio的博客:带有表达式语言注入的
远程代码远程执行代码…设计使然:恶意负载示例。给出的样本可用于测试沙盒规则。

Seam记录调用中的潜在代码注入

错误模式:SEAM_LOG_INJECTION

Seam Logging API支持一种表达语言,以引入bean属性来记录消息。表达式语言也可能是不必要的代码执行的来源。

在这种情况下,将使用动态值构建表达式。值的来源应经过验证,以避免未过滤的值落入此风险代码评估中。

代码有风险:

public void logUser(User user) {
    log.info("Current logged in user : " + user.getUsername());
    //...
}

解决方案:

public void logUser(User user) {
    log.info("Current logged in user : #0", user.getUsername());
    //...
}

参考文献
JBSEAM-5130:风险
记录文档JBoss接缝:日志记录(官方文档)
Java EE 6教程:表达式语言
CWE-94:代码生成的不当控制(“代码注入”)
CWE-95:中的指令不适当地中和动态评估代码(“评估注入”)

使用OGNL表达式时的潜在代码注入

错误模式:OGNL_INJECTION

使用动态值构建表达式。值的来源应经过验证,以避免未过滤的值落入此风险代码评估中。

代码有风险:

public void getUserProperty(String property) {
  [...]
  //The first argument is the dynamic expression.
  return ognlUtil.getValue("user."+property, ctx, root, String.class);
}

解决方案:
通常,评估OGNL表达式的方法不应接收用户输入。它旨在用于静态配置和JSP。

参考资料
惠普企业版:AlvaroMuñoz
Gotham的Struts 2 OGNL表达注入数字科学:对CVE-2017-5638的分析
Apache Struts2:漏洞S2-016
Apache Struts 2文档:OGNL

使用GroovyShell时可能的代码注入

错误模式:GROOVY_SHELL

使用动态值构建表达式。值的来源应经过验证,以避免未过滤的值落入此风险代码评估中。

代码有风险:

public void evaluateScript(String script) {
  GroovyShell shell = new GroovyShell();
  shell.evaluate(script);
}

解决方案:
通常,评估Groovy表达式的方法不应接收来自低特权用户的用户输入。

参考文献
Hacking Jenkins第2部分-为未经身份验证的RCE滥用元编程!由Orange蔡
詹金斯RCE有效载荷由Orange蔡
POC为CVE-2019-1003001亚当约旦
利用Groovy代码评估的各种有效载荷

潜在的HTTP响应拆分

错误模式:HTTP_RESPONSE_SPLITTING

当HTTP请求包含意外 字符CR 和 LF字符时,服务器可能会以输出流进行响应,该输出流被解释为两个不同的HTTP响应(而不是一个)。攻击者可以控制第二响应并发起攻击,例如跨站点脚本和缓存中毒攻击。根据OWASP,几乎所有现代Java EE应用服务器中的问题都已得到解决,但是验证输入还是更好的。如果您担心这种风险,则应该在所关注的平台上进行测试,以查看底层平台是否允许 CR 或将 LF 字符插入标头。报告此漏洞的优先级较低,因为它要求Web容器易受攻击。

代码有风险:

String author = request.getParameter(AUTHOR_PARAMETER);
// ...
Cookie cookie = new Cookie("author", author);
response.addCookie(cookie);

参考文献
OWASP:HTTP响应拆分
CWE-113:HTTP标头中CRLF序列的不正确中和(“ HTTP响应拆分”)CWE-93:CRLF序列不正确的中和(“ CRLF注入”)

日志可能的CRLF注入

错误模式:CRLF_INJECTION_LOGS

如果将来自不受信任来源的数据放入记录器中并没有正确进行中和,则攻击者可能伪造日志条目或包含恶意内容。插入的虚假条目可能会用于歪曲统计信息,分散管理员注意力,甚至使他人参与恶意行为。如果日志文件是自动处理的,则攻击者可以通过破坏文件格式或注入意外字符来使该文件不可用。攻击者还可能向日志文件中注入代码或其他命令,并利用日志处理实用程序中的漏洞(例如,命令注入或XSS)。

代码有风险:

String val = request.getParameter("user");
String metadata = request.getParameter("metadata");
[...]
if(authenticated) {
    log.info("User " + val + " (" + metadata + ") was authenticated successfully");
}
else {
    log.info("User " + val + " (" + metadata + ") was not authenticated");
}
恶意用户可能会发送带有值的元数据参数 
"Firefox) was authenticated successfully\r\n[INFO] User bbb (Internet Explorer"。

解决方案:
您可以手动清理每个参数。

log.info("User " + val.replaceAll("[\r\n]","") + " (" + userAgent.replaceAll("[\r\n]","") + ") was not authenticated");

您还可以配置记录器服务以替换所有消息事件的新行。这是使用该replace 功能进行LogBack的示例配置 。

%-5level - %replace(%msg){'[\r\n]', ''}%n

最后,您可以使用记录器实现,该记录器实现将新行替换为空格。OWASP安全日志记录项目具有Logback和Log4j的实现。

参考文献
CWE-117:对日志的输出中和不当
CWE-93:对CRLF序列的不正确中和(“ CRLF注入”)
CWE-93:对CRLF序列的不正确中和(“ CRLF注入”)
OWASP安全日志记录

潜在的外部控制配置

错误模式:EXTERNAL_CONFIG_CONTROL

允许系统设置的外部控制可能会中断服务或导致应用程序以意外的和潜在的恶意方式运行。攻击者可能会通过提供不存在的目录名称而导致错误,或者连接到数据库的未授权部分。

代码有风险:

conn.setCatalog(request.getParameter("catalog"));

参考文献
CWE-15:系统或配置设置的外部控制

错误的十六进制串联

错误模式:BAD_HEXA_CONVERSION

将包含哈希签名的字节数组转换为人类可读的字符串时,如果逐字节读取该数组,则可能会发生转换错误。下面的示例说明了该方法的用法,该方法 Integer.toHexString() 将从所计算的哈希值的每个字节中修剪所有前导零。

MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] resultBytes = md.digest(password.getBytes("UTF-8"));

StringBuilder stringBuilder = new StringBuilder();
for(byte b :resultBytes) {
    stringBuilder.append( Integer.toHexString( b & 0xFF ) );
}

return stringBuilder.toString();

此错误削弱了计算的哈希值,因为它引入了更多的冲突。例如,对于上述功能,哈希值“ 0x0679”和“ 0x6709”都将输出为“ 679”。

在这种情况下,该方法 Integer.toHexString() 应替换 String.format() 为:

stringBuilder.append( String.format( "%02X", b ) );

参考
CWE-704:类型转换或转换不正确

Hazelcast对称加密

错误模式:HAZELCAST_SYMMETRIC_ENCRYPTION

Hazelcast的网络通信被配置为使用对称密码(可能是DES或Blowfish)。

仅这些密码不能提供完整性或安全认证。首选使用非对称加密。

参考文献
WASC-04:传输层保护不足
Hazelcast文档:加密
CWE-326:加密强度不足

NullCipher不安全

错误模式:NULL_CIPHER

NullCipher在生产应用中很少有意使用。它通过返回与提供的纯文本相同的密文来实现Cipher接口。在某些情况下,例如测试,NullCipher可能是合适的。

脆弱代码:

Cipher doNothingCihper = new NullCipher();
[...]
//The ciphertext produced will be identical to the plaintext.
byte[] cipherText = c.doFinal(plainText);

解决方案:
避免使用NullCipher。意外使用可能会带来重大的保密风险。

参考
CWE-327:使用破损或有风险的密码算法

未加密的套接字

错误模式:UNENCRYPTED_SOCKET

使用的通信通道未加密。攻击者可以通过拦截网络流量来读取流量。

漏洞代码:
普通套接字(明文通信):

Socket soc = new Socket("www.google.com",80);

解决方案:
SSL套接字(安全通信):

Socket soc = SSLSocketFactory.getDefault().createSocket("www.google.com", 443);

除了使用SSL套接字外,您还需要确保对SSLSocketFactory的使用会执行所有适当的证书验证检查,以确保您不受中间人攻击。请阅读OWASP传输层保护备忘单,以获取有关如何正确执行此操作的详细信息。

参考资料
OWASP:十大2010-A9-传输层保护不足
OWASP:十大2013-A6-敏感数据暴露
OWASP:传输层保护备忘单
WASC-04:传输层保护不足
CWE-319:敏感信息的明文传输

未加密的服务器套接字

错误模式:UNENCRYPTED_SERVER_SOCKET

使用的通信通道未加密。攻击者可以通过拦截网络流量来读取流量。

漏洞代码:
普通服务器套接字(明文通信):

ServerSocket soc = new ServerSocket(1234);

解决方案:
SSL服务器套接字(安全通信):

ServerSocket soc = SSLServerSocketFactory.getDefault().createServerSocket(1234);

除了使用SSL服务器套接字之外,您还需要确保对SSLServerSocketFactory的使用会执行所有适当的证书验证检查,以确保您不受中间人攻击。请阅读OWASP传输层保护备忘单,以获取有关如何正确执行此操作的详细信息。

参考资料
OWASP:十大2010-A9-传输层保护不足
OWASP:十大2013-A6-敏感数据暴露
OWASP:传输层保护备忘单
WASC-04:传输层保护不足
CWE-319:敏感信息的明文传输

DES不安全

错误模式:DES_USAGE

DES被认为是现代应用程序的强密码。当前,NIST建议使用AES分组密码而不是DES。

弱代码示例:

Cipher c = Cipher.getInstance("DES/ECB/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, k, iv);
byte[] cipherText = c.doFinal(plainText);

解决方案示例:

Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, k, iv);
byte[] cipherText = c.doFinal(plainText);

参考
NIST撤销了过时的数据加密标准
CWE-326:加密强度不足

DESede不安全

错误模式:TDES_USAGE

三重DES(也称为3DES或DESede)被认为是现代应用程序的强密码。当前,NIST建议使用AES分组密码而不是3DES。

弱代码示例:

Cipher c = Cipher.getInstance("DESede/ECB/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, k, iv);
byte[] cipherText = c.doFinal(plainText);

解决方案示例:

Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, k, iv);
byte[] cipherText = c.doFinal(plainText);

参考
NIST撤销了过时的数据加密标准
CWE-326:加密强度不足

没有填充的RSA是不安全的

错误模式:RSA_NO_PADDING

该软件使用RSA算法,但未合并最佳非对称加密填充(OAEP),这可能会削弱加密效果。

脆弱代码:

Cipher.getInstance("RSA/NONE/NoPadding")

解决方案:
该代码应替换为:

Cipher.getInstance("RSA/ECB/OAEPWithMD5AndMGF1Padding")

参考文献
CWE-780:在没有OAEP
根实验室的情况下使用RSA算法:为什么RSA加密填充至关重要

硬编码密码

错误模式:HARD_CODE_PASSWORD

密码不应保留在源代码中。源代码可以在企业环境中广泛共享,当然可以在开放源代码中共享。为了安全管理,密码和秘密密钥应存储在单独的配置文件或密钥库中。(硬编码键由硬编码键 模式单独报告 )

脆弱代码:

private String SECRET_PASSWORD = "letMeIn!";

Properties props = new Properties();
props.put(Context.SECURITY_CREDENTIALS, "p@ssw0rd");

参考文献
CWE-259:使用硬编码密码

硬编码键

错误模式:HARD_CODE_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);

参考文献
CWE-321:硬编码密码密钥的使用

不安全的哈希等于

错误模式:UNSAFE_HASH_EQUALS

由于暴露了比较时机,攻击者可能能够检测到秘密哈希的值。 调用函数 Arrays.equals() 或时 String.equals(),如果匹配较少的字节,它们将更早退出。

脆弱代码:

String actualHash = ...

if(userInput.equals(actualHash)) {
    ...
}

解决方案:

String actualHash = ...

if(MessageDigest.isEqual(userInput.getBytes(),actualHash.getBytes())) {
    ...
}

参考文献
CWE-203:通过DiscrepancyKey公开信息

没有输入验证的Struts Form

错误模式:STRUTS_FORM_VALIDATION

表单输入应具有最少的输入验证。预防性验证有助于针对各种风险提供深度防御。

可以通过实现一种validate 方法来引入验证 。

public class RegistrationForm extends ValidatorForm {

    private String name;
    private String email;

    [...]

    public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
        //Validation code for name and email parameters passed in via the HttpRequest goes here
    }
}

参考文献
CWE-20:输入验证不正确
CWE-106:Struts:未使用插件框架

XSSRequestWrapper是弱XSS保护

错误模式:XSS_REQUEST_WRAPPER
实现HttpServletRequestWrapper 所谓的 XSSRequestWrapper 通过各种博客网站公布。

过滤功能很弱,原因如下:

它仅涵盖参数,不包括标头和辅助通道输入
可轻松绕过替换功能链(请参见下面的示例)
这是非常具体的不良模式的黑名单(而不是良好/有效输入的白名单)

旁路示例:

alert(1)

先前的输入将转换为""。的删除"vbscript:"是在替换之后""
为了获得更强大的保护,请选择一种解决方案,该解决方案应 按照OWASP XSS Prevention备忘单中定义的XSS保护规则在视图(模板或JSP)中自动对字符进行编码 。

参考文献
WASC-8:跨站点脚本
OWASP:XSS预防速查表
OWASP:前10名2013-A3:跨站点脚本(XSS)
CWE-79:网页生成过程中输入的不正确中和(“跨站点脚本”)

短键河豚用法

错误模式:BLOWFISH_KEY_SIZE

Blowfish密码支持从32位到448位的密钥大小。较小的密钥长度使密文容易受到暴力攻击。如果需要使用河豚,则在生成密钥时至少应使用128位熵。

如果可以更改算法,则应改用AES分组密码。

脆弱代码:

KeyGenerator keyGen = KeyGenerator.getInstance("Blowfish");
keyGen.init(64);

解决方案:

KeyGenerator keyGen = KeyGenerator.getInstance("Blowfish");
keyGen.init(128);

参考
河豚(密码)
CWE-326:加密强度不足

RSA与短键的使用

错误模式:RSA_KEY_SIZE

NIST建议将 2048位及更高的 密钥用于RSA算法。

“数字签名验证| RSA:  1024 ≤ len(n) < 2048 |旧版使用”
“数字签名验证| RSA:  len(n) ≥ 2048 |可接受”
-NIST:关于过渡使用密码算法和密钥长度的建议p.7

脆弱代码:

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(512);

解决方案:
KeyPairGenerator的创建应至少具有2048位密钥大小,如下所示。

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);

参考文献
NIST:关于密钥管理的最新出版物
NIST:关于过渡使用密码算法和密钥长度的建议p.7
RSA实验室:3.1.5在RSA密码系统中应使用多大的密钥?
Wikipedia:不对称算法密钥长度
CWE-326:加密强度不足
Keylength.com(BlueKrypt):汇总密钥长度建议。

未经验证的重定向

错误模式:UNVALIDATED_REDIRECT

当应用程序将用户重定向到由用户提供的未经验证的参数指定的目标URL时,将发生未经验证的重定向。此类漏洞可用于促进网络钓鱼攻击。

方案
1.诱使用户访问恶意URL:  http://website.com/login?redirect=http://evil.vvebsite.com/fake/login
2.将用户重定向到伪造的登录页面,该页面看起来像他们信任的站点。(http://evil.vvebsite.com/fake/login)
3.用户输入其凭据。
4.恶意站点窃取用户的凭据,并将其重定向到原始网站。

这种攻击是合理的,因为大多数用户在重定向后不会仔细检查URL。同样,重定向到身份验证页面也很常见。

脆弱代码:

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    [...]
    resp.sendRedirect(req.getParameter("redirectUrl"));
    [...]
}

解决方案/对策:
不接受来自用户的重定向目标
接受目标密钥,并使用它来查找目标(合法)目标
仅接受相对路径
白名单网址(如果可能)
验证URL的开头是否为白名单的一部分

参考文献
WASC-38:URL重定向器滥用
OWASP:前10名2013-A10:未经验证的重定向和转发
OWASP:未经验证的重定向和转发备忘单
CWE-601:URL重定向到不受信任的站点(“开放重定向”)

未经验证的重定向(播放框架)

错误模式:PLAY_UNVALIDATED_REDIRECT

当应用程序将用户重定向到由用户提供的未经验证的参数指定的目标URL时,将发生未经验证的重定向。此类漏洞可用于促进网络钓鱼攻击。

方案
1.诱使用户访问恶意URL:  http://website.com/login?redirect=http://evil.vvebsite.com/fake/login
2.将用户重定向到伪造的登录页面,该页面看起来像他们信任的站点。(http://evil.vvebsite.com/fake/login)
3.用户输入其凭据。
4.恶意站点窃取用户的凭据,并将其重定向到原始网站。

这种攻击是合理的,因为大多数用户在重定向后不会仔细检查URL。同样,重定向到身份验证页面也很常见。

脆弱代码:

def login(redirectUrl:String) = Action {
    [...]
    Redirect(url)
}

解决方案/对策:
不接受来自用户的重定向目标
接受目标密钥,并使用它来查找目标(合法)目标
仅接受相对路径
白名单网址(如果可能)
验证URL的开头是否为白名单的一部分

参考文献
WASC-38:URL重定向器滥用
OWASP:前10名2013-A10:未经验证的重定向和转发
OWASP:未经验证的重定向和转发备忘单
CWE-601:URL重定向到不受信任的站点(“开放重定向”)

Spring未验证的重定向

错误模式:SPRING_UNVALIDATED_REDIRECT

当应用程序将用户重定向到由用户提供的未经验证的参数指定的目标URL时,将发生未经验证的重定向。此类漏洞可用于促进网络钓鱼攻击。

方案
1.诱使用户访问恶意URL:  http://website.com/login?redirect=http://evil.vvebsite.com/fake/login
2.将用户重定向到伪造的登录页面,该页面看起来像他们信任的站点。(http://evil.vvebsite.com/fake/login)
3.用户输入其凭据。
4.恶意站点窃取用户的凭据,并将其重定向到原始网站。

这种攻击是合理的,因为大多数用户在重定向后不会仔细检查URL。同样,重定向到身份验证页面也很常见。

脆弱代码:

@RequestMapping("/redirect")
public String redirect(@RequestParam("url") String url) {
    [...]
    return "redirect:" + url;
}

解决方案/对策:
不接受来自用户的重定向目标
接受目标密钥,并使用它来查找目标(合法)目标
仅接受相对路径
白名单网址(如果可能)
验证URL的开头是否为白名单的一部分

参考文献
WASC-38:URL重定向器滥用
OWASP:前10名2013-A10:未经验证的重定向和转发
OWASP:未经验证的重定向和转发备忘单
CWE-601:URL重定向到不受信任的站点(“开放重定向”)

Spring实体泄漏

错误模式:SPRING_ENTITY_LEAK

永久对象永远不会被API返回/接受。它们可能会导致UI上的业务逻辑泄漏,对数据库中持久对象的未授权篡改。

持久性注释
1. javax.persistence.Entity(JPA)
2. javax.jdo.spi.PersistenceCapable(JDO)
3. org.springframework.data.mongodb.core.mapping.Document(文档数据库)

脆弱代码:

@GetMapping("/sample/api")
    public SampleEntityClass sampleMethod(Principal principal) {
        Query query = getEntityManager().createQuery("select c from SampleTable c");
        SampleEntityClass entityObject = query.getResultList().get(0);
        return entityObject;
    }
 

@GetMapping("/sampletwo/api")
public void update(SampleEntityClass updateEntity) {
  ...
}

解决方案/对策:
应该使用数据传输对象来代替,仅包括作为API的输入/响应所需的参数。
敏感参数应在传输到UI之前正确删除。
只有经过适当的卫生检查后,数据才应保留在数据库中。

意外的属性泄漏

错误模式:ENTITY_LEAK

永久对象不应该由API返回。它们可能会导致UI上的业务逻辑泄漏,对数据库中持久对象的未授权篡改。

脆弱代码:

@javax.persistence.Entity
class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    private String password;
}

[...]
@Controller
class UserController {

    @GetMapping("/user/{id}")
    public UserEntity getUser(@PathVariable("id") String id) {

        return userService.findById(id).get(); //Return the user entity with ALL fields.
    }

}

解决方案/对策:
应该使用数据传输对象来代替,仅包括作为API的输入/响应所需的参数。
敏感参数应在传输到UI之前正确删除。
只有经过适当的卫生检查后,数据才应保留在数据库中。
Spring MVC解决方案:特别是
在Spring中,您可以应用以下解决方案来允许或禁止特定字段。

@Controller
class UserController {

   @InitBinder
   public void initBinder(WebDataBinder binder, WebRequest request)
   {
      binder.setAllowedFields(["username","firstname","lastname"]);
   }

}

参考文献
OWASP Top 10-2017 A3:敏感数据暴露
OWASP备忘单:质量分配
CWE-212:敏感数据的不正确跨界清除
CWE-213:有意信息暴露

批量分配

错误模式:ENTITY_MASS_ASSIGNMENT

永久对象不应该由API返回。它们可能会导致UI上的业务逻辑泄漏,对数据库中持久对象的未授权篡改。

脆弱代码:

@javax.persistence.Entity
class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    private String password;

    private Long role;
}

[...]
@Controller
class UserController {

    @PutMapping("/user/")
    @ResponseStatus(value = HttpStatus.OK)
    public void update(UserEntity user) {

        userService.save(user); //ALL fields from the user can be altered
    }

}

一般准则:
应该使用数据传输对象来代替,仅包括作为API的输入/响应所需的参数。
敏感参数应在传输到UI之前正确删除。
只有经过适当的卫生检查后,数据才应保留在数据库中。
Spring MVC解决方案:特别是
在Spring中,您可以应用以下解决方案来允许或禁止特定字段。

有白名单:

@Controller
class UserController {

   @InitBinder
   public void initBinder(WebDataBinder binder, WebRequest request)
   {
      binder.setAllowedFields(["username","password"]);
   }

}

带有黑名单:

@Controller
class UserController {

   @InitBinder
   public void initBinder(WebDataBinder binder, WebRequest request)
   {
      binder.setDisallowedFields(["role"]);
   }

}

参考
OWASP备忘单:质量分配
CWE-915:动态确定的对象属性的不正确控制的修改

你可能感兴趣的:(JAVA,java,安全)