Как избежать хранения паролей в открытом виде для tomcat server.xml Определение ресурса DataSource? - PullRequest
36 голосов
/ 24 сентября 2008

Определение ресурса в Tomcat server.xml выглядит примерно так ...

<Resource
    name="jdbc/tox"
    scope="Shareable"
    type="javax.sql.DataSource"
    url="jdbc:oracle:thin:@yourDBserver.yourCompany.com:1521:yourDBsid"
    driverClassName="oracle.jdbc.pool.OracleDataSource"
    username="tox"
    password="toxbaby"
    maxIdle="3"
    maxActive="10"
    removeAbandoned="true"
    removeAbandonedTimeout="60"
    testOnBorrow="true"
    validationQuery="select * from dual"
    logAbandoned="true"
    debug="99"/>

Пароль в открытом виде. Как этого избежать?

Ответы [ 8 ]

39 голосов
/ 10 декабря 2008

Как уже говорилось, шифрование паролей просто перемещает проблему куда-то еще.

Во всяком случае, все довольно просто. Просто напишите класс со статическими полями для вашего секретного ключа и так далее, а также статические методы для шифрования, расшифровки ваших паролей. Зашифруйте свой пароль в файле конфигурации Tomcat (server.xml или yourapp.xml ...), используя этот класс.

И для расшифровки пароля "на лету" в Tomcat, расширьте DBCP BasicDataSourceFactory и используйте эту фабрику в своем ресурсе.

Это будет выглядеть так:

    <Resource
        name="jdbc/myDataSource"
        auth="Container"
        type="javax.sql.DataSource"
        username="user"
        password="encryptedpassword"
        driverClassName="driverClass"
        factory="mypackage.MyCustomBasicDataSourceFactory"
        url="jdbc:blabla://..."/>

А для фабрики на заказ:

package mypackage;

....

public class MyCustomBasicDataSourceFactory extends org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory {

@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception {
    Object o = super.getObjectInstance(obj, name, nameCtx, environment);
    if (o != null) {
        BasicDataSource ds = (BasicDataSource) o;
        if (ds.getPassword() != null && ds.getPassword().length() > 0) {
            String pwd = MyPasswordUtilClass.unscramblePassword(ds.getPassword());
            ds.setPassword(pwd);
        }
        return ds;
    } else {
        return null;
    }
}

Надеюсь, это поможет.

9 голосов
/ 16 июня 2015

Tomcat имеет FAQ по паролю , который конкретно отвечает на ваш вопрос. Короче говоря: храните пароль в открытом и надежно заблокированном сервере.

На этой странице также предлагаются некоторые предложения о том, как безопасность по незаметности может использоваться для прохождения контрольного списка аудитора.

3 голосов
/ 02 сентября 2016

Как упомянул @Ryan, пожалуйста, прочитайте Tomcat Password FAQ перед внедрением этого решения. Вы только добавляете неизвестность, а не безопасность.

@ Ответ Джерома Делаттре будет работать для простых источников данных JDBC, но не для более сложных, которые связаны как часть конструкции источника данных (например, oracle.jdbc.xa.client.OracleXADataSource).

Это альтернативный подход, который изменяет пароль перед вызовом существующей фабрики. Ниже приведен пример фабрики для базового источника данных и фабрики для совместимого с Attaikos JTA источника данных XA.

Базовый пример:

public class MyEncryptedPasswordFactory extends BasicDataSourceFactory {

    @Override
    public Object getObjectInstance(Object obj, Name name, Context context, Hashtable<?, ?> environment)
            throws Exception {
        if (obj instanceof Reference) {
            Reference ref = (Reference) obj;
            DecryptPasswordUtil.replacePasswordWithDecrypted(ref, "password");
            return super.getObjectInstance(obj, name, context, environment);
        } else {
            throw new IllegalArgumentException(
                    "Expecting javax.naming.Reference as object type not " + obj.getClass().getName());
        }
    }
}

Atomikos Пример:

public class MyEncryptedAtomikosPasswordFactory extends EnhancedTomcatAtomikosBeanFactory {
    @Override
    public Object getObjectInstance(Object obj, Name name, Context context, Hashtable<?, ?> environment)
            throws NamingException {
        if (obj instanceof Reference) {
            Reference ref = (Reference) obj;
            DecryptPasswordUtil.replacePasswordWithDecrypted(ref, "xaProperties.password");
            return super.getObjectInstance(obj, name, context, environment);
        } else {
            throw new IllegalArgumentException(
                    "Expecting javax.naming.Reference as object type not " + obj.getClass().getName());
        }
    }
}

Обновление значения пароля в справочнике:

public class DecryptPasswordUtil {

