不用在命令行上输入值,设置Java系统属性

6
我是一名有用的助手,可以翻译文本。

我有一些Java代码依赖于一个系统属性super.secret.password。当我运行我的应用程序时,我需要设置该属性。应用程序将由一个shell脚本启动,并且密码将保存在一个最小读取权限的文件中。

我真的不想写:

java -Dsuper.secret.password=letmein gov.fortknox.MyApp

因为任何人只要能够进入机器并运行pstop命令就可以看到密码是什么。

那么,有没有一种好的方法可以设置系统属性而不在命令行中公开它们呢?

我们想到的唯一通用的解决方案是编写一个小的C程序,从文件中读取系统属性,然后使用JNI调用API启动JVM。不用说,我们不愿意这样做。

如果没有一种不使用命令行设置它们的方法,是否有一种方法可以隐藏命令行不被窥探?我们正在使用Red Hat Enterprise Linux Server 5.5。

就目前而言,所涉及的应用程序实际上是 JBoss EAP 4.3.0,我们使用系统属性来填充其 XML 配置文件中的替换构造(${like.this})。有针对 JBoss 的解决方案 - 要么使用 SystemPropertiesService(默认情况下,通过 deploy 目录中的 properties-service.xml 文件进行配置),要么将 -P option 传递给 run.sh。然而,我更感兴趣的是更一般的情况,即这可能是任何 Java 程序。


1
我会创建一个属性,用于指定包含密码的文件,并赋予适当的读写权限。 - Peter Lawrey
@Peter:如果密码的使用者是我自己编写的代码,那么这个方法可以奏效。但在这种情况下,它被JBoss的模板机制(以及另一个第三方模板机制,但这不太重要)使用。有没有办法告诉JBoss的模板机制使用系统属性中命名的文件内容,而不是系统属性本身?如果没有,那么这并不能解决问题。 - Tom Anderson
在这种情况下,我建议您限制访问该框的人员仅限于那些被允许查看密码的人。 ;) - Peter Lawrey
也许我会让你告诉他,因为他已经非常忙了! - Tom Anderson
6个回答

3
如果您的担忧是以明文方式暴露 super.secret.password 的值,但您不担心某人因为您已经使用了权限或其他方式解决了此问题而以正确的密码值调用您的程序,则我认为您可以在启动脚本中简单加密密码,并有一个包装类对其进行解密。
java -Dsuper.secret.password=BbWvOuliHZVHVwsXudsj14d1iXzo655R gov.fortknox.DecryptWrapper

如果凭据是用于数据源,我还应该指出其他特定于 JBoss 的解决方案: SecureIdentityLoginModule 基本上执行上述操作,而 PBEUtils 在与 SecureIdentityLoginModule 结合使用时提供了一个密钥库解决方案。请参见EncryptingDataSourcePasswords
最后,Peter Lawery 的建议也是有效的,可以使用文件。

DecryptWrapper类会做什么? - Tom Anderson
实际上会有几个密码:一些用于数据源,一些用于 Web 服务。对于数据源,SecureIdentityLoginModule 看起来非常有趣 - 谢谢。我们可能能够找到一些方法在 Web 服务中也使用它,在那里我们有一些自己的代码路径。 - Tom Anderson

3
您可以在启动时读取文件,并调用 System.setProperty()。对于 Web 应用程序,使用ServletContextListener,这样它就会尽早发生。有关快速示例,请参见此答案更新: 对于 JBoss 加载其配置文件的情况,这可能不够早。

一般来说,对于完全由程序员控制的代码,这是一个不错的解决方案。但在像应用服务器这样的环境中,无法将文件读取代码注入到足够靠近启动的位置。这就是为什么我对一种在调用Java时设置属性的方法感兴趣,而不是在代码中设置。 - Tom Anderson
最终,我们做了这件事。我们对 JBoss 的核心组件进行了子类化,该组件在启动非常早期就开始工作,并在其初始化方法中进行了属性加载。这样做并不美观,如果 Red Hat 支持团队看到它们可能会感到非常惊讶,但它确实有效。 - Tom Anderson

1

您可以在包含主方法的类的静态初始化器中从某个文件中读取它:

    static {
        try {
          FileReader fr = new FileReader(FILE_NAME);
          // read the property
          System.setProperty(property.getName(), property.getValue());
        } catch (final FileNotFoundException ex) {
          logger.log(Level.SEVERE, ex.getMessage(), ex);
        } catch (final IOException ex) {
          logger.log(Level.SEVERE, ex.getMessage(), ex);
        }
    } 
    ...
    public static void main(...){
    ...
    }

1

0

您可以使用JAVA_TOOL_OPTIONS环境变量并在其中设置属性。但它仍将在您的脚本文件中可见。


这对我来说是新的。根据快速谷歌搜索,JAVA_TOOL_OPTIONS似乎被JVMTI使用。您能否解释一下您认为这在这里有何帮助? - Tom Anderson
Java命令会将JAVA_TOOL_OPTIONS环境变量中的任何内容作为附加的命令行参数进行处理。 - Dakshinamurthy Karra
我的错误。混淆了JAVA_OPTS和JAVA_TOOL_OPTIONS。它们的工作方式相同。JAVA_OPTS只是一种约定,并由脚本使用。但是JVM会读取JAVA_TOOL_OPTIONS。 - Dakshinamurthy Karra

0

如果攻击者可以物理接触您的计算机,那么他们就没有理由不能拥有该计算机 - 简单的rot13足以避免被轻易发现。

如果攻击者具有某些特权(例如运行top),但没有物理访问权限,则可以在专用用户帐户(例如web-server-user)下执行程序,该帐户几乎没有特权,但具有包含密码的文件的独占读取访问权限。然后,您可以使用该用户帐户启动应用程序,并传递文件路径。

这样做依赖于操作系统中的访问权限限制,这很可能比您自己实现的要好得多。


你所写的一切都是正确的。但它并没有回答问题,即我如何设置系统属性,以便某些任意代码(在我的情况下是 JBoss)可以读取它。 - Tom Anderson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接