Подтвердить что ты не робот

Как избежать хранения паролей в clear для tomcat server.xml Определение ресурсов DataSource?

Определение ресурса в 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"/>

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

4b9b3361

Ответ 1

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

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

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

Он будет выглядеть так:

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

И для пользовательского factory:

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;
    }
}

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

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Основной пример:

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... />

Ответ 5

После 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;
}

Ответ 6

Примечание:

Вы можете использовать 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);
  }
}

Ответ 7

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

Ответ 8

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

Ответ 9

Мы используем С# 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;
        });

)