sobota, 10 października 2009

Defensywne kopiowanie

W dzisiejszych czasach gdy nasze zintegrowane środowiska programistyczne potrafią coraz więcej, bardzo ułatwiają programowanie niemal chronią nas przed popełnieniem błędów przy tworzeniu kodu zapominamy o bezpieczeństwie tworzonego przez nas kodu. Chciałbym napisać dzisiaj kilka słów o defensywnym kopiowaniu. Czym jest defensywne kopiowanie? Najkrócej można powiedzieć iż zabezpieczeniem przez modyfikacją niezmienników klasy. Przykład:
  
//Naruszona "niezmienność" klasy reprezentującej odcinek czasu
public final class Period {
private final Date start;
private final Date end;
/**
* @param start - początek odcinka czasu
* @param end - koniec odcinka czasu, nie może być wcześniejszy niż start
* @throws IllegalArgumentException , jeżeli start jest większy niż end
* @throws NullPointerException jeżeli start lub end ma wartość null
*/
public Period(Date start, Date end) {
if (start.compareTo(end) > 0)
throw new IllegalArgumentException(
start + " after " + end);
this.start = start;
this.end = end;
}
public Date start() {
return start;
}
public Date end() {
return end;
}
//... Pozostała część pominięta
}
// przykład pochodzi z książki "Java Efektywne programowanie" - Joshua Bloch

Kod na pierwszy rzut oka wygląda całkiem poprawnie, nic bardziej mylnego. Wyobraźmy sobie sytuację tworzymy obiekt klasy Period przekazując mu w konstruktorze dwa obiekty klasy Date. A następnie modyfikujemy wartość jednego z przekazanych obiektów. Pomimo iż obiekty start oraz end są zadeklarowane jako private final, możemy modyfikować je. W jaki sposób? Oto przykład:

  

// Atak na wewnętrzene składowe obiektu Period
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78); // Modyfikacja wewnętrzenej składowej p!
// przykład pochodzi z książki "Java Efektywne programowanie" - Joshua Bloch

Czy tego typu atak może się zdarzyć? Oczywiście, że tak, wystarczy wyobrazić sobie sytuację iż koś chce złamać zabezpieczenia twojego systemu. Aby zabezpieczyć kod przez tego typu atakiem należy zastosować defensywne kopiowanie. Przykład poprawionego kodu z zaimplementowanym defensywnym kopiowaniem:

  
// Poprawione kod - defensywne kopiowanie przekazanych w parametrze konstruktora obiektów
public Period(Date start, Date end) {
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());

if (this.start.compareTo(this.end) > 0)
throw new IllegalArgumentException(
start + " after " + end);
}
//defensywne kopiowanie zwracanych obiektów
public Date start() {
return new Date(start.getTime());
}
public Date end() {
return new Date(end.getTime());
}
// przykład pochodzi z książki "Java Efektywne programowanie" - Joshua Bloch


Jeżeli klasa posiada modyfikowalne przez metody dostępu lub przekazywane w konstruktorze komponenty musi ona defensywnie kopiować te komponenty. Czasami koszt defensywnego kopiowania obiektów może być zbyt duży. Jeżeli mamy pewność iż klienci naszego API nie będą niewłaściwie modyfikować komponentów można zrezygnować z defensywnego kopiowania. Jednak w takim wypadku należy dokładnie udokumentować odpowiedzialność klienta za to, że nie może on modyfikować komponentów obiektu.

Brak komentarzy:

Prześlij komentarz