Comment éviter de stocker des mots de passe dans le clear pour le serveur de tomcat.définition d'une ressource xml d'une source de données?

La définition de la ressource dans de tomcat server.xml ressemble à quelque chose comme ça...

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

le mot de passe est clair. Comment faire pour éviter cela?

35
demandé sur Ahmed Ashour 2008-09-24 23:03:16

8 réponses

comme dit avant de chiffrer les mots de passe est tout simplement déplacer le problème ailleurs.

de toute façon, c'est assez simple. Il suffit d'écrire une classe avec des champs statiques pour votre clé secrète et ainsi de suite, et des méthodes statiques pour chiffrer, déchiffrer vos mots de passe. Chiffrez votre mot de passe dans le fichier de configuration de Tomcat (server.xml ou yourapp.xml...) à l'aide de cette classe.

et pour décrypter le mot de passe "à la volée" dans Tomcat, étendre les BasicDataSourceFactory et utiliser cette machine dans votre ressources.

Il va ressembler à ça:

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

Et pour l'personnalisée en usine:

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

J'espère que cela vous aidera.

38
répondu Jerome Delattre 2016-06-22 08:26:21

Tomcat a un mot de passe FAQ cela répond spécifiquement à votre question. En bref: Gardez le mot de passe dans le clair et correctement verrouiller votre serveur.

cette page présente également quelques suggestions sur la façon d'utiliser security-by-obscurity pour passer la liste de vérification d'un vérificateur.

9
répondu Ryan 2015-06-15 23:43:48

Tomcat doit savoir comment se connecter à la base de données, il a donc besoin d'accéder au mot de passe en clair. Si le mot de passe est crypté, Tomcat doit savoir comment le déchiffrer, donc vous ne déplacez le problème ailleurs.

Le vrai problème est: qui peut accéder à server.xml sauf pour Tomcat? Une solution est de donner accès en lecture à server.xml seulement à l'utilisateur root, ce qui nécessite que Tomcat soit démarré avec les privilèges root: si un utilisateur malveillant gagne les privilèges root sur le système, perdre un le mot de passe de la base de données est probablement une préoccupation mineure.

Sinon, vous tapez le mot de passe manuellement à chaque démarrage, mais c'est rarement une option viable.

5
répondu gameame 2016-09-28 13:22:14

@Ryan mentionné, veuillez lire Tomcat Tomcat Password FAQ avant de mettre en oeuvre cette solution. Vous ajoutez seulement l'obscurité et non la sécurité.

la réponse de@Jerome Delattre fonctionnera pour les sources de données JDBC simples, mais pas pour les plus compliquées qui se connectent dans le cadre de la construction datasource (par exemple oracle.jdbc.xa.client.OracleXADataSource).

il s'agit d'une approche alternative qui modifie le mot de passe avant d'appeler l'usine existante. Vous trouverez ci-dessous un exemple d'usine pour une source de données de base et un autre pour une source de données XA compatible Atomikos JTA.

Exemple De Base:

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 Exemple:

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

mise à jour de la valeur du mot de passe dans la référence:

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

une Fois l' .le fichier jar contenant ces classes se trouve dans classpath de Tomcat.vous pouvez mettre à jour votre serveur.xml à utiliser.

<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
répondu JustinKSU 2016-09-06 15:57:34

après 4 heures de travail, chercher des questions et des réponses j'ai trouvé la solution. Basé sur la réponse de @Jerome Delattre voici le code complet (avec la configuration des sources de données JNDI).

Contexte.xml

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

Source De Données Personnalisée En Usine:

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

source des données bean:

@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;
}
2
répondu HolloW 2018-01-10 03:30:58

Remarque:

Vous pouvez utiliser WinDPAPI pour chiffrer et déchiffrer les données

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
répondu Gawri Edussuriya 2018-05-04 08:26:13

Tout ce qui précède étant dit, si vous voulez toujours éviter les mots de passe en clair, vous pouvez utiliser un algorithme de hachage tel que SHA-256 ou (de préférence) SHA-512. Lorsqu'un mot de passe est créé, obtenez la valeur hachée et stockez-la plutôt que le mot de passe. Quand un utilisateur se connecte, hachez le mot de passe et voyez-le correspondre au mot de passe hash stocké. Les algorithmes de hachage prennent une chaîne de caractères (ou un nombre) à partir d'une petite chaîne (ou un nombre) l'espace dans un beaucoup plus grand d'une manière qui est coûteuse à inverser.

-2
répondu stuartw 2016-09-03 00:23:18

Nous utilisons de 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;
        });

)

-8
répondu Brad8118 2008-09-24 19:19:46