在spring xml文件中隐藏数据源密码

发布于 2024-10-02 08:38:43 字数 165 浏览 4 评论 0原文

有没有办法在 xml spring 配置文件中隐藏/加密密码? 我读到,使用 DataSource 的“自定义”子类是可能的,但解决方案将密钥作为纯文本保存在同一配置文件中......所以有点无用。

有没有办法使用 KeyStore 来实现这一点? 例如,从密钥库读取值。

谢谢大家。

there is a way to hide/encrypt password in xml spring config file?
I read that is possible with a "custom" subclass of DataSource, but the solutions keep key in same config file as plain text...so is a bit useless.

There is a way to use KeyStore for this?
For example read the value from a keystore.

Thanks all.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

花伊自在美 2024-10-09 08:38:43

隐藏密码的目的是什么?我建议您在容器(Tomcat、JBoss 或您使用的任何容器)中配置数据源,并使用 jndi 将数据源注入到您的应用程序中:

<jee:jndi-lookup id="thedatasource"
                     jndi-name="java:comp/env/jdbc/thedatasource"
                     lookup-on-startup="false"
                     expected-type="javax.sql.DataSource"/>

这样您就不必在应用程序中公开密码,而只需在 servlet 容器中公开密码。

What is the purpose of hiding the password? I suggest you configure the datasource in the container (Tomcat, JBoss or whatever you use) and inject the datasource into your application using jndi:

<jee:jndi-lookup id="thedatasource"
                     jndi-name="java:comp/env/jdbc/thedatasource"
                     lookup-on-startup="false"
                     expected-type="javax.sql.DataSource"/>

This way you have not to expose and password in your application but only in the servlet container.

白况 2024-10-09 08:38:43

是的,你可以这样做。您必须围绕数据源类创建一个包装器 bean。这是我之前的做法的一个例子。希望这有帮助!

<beans>
    <bean id="someDao" class="com.dao.SomeDAOImpl">
         <property name="datasource">
            <ref local="secureDataSource"/>
        </property>
    </bean>
    <bean id="secureDataSource" class="com.ds.SecureDataSource">
        <property name="driverClassName">
            <value><your driver></value>
        </property>
        <property name="url">
            <value><your url></value>
        </property>  
        <property name="username">
            <value><your user id></value>
        </property>
        <property name="password">
            <value><encrypted_pwd></value>
        </property> 
    </bean> 
</beans>

然后在 SecureDataSource 类中,您将需要解密密码。

import java.sql.Connection;
import java.sql.SQLException;


public class SecureDataSource extends DriverManagerDataSource{

    private String url;
    private String username;
    private String password;
    /**
     * @param url the url to set
     */
    public void setUrl(String url) {
        this.url = url;
    }

    /**
     * @param username the username to set
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }

    protected Connection getConnectionFromDriverManager() throws SQLException {
        String decryptedPassword = null;
        //decrypt the password here
        return getConnectionFromDriverManager(url,username,decryptedPassword);
    }
}

Yes, you can do that. You will have to create a wrapper bean around the data source class. Here is an example of how I have done it before. Hope this helps!

<beans>
    <bean id="someDao" class="com.dao.SomeDAOImpl">
         <property name="datasource">
            <ref local="secureDataSource"/>
        </property>
    </bean>
    <bean id="secureDataSource" class="com.ds.SecureDataSource">
        <property name="driverClassName">
            <value><your driver></value>
        </property>
        <property name="url">
            <value><your url></value>
        </property>  
        <property name="username">
            <value><your user id></value>
        </property>
        <property name="password">
            <value><encrypted_pwd></value>
        </property> 
    </bean> 
</beans>

Then inside the SecureDataSource class you will need to decrypt the password.

import java.sql.Connection;
import java.sql.SQLException;


public class SecureDataSource extends DriverManagerDataSource{

    private String url;
    private String username;
    private String password;
    /**
     * @param url the url to set
     */
    public void setUrl(String url) {
        this.url = url;
    }

