Inicjalizacja

Błędy inicjalizacji są podstępne i trudne do znalezienia. Najbardziej podstępne programy zapisują informacje inicjujące na dysku i zawodzą tylko przy pierwszym użyciu – to znaczy przed utworzeniem pliku inicjującego. Przy drugim uruchomieniu programu przez danego użytkownika nie ma dalszych błędów inicjalizacji. W związku z tym błędy pojawiają się tylko dla pracowników i klientów, gdy aktywują nową kopię wadliwego programu. Inne programy z błędami inicjalizacji mogą pokazywać dziwne obliczenia lub inne błędy przy pierwszym użyciu lub inicjalizacji; ponieważ nie przechowują swoich wartości inicjalizacji, te błędy inicjalizacji będą pojawiać się ponownie za każdym razem, gdy program jest używany.

Wewnętrzne błędy projektowe lub implementacyjne

Ogólna definicja błędu oprogramowania to niezgodność między programem a jego specyfikacją; bardziej konkretną definicją jest niepowodzenie programu w wykonywaniu tego, czego rozsądnie oczekuje użytkownik końcowy. Istnieje wiele rodzajów błędów oprogramowania. Niektóre z najważniejszych to:

* Inicjalizacja

* Przepływ logiczny

* Obliczenia

* Naruszenia warunków brzegowych

* Przekazywanie parametrów

* Warunki wyścigu

* Stan obciążenia

* Wyczerpanie zasobów

* Konflikt zasobów, adresu lub programu z systemem operacyjnym lub aplikacjami

* Względy dotyczące zgodności z przepisami

* Inne błędy

str. 38.11

RODZAJE BŁĘDÓW OPROGRAMOWANIA

Nowi programiści powinni przejrzeć zakres głównych przyczyn błędów oprogramowania. Taki przegląd jest szczególnie przydatny dla studentów, którzy ukończyli szkolenie, które nie obejmowało omówienia metodologii systematycznego zapewniania jakości. Kolejne sekcje mogą służyć jako podstawa do tworzenia efektywnych zestawów danych testowych i procedur testowych dla testów jednostkowych nowych lub zmodyfikowanych procedur.

Języki

Do chwili obecnej żadne popularne języki komputerowe nie mają wbudowanych funkcji zabezpieczeń. Java zawiera postanowienia dotyczące ograniczania dostępu do zasobów poza „piaskownicą” zarezerwowanych dla procesu, jak opisano w książkach Felten i McGraw. Niemniej jednak Java jest nadal jednym z języków będących celem ataku wielu implementacji szkodliwego oprogramowania. PASCAL wykorzystuje silne typowanie i wymaga pełnego zdefiniowania struktur danych, co utrudnia dostęp do danych i kodu poza maszyną wirtualną zdefiniowaną dla danego procesu. W przeciwieństwie do tego, C i C++ pozwalają programistom na dostęp do dowolnego regionu pamięci w dowolnym momencie, gdy system operacyjny na to pozwala. Języki komputerowe pozwalają programistom pisać kod tak dobrze, jak potrafią. Języki z silną typizacją mogą stwarzać większe ograniczenia programistom, ale zasadniczym wymogiem jest to, aby programiści nadal myśleli o bezpieczeństwie podczas projektowania i kompilacji kodu. Istnieje kilka zestawów narzędzi bezpieczeństwa i zasobów dostępnych dla programistów; na przykład RSA ma wiele zestawów narzędzi kryptograficznych. Niektóre podręczniki (np. Schneier’s Applied Cryptography) zawierają płyty CD-ROM z przykładowym kodem. Ponadto Centrum Koordynacji Zespołu Reagowania na Awarie Komputerowe (CERT/CC) zostało uruchomione w grudniu 1988 r. przez Agencję Zaawansowanych Projektów Badawczych Obrony (DARPA), która była częścią Departamentu Obrony USA. W CERT/CC znajduje się  Software Engineering Institute, ośrodek badawczy finansowany ze środków federalnych, prowadzony przez Carnegie Mellon University. CERT/CC bada luki w zabezpieczeniach Internetu, świadczy usługi dla zaatakowanych stron internetowych i publikuje alerty bezpieczeństwa. Działalność badawcza CERT/CC obejmuje obszar obliczeń w sieci WAN i rozwijanie ulepszonego bezpieczeństwa w Internecie. Firma Carnegie-Mellon ustanowiła bezpieczne standardy kodowania oparte na języku, które mogą pomóc w ustaleniu jednolitego zestawu zasad i wytycznych w całej grupie programistów. Niezależnie od języka wybranego do tworzenia oprogramowania, ważne jest, aby podjąć decyzję w oparciu o mocne i słabe strony języka. Oznacza to, że analiza ryzyka musi być częścią projektowania każdego projektu programistycznego, w tym wyboru języka programistycznego. Dzięki zrozumieniu tych mocnych i słabych stron, można podejmować lepsze decyzje, aby zapobiec nieumyślnemu ujawnieniu luk w zabezpieczeniach języka podczas opracowywania.

