Niebezpieczne liczby w PHP i Javie

Autorzy aplikacji pisanych w PHP i Javie mogą mieć problem - w interpreterach tych języków wykryto błąd, który może skutkować zawieszeniem aplikacji jeśli tylko na wejściu pojawi się odpowiednio sformatowana liczba zmiennoprzecinkowa.

Problem występuje zarówno w Javie (kompilator i runtime) jak i w PHP (interpreter). W obu przypadkach ma podobny charakter - aplikacja próbująca przetworzyć pojawiającą się na wejściu liczbę zmiennoprzecinkową wpada w nieskończoną pętlę i zawiesza się. W PHP dziurę można przetestować wykonując następujący fragment kodu:

<?php $d = 2.2250738585072011e-308; ?>

W przypadku aplikacji webowych błąd umożliwia przeprowadzenie trywialnego ataku, polegającego na podaniu w parametrach URL odpowiedniej wartości. Wystarczy, że w kodzie aplikacji znajdzie się fragment o funkcjonalności podobnej do tej:

<?php $number = (float) $_GET['number']; ?>

W przypadku Javy (przetestowane na wersjach do 1.6.0_20) zwis nastąpi już przy próbie skompilowania takiego kodu:

double d = 2.2250738585072012e-308;

System.out.println(“Value: " + d);

Runtime Javy powiesi się, jeśli zamiast próby przypisania tej wartości do zmiennej podamy ją na wejściu funkcji konwertującej (Double.parseDouble). Według komentatorów aplikacja wpada w nieskończoną pętlę ponieważ w obu językach z niedostateczną starannością zaimplementowano funkcję zaokrąglającą duże liczby zmiennoprzecinkowe przy konwersji typów.

Według Briana Sullivana z Adobe autorzy aplikacji webowych mogą stosunkowo łatwo ochronić się blokując na wejściu ciągi cyfr z fatalnego przedziału, na przykład tak (Java):

public static boolean containsMagicDoSNumber(String s) {

return s.replace(".", "").contains("2225073858507201");

}

Uwaga na obliczenia w PHP

Warto pamiętać, że opisany problem nie jest jedynym na jaki może się natknąć programista implementujący obliczenia na dużych liczbach. Zwłaszcza język PHP jest pełen podobnych pułapek, choć wynikających głównie z dość elastycznej konwersji typów niż ewidentnych błędów implementacyjnych. W szczególności typy danych w PHP mogą się zmieniać w trakcie wykonywania programu w zależności od przypisywanych im akurat wartości i nie zawsze zgodnie z intencjami autora. Na przykład jeśli utworzymy zmienną var jako liczbę całkowitą ($var = integer(10)) to po przypisaniu jej wartości zmiennoprzecinkowej ($var = '10.10') stanie się ona dokładnie zmienną zmiennoprzecinkową (co można sprawdzić za pomocą funkcji gettype()). Jeśli potem przypiszemy jej wartość tekstową, to stanie się zmienną typu String i tak dalej.

Dalej, jeśli na zmiennej typu tekstowego wyglądającej jak liczba zmiennoprzecinkowa ($x="2.222") wykonamy w PHP operację przypominającą arytmetykę (np. $x + 1) to PHP usłużnie zmieni nam typ tej zmiennej na Float. I to nie koniec problemów bo interpreter PHP operuje wewnętrznie na liczbach 32-bitowych, co łatwo może się skończyć "przekręceniem" licznika przy sumowaniu dużych liczb w pętlach ($suma = $suma + $cena).

Jako zabezpieczenie, poza uniwersalną receptą polegającą na starannym filtrowaniu danych wejściowych, można polecić korzystanie z wbudowanej w PHP biblioteki BCMath, która gwarantuje poprawność obliczeń arytmetycznych nawet na bardzo dużych liczbach.

W celu komercyjnej reprodukcji treści Computerworld należy zakupić licencję. Skontaktuj się z naszym partnerem, YGS Group, pod adresem [email protected]

TOP 200