    public static void replacePasswordWithDecrypted(Reference reference, String passwordKey) {
        if(reference == null) {
            throw new IllegalArgumentException("Reference object must not be null");
        }

        // Search for password addr and replace with decrypted
        for (int i = 0; i < reference.size(); i++) {
            RefAddr addr = reference.get(i);
            if (passwordKey.equals(addr.getType())) {
                if (addr.getContent() == null) {
                    throw new IllegalArgumentException("Password must not be null for key " + passwordKey);
                }
                String decrypted = yourDecryptionMethod(addr.getContent().toString());
                reference.remove(i);
                reference.add(i, new StringRefAddr(passwordKey, decrypted));
                break;
            }
        }
    }
}

Как только файл .jar, содержащий эти классы, находится в пути к классам Tomcat, вы можете обновить свой server.xml, чтобы использовать их.

<Resource factory="com.mycompany.MyEncryptedPasswordFactory" username="user" password="encryptedPassword" ...other options... />

<Resource factory="com.mycompany.MyEncryptedAtomikosPasswordFactory" type="com.atomikos.jdbc.AtomikosDataSourceBean" xaProperties.user="user" xaProperties.password="encryptedPassword" ...other options... />
3 голосов
/ 26 сентября 2008

Tomcat должен знать, как подключиться к базе данных, поэтому ему нужен доступ к простому текстовому паролю. Если пароль зашифрован, Tomcat должен знать, как его расшифровать, так что вы переносите проблему только в другое место.

Реальная проблема: кто может получить доступ к server.xml, кроме Tomcat? Решение состоит в том, чтобы предоставить доступ на чтение server.xml только пользователю root, требуя, чтобы Tomcat был запущен с привилегиями root: если злонамеренный пользователь получает привилегии root в системе, потеря пароля к базе данных, вероятно, является незначительной проблемой.

В противном случае вы должны вводить пароль вручную при каждом запуске, но это редко приемлемый вариант.

2 голосов
/ 04 мая 2018

Примечание:

Вы можете использовать WinDPAPI для шифрования и дешифрования данных

public class MyDataSourceFactory extends DataSourceFactory{

private static WinDPAPI winDPAPI;

protected static final String DATA_SOURCE_FACTORY_PROP_PASSWORD = "password";

@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception{

    Reference ref = (Reference) obj;
    for (int i = 0; i < ref.size(); i++) {
        RefAddr ra = ref.get(i);
        if (ra.getType().equals(DATA_SOURCE_FACTORY_PROP_PASSWORD)) {

            if (ra.getContent() != null && ra.getContent().toString().length() > 0) {
                String pwd = getUnprotectedData(ra.getContent().toString());
                ref.remove(i);
                ref.add(i, new StringRefAddr(DATA_SOURCE_FACTORY_PROP_PASSWORD, pwd));
            }

            break;
        }
    }

    return super.getObjectInstance(obj, name, nameCtx, environment);
  }
}
2 голосов
/ 10 января 2018

После 4 часов работы, поиска вопросов и ответов я получил решение. На основе ответа @Jerome Delattre приведен полный код (с конфигурацией источника данных JNDI).

context.xml

<Resource
    name="jdbc/myDataSource"
    auth="Container"
    type="javax.sql.DataSource"
    username="user"
    password="encryptedpassword"
    driverClassName="driverClass"
    factory="mypackage.MyCustomBasicDataSourceFactory"
    url="jdbc:blabla://..."/>

Фабрика пользовательских источников данных:

package mypackage;

public class MyCustomBasicDataSourceFactory extends org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory {
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception {
        Object o = super.getObjectInstance(obj, name, nameCtx, environment);
        if (o != null) {
            BasicDataSource ds = (BasicDataSource) o;
            if (ds.getPassword() != null && ds.getPassword().length() > 0) {
                String pwd = MyPasswordUtilClass.unscramblePassword(ds.getPassword());
                ds.setPassword(pwd);
            }
            return ds;
        } else {
            return null;
        }
    }
}

Боб источника данных:

@Bean
public DataSource dataSource() {
    DataSource ds = null;
    JndiTemplate jndi = new JndiTemplate();
    try {
        ds = jndi.lookup("java:comp/env/jdbc/myDataSource", DataSource.class);
    } catch (NamingException e) {
        log.error("NamingException for java:comp/env/jdbc/myDataSource", e);
    }
    return ds;
}
0 голосов
/ 03 сентября 2016

С учетом всего вышесказанного, если вы все еще хотите избежать использования простых текстовых паролей, вы можете использовать алгоритм хеширования, такой как SHA-256 или (предпочтительно) SHA-512. Когда пароль создан, получите хешированное значение и сохраните его, а не пароль. Когда пользователь входит в систему, хэширует пароль и видит, что он соответствует сохраненному хешированному паролю. Алгоритмы хеширования переводят символьную строку (или число) из небольшого строкового (или числового) пространства в намного больший, что обходится дорого.

0 голосов
/ 24 сентября 2008

Мы используем C # SHA1CryptoServiceProvider

print(SHA1CryptoServiceProvider sHA1Hasher = new SHA1CryptoServiceProvider();
        ASCIIEncoding enc = new ASCIIEncoding();

        byte[] arrbytHashValue = sHA1Hasher.ComputeHash(enc.GetBytes(clearTextPW));
        string HashData = System.BitConverter.ToString(arrbytHashValue);
        HashData = HashData.Replace("-", "");
        if (HashData == databaseHashedPassWO)
        {
            return true;
        }
        else
        {
            return false;
        });

)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...