Najlepsze praktyki i wytyczne

„Najlepsze praktyki” to termin, który może wywołać ożywioną debatę, zwłaszcza dotyczącą bezpieczeństwa. Ogólnie rzecz biorąc, najlepsze praktyki, a szczególnie w odniesieniu do bezpieczeństwa, często padają ofiarą dogmatycznego, a czasem ślepego oddania bezsensownym praktykom, które mają niewiele wspólnego z bezpieczeństwem, a więcej z wiarą lub tradycją. Niezależnie od tego, najlepsze praktyki pomagają zapewnić zestaw wskazówek, które pomagają zapewnić strukturę. Ponadto najlepsze praktyki można dostosować, aby pomóc w zaspokojeniu potrzeb konkretnej sytuacji. Przykładem może być specjalna seria 800 publikacji National Institute of Standards and Technology (NIST). Seria ta zawiera najlepsze praktyki i zalecenia, które można dostosować lub zintegrować z innymi praktykami, aby pomóc w poprawie i wyeliminowaniu tradycji określonej praktyki, która może nie mają już uzasadniony użytek. Biorąc to pod uwagę, większość ogólnych podręczników bezpieczeństwa zawiera zalecenia dotyczące aspektów programowania związanych z bezpieczeństwem — patrz na przykład Stallings — które w rzeczywistości mają bardzo realną korzyść w tworzeniu bezpieczniejszego oprogramowania. Oprócz projektowania zabezpieczeń w systemie od samego początku, istnieją również oczywiste wskazówki, które pomogą w tworzeniu bezpiecznego oprogramowania:

* Narzucaj silną identyfikację i uwierzytelnianie (I&A) dla krytycznych i wrażliwych systemów oprócz I&A dostępnego z systemu operacyjnego; najlepiej użyć uwierzytelniania opartego na tokenie lub uwierzytelniania biometrycznego jako części fazy inicjowania aplikacji.

* Dokładnie udokumentuj swój kod, w tym za pomocą słowników danych dla pełnej definicji dopuszczalnych danych wejściowych i wyjściowych funkcji oraz dopuszczalnego zakresu i typu wartości dla wszystkich zmiennych.

* Podczas przechowywania wrażliwych używaj zmiennych lokalnych, a nie zmiennych globalnych

 dane, które powinny być używane tylko w ramach określonej procedury (tj. użyj architektury stosu procesów, aby ograniczyć nieumyślny lub nieautoryzowany dostęp do danych w stosie).

* Ponownie zainicjuj tymczasowe przechowywanie natychmiast po ostatnim prawidłowym użyciu zmiennej, co utrudnia oczyszczanie złoczyńców.

* Ogranicz funkcjonalność w określonym module do tego, co jest wymagane dla określonej pracy (np. nie używaj tego samego modułu do funkcji nadzorczych i rutynowych funkcji wykonywanych przez personel biurowy).

* Zdefiniuj widoki danych w bazach danych, które są zgodne z wymaganiami funkcjonalnymi i ograniczają dostęp do danych wrażliwych (np. widok danych z bazy danych dokumentacji medycznej powinien wykluczać identyfikatory pacjentów, gdy pracownik działu finansowego korzysta z bazy danych do agregacji statystycznej).

* Używaj silnego szyfrowania (nie szyfrowania własnego), które ma standardowe w branży procedury do ochrony poufnych i krytycznych danych na dysku. Lokalnie opracowane, domowe szyfrowanie na ogół nie jest tak bezpieczne.

* Nie zezwalaj programistom na dostęp do produkcyjnych baz danych.

* Randomizuj lub w inny sposób maskuj dane wrażliwe podczas generowania podzbiorów testowych z danych produkcyjnych.

* Użyj monitorów pokrycia testów, aby zweryfikować, czy wszystkie sekcje kodu źródłowego są rzeczywiście wykonywane podczas testów zapewnienia jakości; zbadać funkcje kodu, który nigdy nie jest wykonywany.

* Zintegruj możliwość logowania ze wszystkimi aplikacjami w celu debugowania, odzyskiwania danych po awariach w trakcie transakcji oraz w celach bezpieczeństwa, takich jak analiza kryminalistyczna.

