Java efficace dit:
// Trou de sécurité potentiel!
statique public final Thing [] VALUES = {...};
Quelqu'un peut-il me dire quel est le trou de sécurité?
La déclaration de champs static final public
est généralement la marque d'une constante de classe. C'est parfait pour les types primitifs (ints, doubles, etc.) et les classes immuables, comme les chaînes et Java.awt.Color
. Avec les tableaux, le problème est que, même si la référence du tableau est constante, les éléments du tableau peuvent toujours être modifiés et, comme il s'agit d'un champ, les modifications sont non surveillées, non contrôlées et généralement non souhaitées.
Pour lutter contre cela, la visibilité du champ de tableau peut être limitée à private ou à package private. Vous disposez ainsi d'un corps de code plus petit à prendre en compte lorsque vous recherchez une modification suspecte. Sinon, et souvent mieux, vous devez supprimer le tableau ensemble et utiliser une «liste» ou un autre type de collection approprié. En utilisant une collection, vous contrôlez si les mises à jour sont autorisées, car toutes les mises à jour passent par des méthodes. Vous pouvez empêcher les mises à jour en encapsulant votre collection à l’aide de Collections.unmodifiableList()
. Attention, même si la collection est immuable, vous devez également vous assurer que les types qui y sont stockés le sont également, sinon le risque de modifications non sollicitées d'une constante supposée réapparaîtra.
Pour comprendre pourquoi il s'agit d'une faille de sécurité potentielle et non simplement d'une mauvaise encapsulation, prenons l'exemple suivant:
public class SafeSites {
// a trusted class with permission to create network connections
public static final String[] ALLOWED_URLS = new String[] {
"http://Amazon.com", "http://cnn.com"};
// this method allows untrusted code to connect to allowed sites (only)
public static void doRequest(String url) {
for (String allowed : ALLOWED_URLS) {
if (url.equals(allowed)) {
// send a request ...
}
}
}
}
public class Untrusted {
// An untrusted class that is executed in a security sandbox.
public void naughtyBoy() {
SafeSites.ALLOWED_URLS[0] = "http://myporn.com";
SafeSites.doRequest("http://myporn.com");
}
}
Comme vous pouvez le constater, l'utilisation erronée d'un tableau final signifie qu'un code non fiable peut détourner la restriction que le code/sandbox sécurisé tente d'imposer. Dans ce cas, il s'agit clairement d'un problème de sécurité.
Si votre code ne fait pas partie d'une application critique pour la sécurité, vous pouvez ignorer ce problème. Mais OMI c'est une mauvaise idée. À un moment donné dans le futur, vous (ou quelqu'un d'autre) pourriez réutiliser votre code dans un contexte où la sécurité est une préoccupation. Quoi qu'il en soit, this est la raison pour laquelle l'auteur appelle les baies finales publiques un problème de sécurité.
Amber a dit ceci dans un commentaire:
Pas plus qu'un trou de sécurité que privé ne serait, si vous pouviez lire le code source et/ou le bytecode de toute façon ...
Ce n'est pas vrai.
Le fait qu'un "type malhonnête" puisse utiliser le code source/les bytecodes pour déterminer qu'une private
existe et fait référence à un tableau est pas suffisant pour casser la sécurité. Le méchant aussi doit injecter du code dans une machine virtuelle disposant des autorisations nécessaires pour utiliser la réflexion. Cette permission n’est pas disponible pour untrusted code s’exécutant dans un sandbox de sécurité (correctement implémenté).
Notez qu'un tableau de longueur non nulle est toujours modifiable, donc il est erroné qu'une classe ait un champ de tableau final statique public ou un accesseur qui renvoie un tel champ. Si une classe a un tel champ ou accesseur, les clients pourront modifier le contenu du tableau.
- Effective Java , 2e éd. (page 70)
Une classe externe peut modifier le contenu du tableau, ce qui n'est probablement pas ce que vous voulez que les utilisateurs de votre classe fassent (vous voulez qu'ils le fassent via une méthode). On dirait que l'auteur a voulu dire qu'il violait l'encapsulation et non la sécurité.
Je suppose que quelqu'un déclarant cette ligne pourrait penser que les autres classes ne peuvent pas modifier le contenu du tableau car il est marqué comme final, mais ce n'est pas vrai, final ne vous empêche pas de réaffecter l'attribut.
Dans cette déclaration, un client peut modifier Thing [0], Thing [1], etc. (c'est-à-dire des éléments du tableau).
Pensez que cela signifie simplement la chose publique vs privée. Il est censé être une bonne pratique d'avoir des variables locales déclarées privées, puis d'utiliser des méthodes get et set plutôt que d'y accéder directement. Les rend un peu plus difficile à jouer en dehors de votre programme. Pour autant que je sache.
J'ajouterais également ce que Joshua Bloch avait proposé dans Effective Java 3rd edition. Bien entendu, nous pouvons facilement modifier la valeur du tableau s'il est déclaré comme suit:
public static final String[] VALUES = { "a", "b" };
a.VALUES[0] = "changed value on index 0";
System.out.println(String.format("Result: %s", a.VALUES[0]));
et nous obtenons Result: changed value on index 0
Joshua Bloch a proposé de renvoyer une copie du tableau:
private static final String[] VALUES = { "a", "b" };
public static final String[] values()
{
return VALUES.clone();
}
alors maintenant, quand nous essayons:
a.values()[0] = "changed value on index 0";
System.out.println(String.format("Result: %s", a.values()[0]));
nous obtenons Result: a
et c'est ce que nous voulions réaliser - les VALUES
sont immuables.
Il n'y a également rien de mal à déclarer public static final
une valeur de primitive, une chaîne ou tout autre objet immuable comme public static final int ERROR_CODE = 59;
En effet, le mot-clé final assure uniquement la valeur de référence (par exemple, l’emplacement mémoire) mais pas le contenu de celle-ci.