Wdałem się ostatnio w nieco dziwną dyskusję odnośnie tego, czy każdy wyjątek występujący w aplikacji powinien być obsłużony. Dla uściślenia chodzi mi tutaj o aplikacje webowe i wyjątki występujące w wyniku manipulacji przez użytkownika przekazywanymi danymi.
Moim zdaniem obsługiwanie każdego wyjątku na siłę po to, by było "ładnie" nie jest dobrym pomysłem. Na przykłada dlatego, że do obsługi takiego wyjątku potrzeba jakiegoś kodu, a im więcej linii kodu, tym większe prawdopodobieństwo wystąpienia błędu. Jeszcze gorsze rzeczy mogą się dziać, jeśli kod obsługi wyjątku będzie usiłował "naprawić" sytuację wyjątkową i kontynuować wykonanie kodu (i to nawet wówczas, gdy nie ma to większego sensu). W takich przypadkach często jest lepiej po prostu przerwać wykonanie kodu, a nieobsłużony wyjątek robi to zwykle dość skutecznie.
Oczywiście można też patrzeć na sprawę w inny sposób. Co się stanie, jeśli wyjątek wystąpi w trakcie jakiejś operacji? Być może jakiś obiekt/proces/cokolwiek zostanie wówczas w osieroconym stanie i być może będzie można to w jakiś sposób wykorzystać. Pomijam tu również sytuację, gdy nieobsłużony wyjątek skutkuje wyświetleniem jakże przydatnych dla atakującego informacji (stacktrace).
Z ciekawości przejrzałem listę CWE na okoliczność wystąpienia słowa exception. Znalazłem na przykład takie elementy:
- CWE-248: Uncaught Exception
- CWE-396: Declaration of Catch for Generic Exception
- CWE-397: Declaration of Throws for Generic Exception
- CWE-460: Improper Cleanup on Thrown Exception
- CWE-600: Uncaught Exception in Servlet
Pierwszy przypadek, CWE-248: Uncaught Exception, zdaje się kończyć dowód. Faktycznie, nieobsłużony wyjątek może doprowadzić do Denial of Service. Jeśli jednak ten nieobsłużony wyjątek znajduje się w kodzie uruchamianym dla konkretnego użytkownika i skutek tego wyjątku dotyczy tylko tego użytkownika, który go wywołał, nie jest to jeszcze tragedia. Zwłaszcza jeśli sam jest sobie winny.
Tu, by jeszcze bardziej zagmatwać sprawę, dość typowa sytuacja. Często w przypadku środowisk zwracających uwagę na typy (ASP.NET, J2EE) próba manipulacji parametrami numerycznymi kończy się wyjątkiem wynikającym z braku możliwości konwersji przekazanej wartości na liczbę. Czy jest to walidacja danych wejściowych? Czy błąd tego typu powinien być obsłużony? Czy powinien być obsłużony, jeśli po stronie przeglądarki są stosowne ograniczenia dla wprowadzanych danych? Albo jeśli określony parametr nie jest przewidziany do modyfikacji dla użytkownika? Pozostawiam do przemyślenia...
Jeśli chodzi o CWE-600: Uncaught Exception in Servlet, to jest to dokładnie to, o czym pisałem:
When a Servlet throws an exception, the default error response the Servlet container sends back to the user typically includes debugging information. This information is of great value to an attacker. For example, a stack trace might show the attacker a malformed SQL query string, the type of database being used, and the version of the application container. This information enables the attacker to target known vulnerabilities in these components.
A to przy okazji się łapie na CWE-7: J2EE Misconfiguration: Missing Custom Error
Page (lub analogiczne dla innych środowisk).
CWE-460: Improper Cleanup on Thrown Exception mówi natomiast dokładnie o tym, o co mi chodzi:
The product does not clean up its state or incorrectly cleans up its state when an exception is thrown, leading to unexpected state or control flow.
oraz:
Often, when functions or loops become complicated, some level of cleanup in the beginning to the end is needed. Often, since exceptions can disturb the flow of the code, one can leave a code block in a bad state.
Doprowadzenie do takiego "nieokreślonego" stanu jest łatwiejsze, jeśli wyjątki są podnoszone i obsługiwane w bardzo "szeroki" sposób, czyli w sytuacjach, o których mówią CWE-396: Declaration of Catch for Generic Exception oraz CWE-396: Declaration of Throws for Generic Exception.
Jaki z tego morał? Moim zdaniem taki, że nie należy automatycznie uznawać, że każdy nieobsłużony wyjątek to błąd, który powinien być poprawiony. A jakie jest Wasze zdanie?
U mnie w pracy (jestem programista Javy, glownie webaplikacje) nieobsluzony wyjatek lecacy do klienta jest raczej bledem. Zaleca sie obsluzenie wyjatkow ktore sie moga zdazyc, chociazby w ten sposob ogolny - w obsludze tego wyjatku posprzatac aplikacje, zamknac co trzeba i zalogowac wyjatek. Nie trzeba po wyjatku tej aplikacji naprawiac (czasem sie nie da, czasem jak mowisz - nie ma sensu, bo nie zakladamy ze ktos moze manipulowac np polami hidden), ale zalogowac wyjatek i wyswietlic ladna strone bledu - trzeba. I powiem szczerze - zgadzam sie nawet z takim podejsciem, jest bardziej user-friendly, bardziej serwer - friendly (ze tak powiem nie zostaja smiecie) i mniej atakujacy - friendly (poniewaz ukryte sa stacktrace'y, informacje o uzywanym serwerze itd).
Skoro juz sie ujawnilem, to przy okazji podziekuje za tego bloga, jest fajnie pisany, profesjonalny i mi na przyklad pomogl w zakresie bezpieczenstwa pare razy
Czy mógłbyś trochę rozwinąć temat "posprzątania" w tych ogólnych procedurach obsługi wyjątków? Czy w jakiś sposób informacja o tych wyjątkach, które wystąpiły, jest dostępna i jest szansa jej monitorowania?
Ten drugi temat mnie interesuje z tego powodu, że występujący "dziwny" wyjątek może być dobrym sygnałem, że ktoś przy aplikacji w niepożądany sposób próbuje majstrować.
Najśmieszniejszym przypadkiem, z którym spotkałem się w obsłudze wyjątku (takim "catch all" na końcu) było SQLi w funkcji, która wstawiała informacje o wyjątku do logu w bazie
Sygnal ze ktos majstruje. Hmm... z tym ciezej. Ja co prawda nie robie ogromnych i bardzo, bardzo waznych aplikacji (jestem jeszcze mlodszym pracownikiem ^^), wiec nie implementuje jakichs systemow powiadamiania w konkretnych sytuacjach, ale wiem ze logi serwera sa calkiem czesto monitorowane. Nagly przyrost ich wielkosci jest dobrym sygnalem ze cos sie dzieje - warto zajrzec i przeanalizowac sprawe. Zwykle to wystarczalo zeby zareagowac na specyficzne sytuacje.
A czy tworząc funkcje logowania zwracacie uwagę, by nie logować, w przypadku wystąpienia wyjątku, istotnych danych? Zresztą nie tylko w przypadku wyjątku, tu ostatni przykład z Google Wallet (http://intrepidusgroup.com/insight/2012/04/google-wallet-last-four-digits-revealed-to-malware-vulnerability/).
Sam kilka takich przypadków też widziałem, choć z oczywistych przypadków tylko w niektórych projektach można prześledzić to, co jest pisane do logów.
Jeśli chodzi natomiast o dalszą obsługę takich błędów to były one dalej zgłaszane programistom a następnie w miarę możliwości czasowych naprawiane.
pozdrawiam wszystkich!