* Utwórz rekordy pliku dziennika, które zawierają kryptograficznie dźwiękowy kod uwierzytelniania wiadomości (MAC), który sam zawiera adres MAC poprzedniego rekordu jako dane wejściowe algorytmu; ta technika zapewnia, że ​​sfałszowanie lub zmodyfikowanie pliku dziennika będzie trudniejsze dla złoczyńcy.

* Rejestruj wszystkie inicjacje procesów dla programu i rejestruj zakończenie procesu; zawierać pełne informacje o tym, kto załadował program lub moduł.

* Rejestruj wszystkie modyfikacje rekordów i opcjonalnie udostępniaj również rejestrowanie w celu odczytu.

* Użyj blokowania na poziomie rekordu, aby zapobiec przypadkowemu nadpisaniu danych w rekordach, do których uzyskuje się dostęp jednocześnie. Pamiętaj, aby odblokować sekwencję blokad w odwrotnej kolejności sekwencji blokad, aby zapobiec zakleszczeniu. (Tak więc, jeśli zablokujesz zasób A, B i C w tej kolejności, odblokuj C, następnie B, a następnie A.)

* Podpisz swój kod źródłowy za pomocą podpisów cyfrowych.

* Używaj sum kontrolnych w produkcyjnych plikach wykonywalnych, aby utrudnić ukrycie nieautoryzowanych modyfikacji.

Mike Gerdes, były menedżer w AtomicTangerine, przedstawił te sugestie w trakcie dyskusji:

* Poleć czytelnikom przyjęcie praktyki projektowania kodu w bardziej holistyczny sposób. Powszechną praktyką jest pisanie i testowanie procedur w sposób, który weryfikuje, jak kod przetwarza dane w zamierzony sposób. Aby uniknąć skutków złośliwego kodu i ataków z wprowadzaniem danych, programista musi również napisać kod, który zajmuje się tym, co nie powinno być przetwarzane. Bardziej kompletna metodologia projektowania obejmowałaby również testowanie wszystkich informacji przychodzących, aby zapewnić wykluczenie wszelkich danych, które nie spełniają wymagań dotyczących akceptowalnych danych. Ta metoda powinna być stosowana w aplikacjach o wysokim ryzyku i tych, które mają wyjątkowo żmudny cykl testowy, i wyeliminuje wiele powszechnie stosowanych obecnie metod ataku.

* Ustal kryteria określania poziomu wrażliwości informacji zawartych lub przetwarzanych przez aplikację i podprogramy.

* Jeśli nie są one jeszcze obecne, rozważ wdrożenie formalnych procedur kontrolnych w metodologii programowania oprogramowania, aby upewnić się, że wszystkie dane są przeglądane podczas procesów zapewniania jakości oraz są sklasyfikowane i obsługiwane odpowiednio do przypisanego poziomu.

* Zidentyfikuj i uwzględnij wszelkie obowiązkowe cechy systemu operacyjnego i bezpieczeństwa sieci dla systemu produkcyjnego w specyfikacjach oprogramowania. Oprócz dostarczenia zespołom programistycznym i QA pewnej definicji środowiska, w którym ma działać oprogramowanie, dając administratorowi i użytkownikom końcowym wyobrażenie o tym, jakie były Twoje oczekiwania podczas tworzenia kodu, może być niezwykle przydatne w określaniu, gdzie oprogramowanie może, lub nie może być używany.

* W stosownych przypadkach zweryfikuj podpisy cyfrowe procedur przetwarzających poufne dane, gdy kod jest ładowany do wykonania.

* Jeśli uwzględniasz sumy kontrolne plików wykonywalnych dla kodu produkcyjnego, dołącz procedury, które weryfikują sumy kontrolne przy każdym ponownym uruchomieniu systemu.

Zespół Carnegie-Mellon Software Engineering Institute Computer Emergency Response Team (CERT) zapewnia dobrą listę „Top 10 bezpiecznych praktyk kodowania”. Poniższa lista zawiera kilka dodatkowych elementów, które należy wziąć pod uwagę oprócz elementów wymienionych wcześniej:

* Sprawdź poprawność danych wejściowych ze źródeł danych. Właściwa walidacja danych wejściowych może wyeliminować wiele luk w zabezpieczeniach oprogramowania.

* Skompiluj kod przy użyciu najwyższego poziomu ostrzeżeń dostępnego dla kompilatora i wyeliminuj ostrzeżenia, modyfikując kod.

* Utwórz architekturę oprogramowania i zaprojektuj oprogramowanie w celu wdrożenia i egzekwowania zabezpieczeń. Carnegie-Mellon ma wstępnie ustalony standard programowania według języka, który można znaleźć na stronie www.cert.org/secure-coding/scstandards.html