    /**
     * @param username the username to set
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }

    protected Connection getConnectionFromDriverManager() throws SQLException {
        String decryptedPassword = null;
        //decrypt the password here
        return getConnectionFromDriverManager(url,username,decryptedPassword);
    }
}
亢潮 2024-10-09 08:38:43

已经给出了很好的选择,另一个明显的答案是使用 PropertyPlaceholderConfigurer

<context:property-placeholder
    system-properties-mode="OVERRIDE" 
    location="classpath:database.properties" />

<bean id="dataSource" class="com.whatever.datasource.you.Use">
    <property name="password" value="${database.password}" />
</bean> 

现在您可以将密码作为属性保存在属性文件中(如果您不想将其保留在 SCM 中,则可以在部署期间创建该文件) )或作为系统属性(希望其他开发人员也无法实现)。

澄清在部署期间创建有点含糊。我想您将必须编写一个安装程序,在最终用户的计算机上动态生成属性文件,可能还需要加上注册/登录机制。


编辑:我仍然不知道你向谁隐藏了这些信息。两种理论:

a) 有权访问您的源代码的人
b) 你的客户

如果是a),那就走我的路。其他开发人员只需使用调试器启动您的应用程序(突然他就在数据源对象内部并看到密码),就可以轻松地破坏所有其他方法。

如果是b),那么基本上你就没有机会了。客户有很多可能性来获取您的密码:调试器、代理、字节码操作、加载时编织等。即使他不执行任何操作,他也只需附加一个端口嗅探器即可以明文方式获取密码文本。唯一安全的做法是为每个客户提供一个用户名/密码(切勿在客户的计算机上存储全局密码)。

Good options have been given, another obvious answer is to use the PropertyPlaceholderConfigurer:

<context:property-placeholder
    system-properties-mode="OVERRIDE" 
    location="classpath:database.properties" />

<bean id="dataSource" class="com.whatever.datasource.you.Use">
    <property name="password" value="${database.password}" />
</bean> 

Now you can keep your password either as a property in a properties file (which you might create during deployment if you don't want to have it in the SCM) or as a System Property (which will hopefully also be beyond reach of other developers).

Clarification: create during deployment is somewhat vague. I guess you will have to write an installer that generates the properties file dynamically on the end user's machine, probably coupled with a sign up / log in mechanism.


EDIT: I still haven't figured out who you are hiding the information from. Two theories:

a) People who have access to your source code
b) Your customers

If it's a), then go my way. All other ways can easily be breached by the other developer just starting your application with a debugger (and suddenly he's inside the datasource object and sees the password).

If it's b), then you have no chance, basically. The customer has tons of possibilities to get at your password: debuggers, agents, bytecode manipulation, loadtime weaving etc. Even if he doesn't do any of that, he will just have to attach a port sniffer to get at the password in clear text. The only safe thing to do is have a username / password per customer (never store a global password at your customer's machine).

2024-10-09 08:38:43

我最近也有同样的问题。我想将密码的哈希版本存储在 .properties 文件中。
感谢前面的选项,我成功了:我扩展了 DelegatingDataSource 并重写了 getConnection([...]) 方法。

public class UnhashingDataSource extends DelegatingDataSource {

    private static final Logger LOGGER = Logger.getLogger(UnhashingDataSource.class);
    private static final int HEX_RADIX = 16;
    private static final String DB_PASS = "a_sample_password";

    @Override
    public Connection getConnection() throws SQLException {
        DriverManagerDataSource dataSource = (DriverManagerDataSource) getTargetDataSource();
        return getConnection(dataSource.getUsername(), dataSource.getPassword());
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        try {
            DataSource datasource = getTargetDataSource();
            if (datasource == null) {
                throw new RuntimeException("targetDataSource is null");
            }
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.reset();
            md.update(DB_PASS.getBytes());
            if (password.equals(getHexString(md.digest()))) {
                return datasource.getConnection(username, DB_PASS);
            } else {
                throw new RuntimeException("Unable to connect to DB");
            }
        } catch (NoSuchAlgorithmException e) {
            LOGGER.error("Unknown algorithm");
        }
        return null;
    }

    private String getHexString(final byte[] messageDigest) {
        BigInteger bigInt = new BigInteger(1, messageDigest);
        return bigInt.toString(HEX_RADIX);
    }
}

然后,这是我在 applicationContext.xml 中使用它的方式:

# Using the unhashing datasource
<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="unhashingDataSource" />
    # ...
</bean>
<bean id="hashedDataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${datasource.driverClassName}" />
    <property name="url" value="${datasource.url}" />
    <property name="username" value="${datasource.username}" />
    <property name="password" value="${datasource.hash}" />
</bean>
<bean id="unhashingDataSource"
    class="my.package.UnhashingDataSource">
    <property name="targetDataSource" ref="hashedDataSource" />
</bean>

其中 datasource.hash 是一个属性(来自 .properties 文件),存储如下:

datasource.hash = 2e54b0667ef542e3398c55a08a4e04e69b9769e8

纯密码仍然是在字节码中,但不再直接在 .properties 文件中。

I had the same question recently. I wanted to store a hashed version of the password in a .properties file.
I did the trick thanks to the previous options: I extended the DelegatingDataSource and overrided the getConnection([...]) methods.

public class UnhashingDataSource extends DelegatingDataSource {

    private static final Logger LOGGER = Logger.getLogger(UnhashingDataSource.class);
    private static final int HEX_RADIX = 16;
    private static final String DB_PASS = "a_sample_password";

    @Override
    public Connection getConnection() throws SQLException {
        DriverManagerDataSource dataSource = (DriverManagerDataSource) getTargetDataSource();
        return getConnection(dataSource.getUsername(), dataSource.getPassword());
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        try {
            DataSource datasource = getTargetDataSource();
            if (datasource == null) {
                throw new RuntimeException("targetDataSource is null");
            }
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.reset();
            md.update(DB_PASS.getBytes());
            if (password.equals(getHexString(md.digest()))) {
                return datasource.getConnection(username, DB_PASS);
            } else {
                throw new RuntimeException("Unable to connect to DB");
            }
        } catch (NoSuchAlgorithmException e) {
            LOGGER.error("Unknown algorithm");
        }
        return null;
    }

    private String getHexString(final byte[] messageDigest) {
        BigInteger bigInt = new BigInteger(1, messageDigest);
        return bigInt.toString(HEX_RADIX);
    }
}

Then, here is how I used it in my applicationContext.xml:

# Using the unhashing datasource
<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="unhashingDataSource" />
    # ...
</bean>
<bean id="hashedDataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${datasource.driverClassName}" />
    <property name="url" value="${datasource.url}" />
    <property name="username" value="${datasource.username}" />
    <property name="password" value="${datasource.hash}" />
</bean>
<bean id="unhashingDataSource"
    class="my.package.UnhashingDataSource">
    <property name="targetDataSource" ref="hashedDataSource" />
</bean>

Where datasource.hash is a property (from a .properties file) stored like:

datasource.hash = 2e54b0667ef542e3398c55a08a4e04e69b9769e8

The plain password is still in bytecode but not directly in a .properties file anymore.

神魇的王 2024-10-09 08:38:43

感谢您的所有帖子和询问。

希望访问者通过阅读本页能够清楚密码加密的技术方法。我想在这里补充一件重要的事情,如果您正在处理生产,那么肯定会建议您使用任何“安全哈希算法”,例如带盐的 SHA-256。您可以考虑使用盐作为行业标准的安全哈希算法。

Thanks for all your post and queries.

Hope for visitors its clear the technical way to encrypt password by reading this page. One important thing I would like to add here, if you are dealing with production then definitely will suggest you to use any "Secure Hash Algorithm" like SHA-256 with salt. You can consider secure hash algorithm using salt as industry standard.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文