提问者:小点点

如何防止Spring Boot将危险控制器包含在生产配置文件中?


tl; dr如何根据Spring配置文件排除类包含在bootJar生成的jar文件中?

我会故意含糊不清,但会尽量提供尽可能多的信息。

我有一个C#。NETweb api(JSONHTTP/1.1)。这个api从第三方接收一个加密的有效负载。这个第三方有一个为Java编写的库,但不是C#。由于时间限制和其他因素,我们决定用Spring Boot建立一个JavaAPI,并调用该API,而不是自己实现它们的库。

流量是

  1. 将加密的有效负载发送到C#API
  2. (C#)将加密的有效负载发送到JavaAPI
  3. (Java)解密加密的有效载荷
  4. (Java)使用第三方库进行数据转换/验证
  5. (Java)重新加密有效载荷
  6. (Java)将加密的有效负载返回给C#API
  7. (C#)发送给不同的第三方

我们有一套目前用C#编写的自动化集成测试。

我们需要确保,给定一个有效的有效负载,当它被发送到C#API时,有效负载被转发到JavaAPI,被正确解密,被正确转换,并被正确重新加密以供其他第三方处理。

为了促进这一点,我在JavaAPI上创建了一个新控制器,它接收明文有效负载,对其进行加密(就像第三方一样)并将其返回给调用者。然后,调用者可以发送加密的有效负载,在JavaAPI上执行不同的控制器来进行解密验证,并将重新加密的有效负载发送回调用者。

JavaAPI信任这些自动化创建的有效负载。JavaAPI在运行时生成一个新的密钥对,当它收到明文有效负载时,它使用运行时生成的私钥对其进行签名,并信任使用运行时生成的公钥签名的消息。

我现在的问题是,我不希望这个功能在生产中可用。这些有效负载的内容太敏感了,坏人无法操纵。

我通过向控制器添加@Profile("test")属性解决了部分问题。这允许我在运行时配置它,例如

java -jar sensitive-app.jar --spring.profiles.active=production

这样,在运行时,Spring会阻止控制器工作

{
    "timestamp": "2018-03-16T23:20:00.781+0000",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/dangerous-endpoint"
}

但是,我知道没有什么是不可破解的。尽可能地,我想确保这不会被任何访问盒子的不良行为者击中;或者换句话说,即使攻击者访问了盒子,并以

java -jar sensitive-app.jar --spring.profiles.active=test

控制器仍然无法正常工作。

我正在使用spring-boot gradle插件构建这个jar。

我能想到的唯一方法是首先防止控制器被包含在jar中。

我尝试将此添加到我的构建脚本中

bootJar {
    exclude('sensitive/dangerous/**')
}

但是我看到类文件仍然包含在jar中。

然后我想知道是否有某种方法可以让Spring插件根据属性智能地包含或排除文件,例如

gradlew clean build bootJar -Dspring.build.profile=production

但什么也找不到。

我还试图排除源集中的文件

sourceSets {
    main {
        java {
            if (project.environment == 'prod') {
                exclude '**/dangerous/**'
            }
        }
    }
}

但是class文件仍然在jar文件中(在BOOT-INF/class文件夹下)

注意:此控制器需要在所有非生产环境中可用,因此不能直接编译此文件。

如何将类排除在bootJar生成的jar文件中;或者有没有一种方法可以告诉gradle构建将包含在Spring配置文件中的文件?


共2个答案

匿名用户

我会采取不同的策略。

我会编写危险控制器,以便它在JVM环境中寻找非常特定的东西(例如自定义系统属性或环境变量)。

>

  • 让控制器测试变量,如果检测到它正在生产中使用,则抛出异常。大声失败,而且很早!可能将其放入静态初始化程序中,以便在控制器加载/连接时发生故障。

    安排属性/变量/任何设置的方式不能被您选择的构建/部署方法论意外复制到生产环境中。(有多种方法可以做到这一点…但是你故意对上下文含糊其辞,这使得很难提出合适的方法。)

    这种方法的优点是它应该不受干扰构建脚本的人的影响。但是建议通过构建脚本也排除控制器。(腰带和括号…)

  • 匿名用户

    您可以使用gradle源集来做到这一点。

    def env = project.hasProperty('env') ? project.env : 'development'
    
    sourceSets {
        main {
            java {
                srcDir 'src'
                if ( env == 'production' ) {
                    println "DANGEROUS source set is being used for project complie (env = $env)!"
                } else {
                    println "SAFE source set is being used for project complie (env = $env)."
                    exclude '**/dangerous/**'
                }
            }
        }
    }
    

    使用危险的Java代码调用进行编译:

    gradle clean build bootJar -Penv=production
    

    如果您使用的是IDE,它可能不尊重您声明的源代码集。您必须使用gradleIDE(eclipse、Ide)插件并添加相同的逻辑。

    在部署到各种环境时,通常可以通过CI作业传入此类内容。此外,您可以将application.properties文件加载到gradle或使用系统属性来确定环境。