* Pamiętaj o zasadzie K-I-S-S (Keep It Simple, Stupid). Złożone projekty zwiększają prawdopodobieństwo wystąpienia błędów lub luk w zabezpieczeniach oraz potrzebę zwiększenia złożoności mechanizmów bezpieczeństwa lub wynikających z nich środków łagodzących.

* Opieraj decyzje o dostępie na pozwoleniu, a nie na wykluczeniu, aby przestrzegać zasady najmniejszych uprawnień.

* Oczyszczanie danych wysyłanych do innych systemów, takich jak powłoki poleceń, relacyjne bazy danych i gotowe komponenty komercyjne (COTS).

* Przećwicz dogłębną obronę w aplikacji, tak aby jeśli jedna warstwa ochrony okazała się niewystarczająca, inna może zapobiec lub ograniczyć lukę w zabezpieczeniach przed przekształceniem się w lukę, którą można wykorzystać.

* Ustanowienie dobrych technik zapewniania jakości, aby skuteczniej identyfikować i eliminować luki w zabezpieczeniach.

* Opracuj i/lub zastosuj bezpieczny standard kodowania dla docelowego języka i platformy programistycznej.

* Zdefiniuj wymagania bezpieczeństwa na początku projektu.

* Myśl jak haker i korzystaj z modelowania zagrożeń, aby przewidywać zagrożenia, na jakie może być narażone oprogramowanie. Jeśli zastanowisz się, jak można go zaatakować, będziesz w stanie zapobiec atakowi, zanim się pojawi.

System operacyjny

Jądro bezpieczeństwa systemu operacyjnego jest odpowiedzialne za egzekwowanie zasad bezpieczeństwa w systemie operacyjnym i aplikacji. W związku z tym architektura systemu operacyjnego jądra jest zwykle warstwowa, z jądrem w warstwie najbardziej uprzywilejowanej. Jest to niewielka część systemu operacyjnego, ale wszystkie odniesienia do informacji i zmiany w autoryzacji przechodzą przez jądro. Aby być bezpiecznym jądro musi spełniać trzy podstawowe kryteria:

  1. Kompletność. Cały dostęp do informacji musi przejść przez jądro.
  2. Izolacja. Jądro musi być chronione przed nieautoryzowanym dostępem.
  3. Weryfikowalność. Należy udowodnić, że jądro spełnia specyfikacje projektowe.

Jak wspomniano, jądro działa w najbardziej uprzywilejowanej warstwie. Większość systemów operacyjnych ma dwa tryby dostępu do procesora, użytkownika i jądra. Ogólny kod aplikacji działa w trybie użytkownika, podczas gdy system operacyjny działa w trybie jądra. Tryb jądra umożliwia procesorowi pełny dostęp do całej pamięci systemowej i instrukcji procesora. Gdy aplikacje nie są napisane bezpiecznie, to rozdzielenie trybów może zostać naruszone, umożliwiając wykorzystanie luk w zabezpieczeniach za pomocą dowolnego kodu, przepełnienia bufora i innych technik.

Projekt

Dane informacyjne zebrane podczas zbierania analizy trafiają do projektu oprogramowania jako wymagania. Wynikiem analizy są dane, logika i projekt proceduralny. Deweloperzy biorą dane i dane modelu informacyjnego i przekształcają je w struktury danych, które będą wymagane do wdrożenia oprogramowania. Projekt logiki definiuje relacje między głównymi strukturami i komponentami aplikacji. Projekt proceduralny przekształca elementy konstrukcyjne w procedury opisowe. Wybierane są mechanizmy kontroli dostępu, określane są prawa i uprawnienia, oceniane są wszelkie inne specyfikacje bezpieczeństwa i ustalane są rozwiązania. Struktura podziału pracy obejmuje etapy rozwoju i wdrożenia. Struktura obejmuje harmonogram i szczegółowe czynności związane z testowaniem, rozwojem, etapami, testami integracyjnymi i dostarczaniem produktów. Decyzje podjęte w fazie projektowania mają kluczowe znaczenie dla rozwoju aplikacji. Projekt jest jedynym sposobem, w jaki wymagania są przekładane na komponenty oprogramowania. W ten sposób projektowanie oprogramowania jest podstawą procesu rozwoju i ma ogromny wpływ na jakość i konserwację oprogramowania. Jeśli dobry projekt produktu nie zostanie wdrożony na początku, reszta procesu rozwoju będzie o wiele trudniejsza.

Analiza wymagań

Bezpieczeństwo musi być częścią analizy wymagań w każdym projekcie deweloperskim. Większość twórców aplikacji wie, że dodanie zabezpieczeń po fakcie zwiększa koszty i czas oraz staje się bardziej skomplikowane. Wymagania mogą pochodzić z różnych źródeł:

* Potrzeby funkcjonalne systemu (w tym system operacyjny, na którym aplikacja będzie działać)

* Normy lub wytyczne krajowe, międzynarodowe lub organizacyjne

* Ograniczenia prawne

* Poziom czułości danych

* Istniejące polityki bezpieczeństwa

* Analiza kosztów i korzyści

* Ocena ryzyka

Celem analizy jest określenie, jakie informacje i procesy są potrzebne do obsługi pożądanych celów i funkcji oprogramowania oraz modelu biznesowego.

KWESTIE TECHNICZNE I PROCEDURALNE

Bezpieczeństwo powinno być zintegrowane na każdym etapie cyklu życia aplikacji. Obejmuje to analizę wymagań, etap projektowania oprogramowania i jądro bezpieczeństwa systemu operacyjnego, opracowywanie polityki korporacyjnej oraz programy uświadamiające, szkoleniowe i edukacyjne. Pisanie bezpiecznego kodu może być trudnym i technicznie trudnym przedsięwzięciem. Nie oznacza to, że nie można tego zrobić, ale wymaga to od dewelopera współpracy z resztą zespołu projektowego i innymi kluczowymi interesariuszami w celu sprostania wyzwaniom, które należy zdefiniować, uzgodnić i przezwyciężyć przed wydaniem oprogramowanie lub system. Dodatkowy nacisk na rynek, który może napędzać proces szybkiego rozwoju, może również mieć negatywny wpływ na bezpieczeństwo aplikacji. Problem należy rozwiązać na dwa sposoby, techniczny i proceduralny. Z technicznego punktu widzenia programiści muszą być świadomi pułapek związanych z tworzeniem aplikacji i ich unikać. Pod względem proceduralnym zespół programistów i organizacja muszą przestrzegać spójnej metodologii rozwoju. Ta spójność musi również obejmować identyfikację zagrożeń bezpieczeństwa na każdym etapie procesu, w tym analizę wymagań.

Rozważania dotyczące przepisów i zgodności

Kwestie dotyczące przepisów i zgodności stały się istotnym aspektem dla większości firm. Dodatkowy aspekt tworzenia oprogramowania zwiększa potrzebę bycia świadomym wpływu, jaki stanowe, federalne lub międzynarodowe przepisy mogą mieć na projekt programistyczny. Konieczne może być również rozważenie zgodności z branżami pozarządowymi. Jeśli organizacja podlega określonym regulacjom (np. ustawodawstwu Sarbanes-Oxley), wówczas dokumentacja napotkanych błędów i wynikających z nich wewnętrznych działań sprawozdawczych i naprawczych ma kluczowe znaczenie. Błąd powinien jasno wskazywać, w jaki sposób został zidentyfikowany, kto go zidentyfikował, w jaki sposób został zgłoszony kierownictwu, jaka będzie remediacja i kiedy przewiduje się jej zakończenie. Bez tych szczegółów kierownictwo może napotkać znaczne trudności w potwierdzeniu, że istnieją odpowiednie mechanizmy kontroli wewnętrznej, wraz z odpowiednim i odpowiednim zaangażowaniem kierownictwa. Ponadto błąd może skutkować zidentyfikowaniem przez audytorów zewnętrznych słabości kontroli lub, co gorsza, jako słabości istotnej (istotną słabością byłoby użycie języka programowania, który ma znaną nieodłączną wadę, a nie innego, bardziej odpowiedniego i bez skazy, podobnie jak w przypadku mostu z aluminium zamiast stali), w zależności od jego charakteru i surowości. Dodatkowo, gdy zostaną zauważone błędy, które mają wpływ na systemy lub aplikacje multilokacyjne, znaczenie i istotność – to znaczy zakres, w jakim błędy wpływają lub mogą wpływać na system lub jakikolwiek inny system, z którym interfejsy aplikacji – muszą być rozpatrywane z punktu widzenia zarówno filii, jak i lokalizacji centrali. Ponieważ przepisy różnią się w poszczególnych stanach i krajach, to, co można uznać za prawnie dopuszczalne standardy prywatności lub rachunkowości w jednym miejscu, może nie być akceptowalne w innym miejscu. W rezultacie, staranne rozważenie wymagań i zamierzonych zastosowań lub klientów musi być ustalone w ramach procesów inicjowania projektu i należytej staranności.