Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00079 009751 11028115 na godz. na dobę w sumie
C i C++. Bezpieczne programowanie. Receptury - książka
C i C++. Bezpieczne programowanie. Receptury - książka
Autor: , Liczba stron: 784
Wydawca: Helion Język publikacji: polski
ISBN: 83-7361-684-5 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> c - programowanie
Porównaj ceny (książka, ebook, audiobook).

Niemal wszystkie współczesne systemy i sieci komputerowe są atakowane przez hakerów. Techniki stosowane przez nich są przeróżne -- od przechwytywania haseł i podszywania się pod komputery lub usługi aż do ataków typu DoS. Niemal każdy problem związany z bezpieczeństwem sieci komputerowej jest wynikiem nieprawidłowego działania wykorzystywanego w niej oprogramowania. Pisanie bezpiecznych aplikacji jest trudne i często wymaga wiedzy, której wielu programistów po prostu nie posiada.

'C i C++. Bezpieczne programowanie. Receptury' to kompletne źródło wiedzy dla programistów, którzy chcą udoskonalić swoje umiejętności z zakresu tworzenia bezpiecznego kodu. Przedstawia gotowe rozwiązania zagadnień programistycznych, takich jak bezpieczna inicjalizacja aplikacji, kryptografia, uwierzytelnianie użytkowników, wymiana kluczy, zapobieganie penetracji i wielu innych. Każde zagadnienie jest przedstawione w postaci kodu źródłowego w języku C i C++ oraz obszernego opisu, co ułatwia dostosowanie go do własnych potrzeb.

Książka zawiera wszystkie informacje niezbędne do zabezpieczenia aplikacji przed hakerami.

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

C i C++. Bezpieczne programowanie. Receptury Autorzy: John Viega, Matt Messier T³umaczenie: Bart³omiej Garbacz (rozdz. 8 – 13), Krzysztof Miesniak (rozdz. 6), Miko³aj Szczepaniak (przedmowa, rozdz. 1 – 5, 7) ISBN: 83-7361-684-5 Tytu³ orygina³u: Secure Programming Cookbook for C and C++ Format: B5, stron: 784 „C i C++. Bezpieczne programowanie. Receptury” to kompletne ĥród³o wiedzy dla programistów, którzy chc¹ udoskonaliæ swoje umiejêtnosci z zakresu tworzenia bezpiecznego kodu. Przedstawia gotowe rozwi¹zania zagadnieñ programistycznych, takich jak bezpieczna inicjalizacja aplikacji, kryptografia, uwierzytelnianie u¿ytkowników, wymiana kluczy, zapobieganie penetracji i wielu innych. Ka¿de zagadnienie jest przedstawione w postaci kodu ĥród³owego w jêzyku C i C++ oraz obszernego opisu, co u³atwia dostosowanie go do w³asnych potrzeb. • Bezpieczne uruchamianie aplikacji • Kontrola dostêpu do plików i aplikacji • Sprawdzanie poprawnosci danych wejsciowych oraz ochrona przed atakami typu XSS i SQL Injection • Generowanie i obs³uga kluczy symetrycznych • Wykorzystywanie szyfrowania symetrycznego • Stosowanie klucza publicznego • Bezpieczna komunikacja sieciowa • Liczby losowe • Zapobieganie penetracjom oraz obs³uga b³êdów Ksi¹¿ka zawiera wszystkie informacje niezbêdne do zabezpieczenia aplikacji przed hakerami. IDZ DO IDZ DO PRZYK£ADOWY ROZDZIA£ PRZYK£ADOWY ROZDZIA£ SPIS TREĎCI SPIS TREĎCI KATALOG KSI¥¯EK KATALOG KSI¥¯EK KATALOG ONLINE KATALOG ONLINE ZAMÓW DRUKOWANY KATALOG ZAMÓW DRUKOWANY KATALOG TWÓJ KOSZYK TWÓJ KOSZYK DODAJ DO KOSZYKA DODAJ DO KOSZYKA CENNIK I INFORMACJE CENNIK I INFORMACJE ZAMÓW INFORMACJE ZAMÓW INFORMACJE O NOWOĎCIACH O NOWOĎCIACH ZAMÓW CENNIK ZAMÓW CENNIK CZYTELNIA CZYTELNIA FRAGMENTY KSI¥¯EK ONLINE FRAGMENTY KSI¥¯EK ONLINE Wydawnictwo Helion ul. Chopina 6 44-100 Gliwice tel. (32)230-98-63 e-mail: helion@helion.pl Spis treści Przedmowa ................................................................................................................... 11 Wstęp.............................................................................................................................15 1. Bezpieczna inicjalizacja ............................................................................................... 25 25 1.1. Zabezpieczanie środowiska pracy programu 32 1.2. Ograniczanie uprawnień w systemach Windows 40 1.3. Rezygnacja z uprawnień w programach setuid 45 1.4. Ograniczanie ryzyka związanego z separacją uprawnień 48 1.5. Bezpieczne zarządzanie deskryptorami plików 50 1.6. Bezpieczne tworzenie procesu potomnego 1.7. Bezpieczne uruchamianie programów zewnętrznych w systemach Unix 53 1.8. Bezpieczne uruchamianie zewnętrznych programów w systemach Windows 58 1.9. Wyłączanie zrzutów pamięci w przypadku wystąpienia błędu 60 2. Kontrola dostępu ......................................................................................................... 63 63 2.1. Model kontroli dostępu w systemach Unix 2.2. Model kontroli dostępu w systemach Windows 66 2.3. Określanie, czy dany użytkownik ma dostęp do danego pliku w systemie Unix 68 70 2.4. Określanie, czy dany katalog jest bezpieczny 73 2.5. Bezpieczne usuwanie plików 2.6. Bezpieczne uzyskiwanie dostępu do informacji o pliku 79 80 2.7. Ograniczone prawa dostępu do nowych plików w systemach Unix 83 2.8. Blokowanie plików 85 2.9. Synchronizacja dostępu procesów do zasobów w systemach Unix 2.10. Synchronizacja dostępu procesów do zasobów w systemach Windows 89 91 2.11. Tworzenie plików tymczasowych 94 2.12. Ograniczanie dostępu do systemu plików w systemach Unix 2.13. Ograniczanie dostępu do systemu plików i sieci w systemie FreeBSD 95 3. Sprawdzanie poprawności danych wejściowych....................................................... 97 98 102 105 3.1. Podstawowe techniki sprawdzania poprawności danych 3.2. Zapobieganie atakom z wykorzystaniem funkcji formatujących 3.3. Zapobieganie przepełnieniom bufora 3 3.4. Stosowanie biblioteki SafeStr 113 3.5. Zapobieganie koercji liczb całkowitych i problemowi przekroczenia zakresu 116 3.6. Bezpieczne stosowanie zmiennych środowiskowych 120 125 3.7. Sprawdzanie poprawności nazw plików i ścieżek 127 3.8. Obsługa kodowania URL 129 3.9. Sprawdzanie poprawności adresów poczty elektronicznej 3.10. Ochrona przed atakami typu cross-site scripting (XSS) 131 135 3.11. Ochrona przed atakami typu SQL injection 3.12. Wykrywanie nieprawidłowych znaków UTF-8 138 3.13. Zapobieganie przepełnieniom deskryptorów plików podczas stosowania funkcji select() 140 4. Podstawy kryptografii symetrycznej ........................................................................ 145 4.1. Reprezentacje kluczy wykorzystywanych w algorytmach kryptograficznych 146 4.2. Generowanie losowych kluczy symetrycznych 148 4.3. Szesnastkowe reprezentacje kluczy binarnych (lub innych nieprzetworzonych danych) 4.4. Przekształcanie szesnastkowych kluczy ASCII (lub innych szesnastkowych danych ASCII) na postać binarną 4.5. Kodowanie Base64 4.6. Dekodowanie łańcucha zakodowanego zgodnie ze standardem Base64 4.7. Reprezentowanie kluczy (lub dowolnych innych danych binarnych) w postaci tekstu zapisanego w języku angielskim 4.8. Przekształcanie kluczy tekstowych na klucze binarne 4.9. Stosowanie argumentów salt, jednorazowych identyfikatorów i wektorów inicjalizacji 4.10. Generowanie kluczy symetrycznych na bazie haseł 4.11. Algorytmiczne generowanie kluczy symetrycznych na bazie jednego tajnego klucza głównego 4.12. Szyfrowanie okrojonego zbioru znaków 4.13. Bezpieczne zarządzanie materiałem klucza 4.14. Badanie czasu działania algorytmów kryptograficznych 149 151 152 154 157 159 161 165 171 175 178 179 5. Szyfrowanie symetryczne ......................................................................................... 185 5.1. Podejmowanie decyzji w kwestii stosowania wielu algorytmów szyfrujących 185 186 5.2. Wybór najlepszego algorytmu szyfrującego 190 5.3. Wybór właściwej długości klucza 5.4. Wybór trybu pracy szyfru blokowego 193 203 5.5. Stosowanie podstawowych operacji szyfru blokowego 207 5.6. Stosowanie ogólnej implementacji trybu CBC 5.7. Stosowanie ogólnej implementacji trybu CFB 217 224 5.8. Stosowanie ogólnej implementacji trybu OFB 4 | Spis treści 5.9. Stosowanie ogólnej implementacji trybu CTR 5.10. Stosowanie trybu szyfrowania CWC 5.11. Ręczne dodawanie i sprawdzanie dopełniania szyfru 5.12. Wyznaczanie z góry strumienia klucza w trybach OFB, CTR, CCM i CWC (oraz w szyfrach strumieniowych) 5.13. Zrównoleglanie szyfrowania i deszyfrowania w trybach, które na takie działania zezwalają (bez wprowadzania ewentualnych niezgodności) 5.14. Zrównoleglanie szyfrowania i deszyfrowania w dowolnych trybach (a więc z możliwością wprowadzania ewentualnych niezgodności) 5.15. Szyfrowanie zawartości plików lub całych dysków 5.16. Stosowanie wysokopoziomowych, odpornych na błędy interfejsów API dla operacji szyfrowania i deszyfrowania 5.17. Konfiguracja szyfru blokowego (dla trybów szyfrowania CBC, CFB, OFB oraz ECB) w pakiecie OpenSSL 5.18. Stosowanie szyfrów ze zmienną długością klucza w pakiecie OpenSSL 5.19. Wyłączanie mechanizmu dopełniania w szyfrach pakietu OpenSSL pracujących w trybie CBC 5.20. Dodatkowa konfiguracja szyfrów w pakiecie OpenSSL 5.21. Sprawdzanie właściwości konfiguracji szyfru w pakiecie OpenSSL 5.22. Wykonywanie niskopoziomowego szyfrowania i deszyfrowania w pakiecie OpenSSL 5.23. Konfiguracja i stosowanie szyfru RC4 5.24. Stosowanie szyfrów z kluczem jednorazowym 5.25. Stosowanie szyfrowania symetrycznego z wykorzystaniem CryptoAPI firmy Microsoft 228 233 237 239 240 244 245 249 254 259 260 261 262 264 267 270 271 5.26. Tworzenie obiektu klucza interfejsu CryptoAPI na bazie dowolnych danych klucza 277 5.27. Uzyskiwanie surowych danych klucza z obiektu klucza interfejsu CryptoAPI 280 6. Funkcje skrótu i uwierzytelnianie wiadomości ........................................................283 6.1. Zrozumienie podstaw funkcji skrótu i kodu uwierzytelniającego wiadomość MAC 283 6.2. Decydowanie, czy obsługiwać wiele skrótów wiadomości lub kodów MAC 287 6.3. Wybór kryptograficznego algorytmu skrótu 288 292 6.4. Wybór kodu uwierzytelnienia wiadomości 296 6.5. Przyrostowe tworzenie skrótów danych 300 6.6. Tworzenie skrótu z pojedynczego łańcucha znaków 6.7. Używanie skrótu kryptograficznego 302 6.8. Wykorzystanie identyfikatora jednorazowego do obrony przed atakami wykorzystującymi paradoks dnia urodzin 6.9. Sprawdzanie spójności wiadomości 6.10. Używanie HMAC 6.11. Używanie OMAC (prostego kodu MAC opartego na szyfrze blokowym) 303 307 309 312 Spis treści | 5 6.12. Używanie HMAC lub OMAC z identyfikatorem jednorazowym 6.13. Używanie kodu MAC, który jest wystarczająco szybki w realizacji programowej i sprzętowej 6.14. Używanie kodu MAC zoptymalizowanego do szybszego działania w realizacji programowej 6.15. Konstruowanie funkcji skrótu z szyfru blokowego 6.16. Używanie szyfru blokowego do budowy mocnej funkcji skrótu 6.17. Używanie mniejszych znaczników MAC 6.18. Szyfrowanie z zachowaniem spójności wiadomości 6.19. Tworzenie własnego kodu MAC 6.20. Szyfrowanie za pomocą funkcji skrótu 6.21. Bezpieczne uwierzytelnianie kodu MAC (obrona przed atakami związanymi z przechwytywaniem i powtarzaniem odpowiedzi) 6.22. Przetwarzanie równoległe kodu MAC 317 318 319 322 325 329 329 331 332 334 335 7. Kryptografia z kluczem publicznym.......................................................................... 337 7.1. Określanie sytuacji, w których należy stosować techniki kryptografii z kluczem publicznym 7.2. Wybór algorytmu z kluczem publicznym 7.3. Wybór rozmiarów kluczy publicznych 7.4. Przetwarzanie wielkich liczb 7.5. Generowanie liczby pierwszej i sprawdzanie czy dana liczba jest liczbą pierwszą 7.6. Generowanie pary kluczy szyfru RSA 7.7. Oddzielanie kluczy publicznych i prywatnych w pakiecie OpenSSL 7.8. Konwertowanie łańcuchów binarnych na postać liczb całkowitych na potrzeby szyfru RSA 7.9. Przekształcanie liczb całkowitych do postaci łańcuchów binarnych na potrzeby szyfru RSA 7.10. Podstawowa operacja szyfrowania za pomocą klucza publicznego algorytmu RSA 7.11. Podstawowa operacja deszyfrowania za pomocą klucza prywatnego algorytmu RSA 7.12. Podpisywanie danych za pomocą klucza prywatnego szyfru RSA 7.13. Weryfikacja cyfrowo podpisanych danych za pomocą klucza publicznego 339 342 343 346 355 358 361 362 363 364 368 370 algorytmu RSA 374 7.14. Bezpieczne podpisywanie i szyfrowanie danych za pomocą algorytmu RSA 376 7.15. Wykorzystywanie algorytmu DSA 381 7.16. Reprezentowanie kluczy publicznych i certyfikatów w postaci łańcuchów binarnych (zgodnie z regułami kodowania DER) 7.17. Reprezentowanie kluczy i certyfikatów w postaci tekstu (zgodnie z regułami kodowania PEM) 386 390 6 | Spis treści 8. Uwierzytelnianie i wymiana kluczy.......................................................................... 397 397 8.1. Wybór metody uwierzytelniania 8.2. Uzyskiwanie informacji o użytkownikach i grupach w systemach uniksowych 8.3. Uzyskiwanie informacji o użytkownikach i grupach w systemach Windows 8.4. Ograniczanie dostępu na podstawie nazwy maszyny lub adresu IP 8.5. Generowanie losowych haseł i wyrażeń hasłowych 8.6. Sprawdzanie odporności haseł na ataki 8.7. Monitowanie o hasło 8.8. Kontrola nad nieudanymi próbami uwierzytelnienia 8.9. Uwierzytelnianie oparte na hasłach z użyciem funkcji crypt() 8.10. Uwierzytelnianie oparte na hasłach z użyciem funkcji MD5-MCF 8.11. Uwierzytelnianie oparte na hasłach z użyciem funkcji PBKDF2 8.12. Uwierzytelnianie przy użyciu modułów PAM 8.13. Uwierzytelnianie za pomocą systemu Kerberos 8.14. Uwierzytelnianie z wykorzystaniem mechanizmu HTTP Cookies 8.15. Uwierzytelnianie oraz wymiana kluczy oparte na hasłach 8.16. Przeprowadzanie uwierzytelnionej wymiany klucza przy użyciu algorytmu RSA 8.17. Użycie podstawowego protokołu uzgadniania klucza metodą Diffiego-Hellmana 8.18. Wspólne użycie metody Diffiego-Hellmana i algorytmu DSA 8.19. Minimalizacja okresu podatności na ataki 407 410 413 420 424 425 430 432 434 439 442 445 449 452 459 461 466 w przypadku uwierzytelniania bez użycia infrastruktury PKI 467 8.20. Zapewnianie przyszłego bezpieczeństwa w systemie symetrycznym 473 8.21. Zapewnianie przyszłego bezpieczeństwa w systemie z kluczem publicznym 474 476 8.22. Potwierdzanie żądań za pomocą wiadomości poczty elektronicznej 9. Komunikacja sieciowa ...............................................................................................483 484 486 9.1. Tworzenie klienta SSL 9.2. Tworzenie serwera SSL 9.3. Używanie mechanizmu buforowania sesji w celu zwiększenia wydajności serwerów SSL 9.4. Zabezpieczanie komunikacji sieciowej na platformie Windows przy użyciu interfejsu WinInet API 9.5. Aktywowanie protokołu SSL bez modyfikowania kodu źródłowego 9.6. Używanie szyfrowania standardu Kerberos 9.7. Komunikacja międzyprocesowa przy użyciu gniazd 9.8. Uwierzytelnianie przy użyciu uniksowych gniazd domenowych 9.9. Zarządzanie identyfikatorami sesji 9.10. Zabezpieczanie połączeń bazodanowych 489 492 496 498 503 509 512 513 Spis treści | 7 10. 9.11. Używanie wirtualnych sieci prywatnych w celu zabezpieczenia połączeń sieciowych 9.12. Tworzenie uwierzytelnionych bezpiecznych kanałów bez użycia SSL 516 517 Infrastruktura klucza publicznego............................................................................ 527 527 10.1. Podstawy infrastruktury klucza publicznego 10.2. Otrzymywanie certyfikatu 538 543 10.3. Używanie certyfikatów głównych 546 10.4. Podstawy metodologii weryfikacji certyfikatów X.509 10.5. Przeprowadzanie weryfikacji certyfikatów X.509 przy użyciu OpenSSL 548 10.6. Przeprowadzanie weryfikacji certyfikatów X.509 przy użyciu interfejsu CryptoAPI 10.7. Weryfikowanie certyfikatu pochodzącego od partnera komunikacji SSL 10.8. Dodawanie mechanizmu sprawdzania nazwy hosta do procesu weryfikacji certyfikatu 10.9. Używanie list akceptacji w celu weryfikowania certyfikatów 10.10. Pobieranie list unieważnionych certyfikatów przy użyciu OpenSSL 10.11. Pobieranie list unieważnionych certyfikatów przy użyciu CryptoAPI 10.12. Sprawdzanie stanu unieważnienia poprzez protokół OCSP przy wykorzystaniu OpenSSL 553 558 562 566 569 576 582 11. Liczby losowe ............................................................................................................. 587 587 11.1. Określanie charakteru liczb losowych, których należy użyć 11.2. Używanie ogólnego interfejsu API dla obsługi losowości i entropii 592 11.3. Używanie standardowej infrastruktury losowości w systemach uniksowych 594 11.4. Używanie standardowej infrastruktury losowości w systemach Windows 598 600 11.5. Używanie generatora poziomu aplikacji 609 11.6. Ponowna inicjalizacja ziarna generatora liczb pseudolosowych 11.7. Używanie rozwiązania kompatybilnego z demonem zbierania entropii 612 11.8. Zbieranie entropii lub wartości pseudolosowych przy użyciu pakietu EGADS 11.9. Używanie interfejsu API obsługi liczb losowych biblioteki OpenSSL 11.10. Otrzymywanie losowych wartości całkowitych 11.11. Otrzymywanie losowych wartości całkowitych z zadanego przedziału 11.12. Otrzymywanie losowych wartości zmiennopozycyjnych 616 620 622 623 o rozkładzie jednorodnym 625 11.13. Otrzymywanie wartości zmiennopozycyjnych o rozkładzie niejednorodnym 626 11.14. Otrzymywanie losowych drukowalnych ciągów znaków ASCII 627 11.15. Uczciwe tasowanie 628 11.16. Kompresowanie danych z entropią do postaci ziarna o ustalonym rozmiarze 629 11.17. Zbieranie entropii w momencie uruchamiania systemu 630 632 11.18. Testowanie statystyczne liczb losowych 8 | Spis treści 11.19. Szacowanie i zarządzanie entropią 11.20. Zbieranie entropii na podstawie interakcji z klawiaturą 11.21. Zbieranie entropii na podstawie zdarzeń związanych z obsługą myszy w systemie Windows 11.22. Zbieranie entropii na podstawie pomiarów czasowych wątków 11.23. Zbieranie entropii na podstawie stanu systemu 637 645 653 657 659 12. Zapobieganie ingerencji .............................................................................................661 662 667 672 677 679 680 681 682 683 684 689 691 693 695 696 698 703 12.1. Podstawowe kwestie dotyczące problemu ochrony oprogramowania 12.2. Wykrywanie modyfikacji 12.3. Zaciemnianie kodu 12.4. Przeprowadzanie zaciemniania na poziomie bitów i bajtów 12.5. Przeprowadzanie przekształceń na zmiennych z użyciem wartości stałych 12.6. Scalanie zmiennych skalarnych 12.7. Rozdzielanie zmiennych 12.8. Ukrywanie wartości logicznych 12.9. Używanie wskaźników do funkcji 12.10. Zmiana struktury tablic 12.11. Ukrywanie ciągów znaków 12.12. Wykrywanie programów uruchomieniowych 12.13. Wykrywanie programów uruchomieniowych w systemie Unix 12.14. Wykrywanie programów uruchomieniowych w systemie Windows 12.15. Wykrywanie programu SoftICE 12.16. Przeciwdziałanie deasemblacji 12.17. Używanie kodu samomodyfikującego 13. Inne zagadnienia........................................................................................................ 709 709 13.1. Obsługa błędów 713 13.2. Bezpieczne usuwanie danych z pamięci 13.3. Zapobieganie stronicowaniu pamięci na dysku 716 717 13.4. Poprawne używanie argumentów zmiennych 720 13.5. Poprawna obsługa sygnałów 13.6. Ochrona przed atakami rozbicia w systemie Windows 724 726 13.7. Ochrona przed uruchomieniem zbyt wielu wątków 731 13.8. Ochrona przed tworzeniem zbyt wielu gniazd sieciowych 734 13.9. Ochrona przed atakami wyczerpania zasobów w systemie Unix 13.10. Ochrona przed atakami wyczerpania zasobów w systemie Windows 737 13.11. Korzystanie ze sprawdzonych praktyk dotyczących rejestrowania nadzorczego 740 Skorowidz ............................................................................................................................. 745 Spis treści | 9 ROZDZIAŁ 9. Komunikacja sieciowa Obecnie większość aplikacji jest związana z uczestniczeniem w pewnego rodzaju aktywności sieciowej. Niestety, wielu programistów nie wie, w jaki sposób należy uzyskiwać dostęp do sieci w sposób bezpieczny. Receptury prezentowane w niniejszym rozdziale mają na celu po- móc w wykorzystywaniu sieci we własnych programach. Dla wielu programistów bezpieczeń- stwo sieciowe postrzegane z punktu widzenia aplikacji oznacza użycie protokołu Secure Socket Layer (SSL), jednak SSL nie stanowi magicznego rozwiązania. Niekiedy może być trudno użyć go w sposób prawidłowy, w wielu sytuacjach stanowi nadmierne obciążenie, a niekiedy jest rozwiązaniem niewystarczającym. W niniejszym rozdziale zaprezentowano receptury opisu- jące użycie pakietu OpenSSL w celu tworzenia klientów i serwerów obsługujących protokół SSL, jak również receptury dotyczące komunikacji sieciowej i międzyprocesowej odbywającej się bez użycia SSL. W przypadku platformy Windows z wyjątkiem użycia SSL w celu szyfrowania ruchu HTTP (co omówiono w recepturze 9.4) autorzy zdecydowali się ograniczyć receptury poświęcone protokołowi SSL tylko do pakietu OpenSSL, który jest dostępny za darmo i jest przenośny na wiele platform, w tym również właśnie Windows. W systemie Windows firma Microsoft zapewnia dostęp do implementacji protokołu SSL po- przez interfejs SSPI (ang. Security Support Provider Interface). SSPI jest dobrze udokumento- wany, ale niestety, użycie samego SSL — nie. Co więcej, implementacja klienta lub serwera wykorzystującego SSL za pomocą SSPI w systemie Windows jest o wiele bardziej skompliko- wana niż użycie OpenSSL. Interfejs SSPI jest zaskakująco niskopoziomowy, wymaga od wyko- rzystujących go programów wykonywania wielu zadań związanych z wymianą komunika- tów protokołu. Ze względu na fakt, że SSL trudno jest używać poprawnie, pożądanym rozwiązaniem jest ukrycie szczegółów protokołu za implementacją wysokopoziomową (taką jak OpenSSL). Stąd też autorzy będą unikać używania interfejsu SSPI. Jeżeli Czytelnik jest bardziej zainteresowany interfejsami SSPI oraz SSL, warto sięgnąć do dokumentacji Microsoft Developer’s Network (MSDN) oraz po przykłady zawarte w pakiecie Microsoft Windows Platform SDK, który jest dostępny pod adresem http://www.microsoft.com/ msdownload/platformsdk/sdkupdate/. Odpowiednie fragmenty przykładowego kodu można zna- leźć z katalogu Microsoft SDKSamplesSecuritySSPISSL, skąd instaluje się je w swoim sys- temie (zazwyczaj w katalogu Program Files na dysku startowym). 483 9.1. Tworzenie klienta SSL Tworzenie klienta SSL Problem Chcemy zestawić połączenia klienta z serwerem zdalnym przy użyciu protokołu SSL. Rozwiązanie Zestawianie połączenia z serwerem zdalnym przy użyciu protokołu SSL nie różni się bardzo od zestawiania połączenia bez jego użycia, a przynajmniej nie musi się wiele różnić. Wymaga to nieco większego nakładu sił w kwestii konfiguracji i w głównej mierze polega na utworzeniu obiektu spc_x509store_t (patrz receptura 10.5), który zawiera informacje potrzebne do do- konania weryfikacji serwera. Kiedy zostanie to zrobione, należy utworzyć obiekt SSL_CTX i dodać go do połączenia. Za pozostałe działania odpowiedzialny jest pakiet OpenSSL. Przed lekturą niniejszej receptury należy dobrze poznać podstawy infrastruktury klucza publicznego (patrz receptura 10.1). Analiza Po utworzeniu obiektu spc_x509store_t poprzez załadowanie go z odpowiednimi certyfika- tami i listami CRL (informacje na temat otrzymywania list CRL1 można znaleźć w recepturach 10.10 oraz 10.11), połączenie się ze zdalnym serwerem przy użyciu protokołu SSL może po- legać tylko na wywołaniu funkcji spc_connect_ssl(). Opcjonalnie można samodzielnie utworzyć obiekt SSL_CTX, używając funkcji spc_create_sslctx() lub interfejsu API OpenSSL. Można również wykorzystać obiekt już istniejący, utworzony dla innych połączeń, lub pozo- stawić to w gestii funkcji spc_connect_ssl(). W tym drugim przypadku połączenie zostanie zestawione, zaś utworzony obiekt SSL_CTX zostanie zwrócony jako wskaźnik na wskaźnik do obiektu SSL_CTX przekazany jako argument funkcji. #include openssl/bio.h #include openssl/ssl.h BIO *spc_connect_ssl(char *host, int port, spc_x509store_t *spc_store, SSL_CTX **ctx) { BIO *conn = 0; int our_ctx = 0; if (*ctx) { CRYPTO_add( ((*ctx)- references), 1, CRYPTO_LOCK_SSL_CTX); if (spc_store spc_store != SSL_CTX_get_app_data(*ctx)) { SSL_CTX_set_cert_store(*ctx, spc_create_x509store(spc_store)); SSL_CTX_set_app_data(*ctx, spc_store); } } else { *ctx = spc_create_sslctx(spc_store); our_ctx = 1; } 1 Lista unieważnionych certyfikatów (ang. Certificate Revocation List) — przyp. tłum. 484 | Rozdział 9. Komunikacja sieciowa if (!(conn = BIO_new_ssl_connect(*ctx))) goto error_exit; BIO_set_conn_hostname(conn, host); BIO_set_conn_int_port(conn, port); if (BIO_do_connect(conn) = 0) goto error_exit; if (our_ctx) SSL_CTX_free(*ctx); return conn; error_exit: if (conn) BIO_free_all(conn); if (*ctx) SSL_CTX_free(*ctx); if (our_ctx) *ctx = 0; return 0; } Powyżej zaprezentowano dodatkową funkcję, która obsługuje różnice między łączeniem się z serwerem zdalnym przy użyciu SSL oraz bez jego użycia. W obu przypadkach zwracany jest obiekt BIO, którego można używać w ten sam sposób bez względu na to, czy zestawiono połączenie SSL czy nie. Jeżeli znacznik ssl w wywołaniu tej funkcji ma wartość zerową, argu- menty spc_store oraz ctx są ignorowane, ponieważ mają zastosowanie wyłącznie w przy- padku połączeń SSL. OpenSSL często korzysta z obiektów BIO i wiele funkcji interfejsu API również pobiera argu- menty BIO. Pojawia się pytanie, czym są te obiekty. Ujmując rzecz skrótowo, obiekty BIO sta- nowią abstrakcję dla operacji wejścia-wyjścia, która oferuje jednolity, częściowo niezależny interfejs. Obiekty BIO są tworzone dla plikowych operacji wejścia-wyjścia, operacji dokonywa- nych na gniazdach oraz w pamięci. Ponadto specjalne obiekty BIO, znane jako filtry BIO (ang. BIO filters), mogą być używane w celu filtrowania danych przed ich zapisaniem lub odczyta- niem z odpowiedniego nośnika. Filtry BIO są tworzone dla operacji takich jak kodowanie ba- se64 oraz szyfrowanie przy użyciu szyfru symetrycznego. Interfejs API OpenSSL bazuje na obiektach BIO i specjalny rodzaj filtra zajmuje się szczegó- łami związanymi z SSL. Filtr SSL BIO jest najbardziej przydatny wówczas, gdy stosuje się go wspólnie z obiektem BIO gniazd, ale może również być używany do bezpośredniego łączenia dwóch obiektów BIO razem (jeden dla operacji odczytu, drugi — zapisu) lub w celu opakowy- wania potoków lub innego rodzaju podstawowych mechanizmów komunikacyjnych związa- nych z połączeniami. BIO *spc_connect(char *host, int port, int ssl, spc_x509store_t *spc_store, SSL_CTX **ctx) { BIO *conn; SSL *ssl_ptr; if (ssl) { if (!(conn = spc_connect_ssl(host, port, spc_store, ctx))) goto error_exit; BIO_get_ssl(conn, ssl_ptr); if (!spc_verify_cert_hostname(SSL_get_peer_certificate(ssl_ptr), host)) goto error_exit; if (SSL_get_verify_result(ssl_ptr) != X509_V_OK) goto error_exit; return conn; } *ctx = 0; if (!(conn = BIO_new_connect(host))) goto error_exit; BIO_set_conn_int_port(conn, port); if (BIO_do_connect(conn) = 0) goto error_exit; return conn; Tworzenie klienta SSL | 485 error_exit: if (conn) BIO_free_all(conn); return 0; } Jak stwierdzono wcześniej, funkcja spc_connect() podejmuje popołączeniową próbę prze- prowadzenia weryfikacji certyfikatu zdalnego klienta. Jeśli zamiast tego chce się przeprowadzić weryfikację za pomocą listy akceptacji (ang. whitelist) lub w ogóle zrezygnować z jej przepro- wadzania, należy wprowadzić odpowiednie zmiany w kodzie, wykorzystując recepturę 10.9 w zakresie weryfikacji za pomocą listy akceptacji. Jeżeli połączenie zostanie zestawione poprawnie, zostanie zwrócony obiekt BIO bez względu na to, czy użyto funkcji spc_connect_ssl() czy spc_connect(). Dzięki temu obiektowi można później używać funkcji BIO_read() w celu czytania danych oraz BIO_write() w celu ich zapisywania. Można również używać innych funkcji BIO, takich jak BIO_printf(). Po zakończeniu działań, kiedy chce się zamknąć połączenie, zawsze należy użyć funkcji BIO_ free_all() zamiast BIO_free() w celu usunięcia wszelkich dowiązanych filtrów BIO. Jeśli obiekt BIO z obsługą SSL otrzymano z którejś z powyższych funkcji, w zbiorze powiązań zawsze występują co najmniej dwa takie filtry: jeden dla filtra SSL oraz jeden dla połączenia gniazdowego. Zobacz również • Witryna główna OpenSSL: http://www.openssl.org/. • Receptury 10.1, 10.5, 10.9, 10.10 oraz 10.11. 9.2. Tworzenie serwera SSL Tworzenie serwera SSL Problem Chcemy napisać serwer sieciowy, który będzie akceptował połączenia SSL z klientami. Rozwiązanie Utworzenie serwera komunikującego się za pomocą protokołu SSL nie różni się zbytnio od utworzenia adekwatnego klienta (patrz receptura 9.1). Wymagane jest przeprowadzenie pewnych dodatkowych działań konfiguracyjnych. W szczególności należy utworzyć obiekt spc_x509store_t (patrz receptura 10.5) z certyfikatem oraz kluczem prywatnym. Informacje zawarte w tym obiekcie są przesyłane do klientów w czasie wstępnej wymiany potwierdzeń. Ponadto znacznik SPC_X509STORE_USE_CERTIFICATE musi być ustawiony w obiekcie spc_ x509store_t. Po jego utworzeniu należy wykonać wywołania służące do utworzenia nasłu- chującego obiektu BIO, wprowadzenia go w stan nasłuchiwania oraz akceptowania nowych połączeń (krótkie omówienie obiektów BIO zawarto w recepturze 9.1). 486 | Rozdział 9. Komunikacja sieciowa Analiza Po utworzeniu i pełnym zainicjalizowaniu obiektu spc_x509store_t pierwszym etapem tworzenia serwera SSL jest wywołanie funkcji spc_listen(). Nazwę hosta można podać jako NULL, co określa, że utworzone gniazdo powinno zostać powiązane ze wszystkimi interfejsami. Wszystkie inne informacje należy podać w formie ciągu znaków jako adres IP interfejsu powią- zania. Przykładowo, ciąg 127.0.0.1 spowoduje, że obiekt BIO serwera zostanie powiązany jedynie z lokalnym interfejsem pseudosieci. #include stdlib.h #include string.h #include openssl/bio.h #include openssl/ssl.h BIO *spc_listen(char *host, int port) { BIO *acpt = 0; int addr_length; char *addr; if (port 1 || port 65535) return 0; if (!host) host = * ; addr_length = strlen(host) + 6; /* 5 dla wartości typu int, 1 dla znaku dwukropka */ if (!(addr = (char *)malloc(addr_length + 1))) return 0; snprintf(addr, addr_length + 1, s: d , host, port); if ((acpt = BIO_new(BIO_s_accept())) != 0) { BIO_set_accept_port(acpt, addr); if (BIO_do_accept(acpt) = 0) { BIO_free_all(acpt); acpt = 0; } } free(addr); return acpt; } Wywołanie funkcji spc_listen() tworzy obiekt BIO, który posiada odpowiednie gniazdo pozo- stające w trybie nasłuchiwania. Nie występują tu żadne działania faktycznie związane z SSL, ponieważ połączenie SSL powstaje dopiero wówczas, gdy zostanie utworzone nowe połączenie gniazdowe. Wywołanie funkcji spc_listen() jest nieblokujące i natychmiast zwraca wynik. Kolejnym etapem jest wywołanie funkcji spc_accept() w celu zestawienia nowego gniazda oraz potencjalnego połączenia SSL między serwerem a zgłaszającym się klientem. Funkcja ta powinna być wywoływana w sposób powtarzalny w celu ciągłego przyjmowania połączeń, jednak należy pamiętać, że powoduje ona powstanie blokady, jeśli nie istnieją żadne połączenia oczekujące. Wywołanie funkcji spc_accept() zwraca nowy obiekt BIO, który jest połączeniem z nowym klientem lub wartość NULL, która określa, że w trakcie zestawiania połączenia wy- stąpił pewien błąd. Funkcja spc_accept() automatycznie tworzy obiekt SSL_CTX w ten sam sposób co funkcja spc_connect() (patrz receptura 9.1). Jednak ze względu na sposób działania funkcji spc_accept() (jest ona wywoływana w sposób powtarzalny przy użyciu tego samego nadrzędnego obiektu BIO dla przyjmowania nowych połączeń) funkcję spc_create_ssl() należy wywołać samodzielnie w celu utworzenia pojedynczego obiektu SSL_CTX, który będzie współużytkowany przez wszystkie akceptowane połączenia. Tworzenie serwera SSL | 487 BIO *spc_accept(BIO *parent, int ssl, spc_x509store_t *spc_store, SSL_CTX **ctx) { BIO *child = 0, *ssl_bio = 0; int our_ctx = 0; SSL *ssl_ptr = 0; if (BIO_do_accept(parent) = 0) return 0; if (!(child = BIO_pop(parent))) return 0; if (ssl) { if (*ctx) { CRYPTO_add( ((*ctx)- references), 1, CRYPTO_LOCK_SSL_CTX); if (spc_store spc_store != SSL_CTX_get_app_data(*ctx)) { SSL_CTX_set_cert_store(*ctx, spc_create_x509store(spc_store)); SSL_CTX_set_app_data(*ctx, spc_store); } } else { *ctx = spc_create_sslctx(spc_store); our_ctx = 1; } if (!(ssl_ptr = SSL_new(*ctx))) goto error_exit; SSL_set_bio(ssl_ptr, child, child); if (SSL_accept(ssl_ptr) = 0) goto error_exit; if (!(ssl_bio = BIO_new(BIO_f_ssl()))) goto error_exit; BIO_set_ssl(ssl_bio, ssl_ptr, 1); child = ssl_bio; ssl_bio = 0; } return child; error_exit: if (child) BIO_free_all(child); if (ssl_bio) BIO_free_all(ssl_bio); if (ssl_ptr) SSL_free(ssl_ptr); if (*ctx) SSL_CTX_free(*ctx); if (our_ctx) *ctx = 0; return 0; } Kiedy zostaje przyjęte nowe połączenie gniazdowe, wywoływana jest funkcja SSL_accept() w celu przeprowadzenia procesu uzgadniania protokołu SSL. Certyfikat serwera (być może wraz z certyfikatami nadrzędnymi w łańcuchu certyfikatów, w zależności od sposobu skon- figurowania obiektu spc_x509store_t) zostaje przesłany, a jeśli certyfikat klienta jest potrzebny i zostanie pobrany, następuje jego weryfikacja. W razie zakończenia powodzeniem operacji uzgadniania zwrócony obiekt BIO zachowuje się dokładnie tak samo jak obiekt BIO zwracany przez funkcję spc_connect() lub spc_connect_ssl(). Bez względu na to, czy nowe połączenie zostało udanie zestawione, nasłuchujący obiekt BIO przekazany do funkcji SSL_accept() bę- dzie gotowy do akceptacji następnego połączenia dla kolejnego wywołania tej funkcji. Zobacz również Receptury 9.1, 10.5. 488 | Rozdział 9. Komunikacja sieciowa 9.3. Używanie mechanizmu buforowania sesji w celu zwiększenia wydajności serwerów SSL Używanie mechanizmu buforowania sesji w celu zwiększenia wydajności serwerów SSL Problem Posiadamy parę klient-serwer, w której komunikacja odbywa się przy wykorzystaniu protoko- łu SSL. Ten sam klient często tworzy kilka połączeń z tym samym serwerem w krótkim cza- sie. Potrzebny jest sposób przyspieszenia procesu ponownego przyłączania klienta do serwera bez naruszenia schematu zabezpieczeń. Rozwiązanie Pojęcia sesji SSL oraz połączenia SSL często bywają mylone lub używane zamiennie, choć w rze- czywistości są to dwie różne rzeczy. Sesja SSL to zbiór parametrów i kluczy szyfrowania utwo- rzonych w wyniku przeprowadzenia procesu uzgadniania protokołu SSL. Połączenie SSL to aktywna konwersacja między dwoma węzłami korzystającymi z sesji SSL. W normalnej sytu- acji, kiedy zostanie zestawione połączenie SSL, proces uzgadniania służy do wynegocjowania parametrów, które stają się sesją. To właśnie ów proces negocjowania sprawia, że tworzenie połączeń SSL jest tak kosztowne. Na szczęście istnieje możliwość buforowania sesji. Kiedy klient połączy się z serwerem i z powo- dzeniem zakończy normalny proces uzgadniania, zarówno klient, jak i serwer posiadają do- stęp do parametrów sesji, tak więc następnym razem, kiedy klient nawiąże połączenie z serwe- rem, może po prostu ponownie wykorzystać tę samą sesję, co pozwala uniknąć narzutu związanego z negocjowaniem nowych parametrów oraz kluczy szyfrowania. Analiza Buforowanie sesji zwykle nie jest domyślnie aktywowane, jednak jest to dość proste zadanie do wykonania. OpenSSL wykonuje większość działań za użytkownika, choć niemal wszystkie ustawienia domyślne można zmodyfikować (można, na przykład, utworzyć własny mecha- nizm buforowania po stronie serwera). Domyślnie OpenSSL używa mechanizmu buforowania wykorzystującego pamięć fizyczną, jednak jeśli ma być buforowana duża liczba sesji lub jeśli sesje mają pozostawać trwałe między przeładowaniami systemu, można wykorzystać pewien mechanizm buforowania dyskowego. Większość zadań związanych z aktywowaniem buforowania sesji należy wykonać po stronie serwera, jednak nie jest ich zbyt dużo. 1. Ustawiamy kontekst identyfikatora sesji. Jego celem jest zapewnienie, że sesja będzie wykorzystywana ponownie w tym samym celu, w jakim została utworzona. Przykładowo, sesja utworzona dla serwera WWW SSL nie powinna automatycznie uwzględniać połączeń SSL serwera FTP. Kontekstem identyfikatora sesji mogą być dowolne dane binarne liczące maksymalnie 32 bajty długości. Nie ma ograniczeń co do postaci tych danych oprócz tego, że powinny one być unikatowe w zakresie usług świadczonych przez serwer — nie do zaakceptowania jest sytuacja, w której serwer przyjmuje sesje innych serwerów. Używanie mechanizmu buforowania sesji w celu zwiększenia wydajności serwerów SSL | 489 2. Ustawiamy czas ważności sesji. W przypadku OpenSSL domyślnie jest to 300 sekund, co w przypadku większości aplikacji jest wartością rozsądną. Kiedy sesja traci ważność, nie zostaje od razu usunięta z bufora serwera, jednak nie będzie akceptowana w przypadku prezentacji przez klienta. Jeżeli klient podejmie próbę użycia wygasłej sesji, serwer usunie ją ze swojej pamięci podręcznej. 3. Ustawiamy tryb buforowania. OpenSSL obsługuje wiele możliwych opcji trybów okre- ślanych za pomocą masek bitowych: SSL_SESS_CACHE_OFF Ustawienie tego trybu wyłącza mechanizm buforowania sesji. Jeżeli chce się to zrobić, wy- starczy określić tylko ten znacznik — nie ma potrzeby ustawiania kontekstu identyfikatora sesji ani czasu ważności sesji. SSL_SESS_CACHE_SERVER Ustawienie tego trybu powoduje, że sesje generowane przez serwer są buforowane. Jest to tryb domyślny i powinien być uwzględniany zawsze, kiedy określa się inne opisywane tu znaczniki, oprócz SSL_SESS_CACHE_OFF. SSL_SESS_CACHE_NO_AUTO_CLEAR Domyślnie bufor sesji jest sprawdzany pod względem wygasłych wpisów co każde 255 zestawionych połączeń. Niekiedy może to powodować niepożądane opóźnienie i może się wówczas okazać przydatne dezaktywowanie takiego automatycznego czyszczenia bufo- ra. W razie ustawienia tego trybu należy zapewnić, aby okresowo była wywoływana funkcja SSL_CTX_flush_sessions(). SSL_SESS_CACHE_NO_INTERNAL_LOOKUP Jeżeli chce się zastąpić wewnętrzny mechanizm buforowania OpenSSL własnym, należy ustawić ten tryb. W celu aktywowania mechanizmu buforowania po stronie serwera można używać opisanych poniżej funkcji pomocniczych. Jeżeli zamierza się ich używać wraz z funkcjami serwera SSL przedstawionymi w recepturze 9.2, należy samodzielnie utworzyć obiekt SSL_CTX przy użyciu funkcji spc_create_sslctx(). Następnie należy wywołać funkcję spc_enable_sessions(), używając obiektu SSL_CTX i przekazać go do funkcji spc_accept(), tak aby nie został auto- matycznie utworzony nowy taki obiekt. Bez względu na to, czy się aktywuje mechanizm bu- forowania sesji czy nie, dobrym pomysłem jest utworzenie własnego obiektu SSL_CTX przed wywołaniem funkcji spc_accept(), tak aby nowy taki obiekt nie był tworzony dla każdego połączenia klienckiego. #include openssl/bio.h #include openssl/ssl.h void spc_enable_sessions(SSL_CTX *ctx, unsigned char *id, unsigned int id_len, long timeout, int mode) { SSL_CTX_set_session_id_context(ctx, id, id_len); SSL_CTX_set_timeout(ctx, timeout); SSL_CTX_set_session_cache_mode(ctx, mode); } Aktywowanie buforowania sesji po stronie klienta jest jeszcze prostsze. Wszystko, czego po- trzeba, to ustawienie obiektu SSL_SESSION w obiekcie SSL_CTX przed faktycznym zestawieniem połączenia. Poniższa funkcja spc_reconnect() stanowi reimplementację funkcji spc_connect_ ssl() poprzez wprowadzenie zmian potrzebnych do aktywowania buforowania sesji po stro- nie klienta. 490 | Rozdział 9. Komunikacja sieciowa BIO *spc_reconnect(char *host, int port, SSL_SESSION *session, spc_x509store_t *spc_store, SSL_CTX **ctx) { BIO *conn = 0; int our_ctx = 0; SSL *ssl_ptr; if (*ctx) { CRYPTO_add( ((*ctx)- references), 1, CRYPTO_LOCK_SSL_CTX); if (spc_store spc_store != SSL_CTX_get_app_data(*ctx)) { SSL_CTX_set_cert_store(*ctx, spc_create_x509store(spc_store)); SSL_CTX_set_app_data(*ctx, spc_store); } } else { *ctx = spc_create_sslctx(spc_store); our_ctx = 1; } if (!(conn = BIO_new_ssl_connect(*ctx))) goto error_exit; BIO_set_conn_hostname(conn, host); BIO_set_conn_int_port(conn, port); if (session) { BIO_get_ssl(conn, ssl_ptr); SSL_set_session(ssl_ptr, session); } if (BIO_do_connect(conn) = 0) goto error_exit; if (!our_ctx) SSL_CTX_free(*ctx); if (session) SSL_SESSION_free(session); return conn; error_exit: if (conn) BIO_free_all(conn); if (*ctx) SSL_CTX_free(*ctx); if (our_ctx) *ctx = 0; return 0; } Zestawienie połączenia SSL w roli klienta może być równie proste jak ustawienie obiektu SSL_SESSION w obiekcie SSL_CTX, jednak pojawia się pytanie, skąd się wziął ów tajemniczy obiekt SSL_SESSION. W momencie zestawiania połączenia OpenSSL tworzy obiekt sesji SSL i ukrywa go w obiekcie SSL, który normalnie jest ukryty w obiekcie BIO zwracanym przez funkcję spc_connect_ssl(). Można go pobrać, wywołując funkcję spc_getsession(). SSL_SESSION *spc_getsession(BIO *conn) { SSL *ssl_ptr; BIO_get_ssl(conn, ssl_ptr); if (!ssl_ptr) return 0; return SSL_get1_session(ssl_ptr); } Obiekt SSL_SESSION zwracany przez funkcję spc_getsession() posiada zwiększony licznik odwołań, tak więc należy zapewnić wywołanie w pewnym momencie funkcji SSL_SESSION_ free()w celu zwolnienia tego odwołania. Obiekt SSL_SESSION można otrzymać od razu po udanym zestawieniu połączenia, jednak ponieważ wartość może ulec zmianie między momen- tem zestawienia połączenia a momentem jego zakończenia ze względu na proces renegocjacji, obiekt SSL_SESSION zawsze należy pobierać tuż przed zakończeniem połączenia. W ten spo- sób można zapewnić sobie posiadanie najnowszego obiektu sesji. Używanie mechanizmu buforowania sesji w celu zwiększenia wydajności serwerów SSL | 491 Zobacz również Receptura 9.2. 9.4. Zabezpieczanie komunikacji sieciowej na platformie Windows przy użyciu interfejsu WinInet API Zabezpieczanie komunikacji sieciowej na platformie Windows przy użyciu interfejsu WinInet API Problem Opracowujemy program na platformie Windows, który musi łączyć się z serwerem HTTP przy użyciu SSL. Chcemy użyć interfejsu firmy Microsoft WinInet API w celu prowadzenia komunikacji z tym serwerem. Rozwiązanie Interfejs WinInet API firmy Microsoft został wprowadzony wraz z przeglądarką Internet Explorer 3.0. Oferuje on zestaw funkcji pozwalających programom na łatwe uzyskiwanie dostępu do serwerów FTP, Gopher, HTTP oraz HTTPS. W przypadku tych ostatnich szczegóły użycia protokołu SSL są ukryte przed programistą, co pozwala mu skoncentrować się na danych, któ- re muszą być wymienione, a nie samych szczegółach protokołu. Analiza WinInet API to bogaty interfejs, który znacznie ułatwia klientom interakcję z serwerami FTP, Gopher, HTTP oraz HTTPS. Jednak podobnie jak w przypadku większości innych interfejsów API systemu Windows wciąż wymagane jest pisanie sporych porcji kodu. Ze względu na bogactwo dostępnych opcji poniżej nie zostanie zaprezentowany pełny przykład działającego kodu. Zamiast tego zostanie omówiony sam interfejs oraz zaprezentowane będą przykłady kodu dla tych jego części, które są interesujące z punktu widzenia kwestii bezpieczeństwa. Autorzy zachęcają Czytelnika do sięgnięcia po dokumentację firmy Microsoft poświęconą temu interfejsowi w celu zapoznania się z jego możliwościami. Jeżeli zamierza się zestawić połączenie z serwerem sieciowym przy użyciu protokołu SSL z wykorzystaniem interfejsu WinInet, pierwszą rzeczą, jaką należy zrobić, jest utworzenie sesji internetowej poprzez wywołanie funkcji InternetOpen(). Inicjalizuje ona i zwraca uchwyt do obiektu wymaganego do faktycznego zestawienia połączenia. Należy zadbać o takie szczegóły jak prezentacja użytkownikowi interfejsu użytkownika mechanizmu łączenia ko- mutowanego, jeżeli nie jest on jeszcze podłączony do internetu, a system został tak skonfigu- rowany. Chociaż pojedyncza aplikacja może wykonać nieograniczoną liczbę wywołań funkcji InternetOpen(), zwykle wymagane jest tylko jednokrotne jej wywołanie. Zwracany uchwyt można używać wielokrotnie. #include windows.h #include wininet.h 492 | Rozdział 9. Komunikacja sieciowa HINTERNET hInternetSession; LPSTR lpszAgent = C i C++. Bezpieczne programowanie. Receptury, receptura 9.4 ; DWORD dwAccessType = INTERNET_OPEN_TYPE_PROXY; LPSTR lpszProxyName = 0; LPSTR lpszProxyBypass = 0; DWORD dwFlags = 0; hInternetSession = InternetOpen(lpszAgent, dwAccessType, lpszProxyName, lpszProxyBypass, dwFlags); Jeżeli jako wartość dwAccessType ustawi się INTERNET_OPEN_TYPE_PROXY, lpszName — 0, zaś lpszProxyBypass — 0, użyte zostaną domyślne ustawienia systemowe dla protokołu HTTP. Jeżeli system skonfigurowano tak, aby korzystał z serwera pośredniczącego, zostanie on uży- ty zgodnie z wymaganiami. Argument lpszAgent jest przekazywany do serwerów jako ciąg znaków agenta HTTP klienta. Może to być dowolna wartość lub ten sam ciąg znaków, który określona przeglądarka internetowa przesyła do serwera sieciowego w momencie zgłoszenia żądania. Kolejnym etapem jest połączenie się z serwerem. Dokonuje się tego, wywołując funkcję In- ternetConnect(), która zwraca nowy uchwyt do obiektu przechowującego wszystkie od- powiednie informacje o połączeniu. Dwa narzucające się wymagane parametry dla tej funkcji to nazwa serwera, z którym ma zostać nawiązane połączenie, oraz port połączenia. Nazwę serwera można podać w formie nazwy hosta lub adresu IP z rozdzielonymi kropkami wartościami dziesiętnymi. Port można określić jako liczbę lub użyć stałej INTERNET_DEFAULT_ HTTPS_PORT w celu połączenia się z domyślnym portem SSL o numerze 443. HINTERNET hConnection; LPSTR lpszServerName = www.helion.pl ; INTERNET_PORT nServerPort = INTERNET_DEFAULT_HTTPS_PORT; LPSTR lpszUsername = 0; LPSTR lpszPassword = 0; DWORD dwService = INTERNET_SERVICE_HTTP; DWORD dwFlags = 0; DWORD dwContext = 0; hConnection = InternetConnect(hInternetSession, lpszServerName, nServerPort, lpszUsername, lpszPassword, dwService, dwFlags, dwContext); Wywołanie funkcji InternetConnect() zestawia połączenie z serwerem zdalnym. Jeżeli próba połączenia zakończy się z pewnego powodu niepowodzeniem, zwracaną wartością jest NULL, zaś kod błędu można pobrać przy użyciu funkcji GetLastError(). W przeciwnym razie zostaje zwrócony nowy uchwyt do obiektu. Jeżeli wymagane jest zgłoszenie kilku żądań względem tego samego serwera, należy używać tego samego uchwytu w celu uniknięcia narzutu związa- nego z tworzeniem wielu połączeń. Po zestawieniu połączenia z serwerem należy zbudować obiekt żądania. Obiekt ten jest kon- tenerem przechowującym różne informacje: zasób, którego będzie dotyczyć żądanie, nagłówki, które zostaną przesłane, zestaw znaczników, które określają zachowanie żądania, informacje nagłówkowe zwrócone przez serwer po przesłaniu żądania oraz inne. Nowy obiekt żądania konstruuje się, wywołując funkcję HttpOpenRequest(). HINTERNET hRequest; LPSTR lpszVerb = GET ; LPSTR lpszObjectName = / ; LPSTR lpszVersion = HTTP/1.1 ; LPSTR lpszReferer = 0; LPSTR lpszAcceptTypes = 0; Zabezpieczanie komunikacji sieciowej na platformie Windows przy użyciu interfejsu WinInet API | 493 DWORD dwFlags = INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; DWORD dwContext = 0; hRequest = HttpOpenRequest(hConnection, lpszVerb, lpszObjectName, lpszVersion, lpszReferer, lpszAcceptTypes, dwFlags, dwContext); Argument lpszVerb steruje typem żądania, które zostanie przesłane — może to być dowolne poprawne żądanie protokołu HTTP, takie jak GET lub POST. Argument lpszObjectName określa zasoby, których dotyczy żądanie, i zazwyczaj stanowi część adresu URL występującą po na- zwie serwera, czyli rozpoczynającą się od znaku ukośnika i kończącą przed ciągiem zapytania (przed znakiem pytajnika). Określenie wartości argumentu lpszAcceptTypes jako 0 informuje serwer, że akceptowane są wszelkiego rodzaju dokumenty tekstowe. Jest to równoważne ty- powi MIME text/*. Najbardziej interesującym argumentem przekazywanym do funkcji HttpOpenRequest() jest dwFlags. Definiuje on wiele znaczników, ale tylko kilka z nich ma bezpośredni związek z użyciem protokołu HTTP poprzez SSL. INTERNET_FLAG_IGNORE_CERT_CN_INVALID W normalnej sytuacji jako element procesu weryfikacji certyfikatu serwera WinInet spraw- dza, czy nazwa hosta jest zawarta w polu commonName lub rozszerzeniu subjectAltName certyfikatu. W razie określenia tego znacznika sprawdzenie nazwy hosta nie odbywa się (w recepturach 10.4 oraz 10.8 omówiono znaczenie przeprowadzania sprawdzeń nazw hostów w przypadku certyfikatów). INTERNET_FLAG_IGNORE_CERT_DATE_INVALID Istotną częścią procesu weryfikacji poprawności certyfikatu X.509 jest sprawdzenie dat jego obowiązywania. Jeżeli bieżąca data jest datą spoza poprawnego zakresu dat certyfikatu, powinien on być traktowany jako niepoprawny. W razie określenia tego znacznika sprawdzenie poprawności dat certyfikatu nie odbywa się. Opcja ta nie powinna być nigdy używana w przypadku finalnej wersji danego produktu. INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP W razie określenia tego znacznika, kiedy serwer podejmuje próbę przekierowania klienta pod adres niewykorzystujący protokołu SSL, przekierowanie takie zostanie zignorowane. Zawsze należy uwzględniać ten znacznik, tak aby zapewnić, że dane, które powinny być chronione, nie będą przesyłane w postaci jawnej. INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS W razie określenia tego znacznika, kiedy serwer podejmuje próbę przekierowania klienta pod adres wykorzystujący protokół SSL, przekierowanie takie zostanie zignorowane. Jeżeli można oczekiwać, że komunikacja będzie się odbywała tylko w ramach serwerów pozosta- jących pod naszą kontrolą, można ją pominąć. W przeciwnym wypadku warto wziąć pod uwagę jej uwzględnienie, aby zapobiec przekierowaniom do miejsc innych niż oczekiwane. INTERNET_FLAG_SECURE Jest to bardzo istotny znacznik. Jego określenie powoduje aktywowanie użycia protokołu SSL w ramach połączenia. Bez niego SSL nie jest używany i wszystkie dane są przesyłane w postaci jawnej. Oczywiście należy go uwzględniać. Po skonstruowaniu obiektu żądania należy przesłać żądanie do serwera. Robi się to, wywo- łując funkcję HttpSendRequest() z obiektem żądania. Można dołączyć dodatkowe nagłówki, 494 | Rozdział 9. Komunikacja sieciowa jak również opcjonalne dane przesyłane po nagłówkach. Takie dane przesyła się w przypadku wykonywania operacji POST. Dodatkowe nagłówki i opcjonalne dane określa się jako ciągi zna- ków oraz długości tych ciągów. BOOL bResult; LPSTR lpszHeaders = 0; DWORD dwHeadersLength = 0; LPSTR lpszOptional = 0; DWORD dwOptionalLength = 0; bResult = HttpSendRequest(hRequest, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength); Po przesłaniu żądania można odebrać odpowiedź serwera. W ramach procesu przesyłania żą- dania WinInet pobiera nagłówki odpowiedzi z serwera. Informacje dotyczące odpowiedzi można pobrać przy użyciu funkcji HttpQueryInfo(). Pełną listę informacji, jakie mogą być dostępne, można znaleźć w dokumentacji interfejsu WinInet, jednak dla naszych celów istotna jest jedynie długość treści. Serwer nie musi odsyłać nagłówka długości treści w ramach swojej odpowiedzi, tak więc musimy zapewnić sobie możliwość obsłużenia również takiej sytuacji, w której nie zostanie on przesłany. Dane odpowiedzi przesyłane przez serwer po jej nagłów- kach można pobrać, wywołując funkcję InternetReadFile() tyle razy, ile jest to konieczne w celu pobrania wszystkich danych. DWORD dwContentLength, dwIndex, dwInfoLevel; DWORD dwBufferLength, dwNumberOfBytesRead, dwNumberOfBytesToRead; LPVOID lpBuffer, lpFullBuffer, lpvBuffer; dwInfoLevel = HTTP_QUERY_CONTENT_LENGTH; lpvBuffer = (LPVOID) dwContentLength; dwBufferLength = sizeof(dwContentLength); dwIndex = 0; HttpQueryInfo(hRequest, dwInfoLevel, lpvBuffer, dwBufferLength, dwIndex); if (dwIndex != ERROR_HTTP_HEADER_NOT_FOUND) { /* Długość treści jest znana. Odczytujemy tylko taką ilość danych */ lpBuffer = GlobalAlloc(GMEM_FIXED, dwContentLength); InternetReadFile(hRequest, lpBuffer, dwContentLength, dwNumberOfBytesRead); } else { /* Długość treści nie jest znana. Odczytujemy do momentu napotkania znaku EOF */ dwContentLength = 0; dwNumberOfBytesToRead = 4096; lpFullBuffer = lpBuffer = GlobalAlloc(GMEM_FIXED, dwNumberOfBytesToRead); while (InternetReadFile(hRequest, lpBuffer, dwNumberOfBytesToRead, dwNumberOfBytesRead)) { dwContentLength += dwNumberOfBytesRead; if (dwNumberOfBytesRead != dwNumberOfBytesToRead) break; lpFullBuffer = GlobalReAlloc(lpFullBuffer, dwContentLength + dwNumberOfBytesToRead, 0); lpBuffer = (LPVOID)((LPBYTE)lpFullBuffer + dwContentLength); } lpFullBuffer = lpBuffer = GlobalReAlloc(lpFullBuffer, dwContentLength, 0); } Po odczytaniu danych za pomocą funkcji InternetReadFile() zmienna lpBuffer będzie zawierała treść odpowiedzi serwera, zaś zmienna dwContentLength będzie zawierała liczbę bajtów zawartych w buforze odpowiedzi. W tym momencie żądanie jest zakończone i obiekt odpowiedzi należy zniszczyć, wywołując funkcję InternetCloseHandle(). Jeżeli są wyma- gane dodatkowe żądania względem tego samego połączenia, można utworzyć nowy obiekt żądania i użyć tego samego uchwytu połączenia pochodzącego z wywołania funkcji Internet- Connect(). Jeśli dane połączenie nie będzie już potrzebne do przesyłania żądań, należy użyć Zabezpieczanie komunikacji sieciowej na platformie Windows przy użyciu interfejsu WinInet API | 495 funkcji InternetCloseHandle() w celu zamknięcia połączenia. Wreszcie, kiedy obiekt sesji internetowej utworzonej przez funkcję InternetConnect()nie jest już używany, należy wywo- łać funkcję InternetCloseHandle() w celu usunięcia także tego obiektu. InternetCloseHandle(hRequest); InternetCloseHandle(hConnection); InternetCloseHandle(hInternetSession); Zobacz również Receptury 10.4, 10.8. 9.5. Aktywowanie protokołu SSL bez modyfikowania kodu źródłowego Aktywowanie protokołu SSL bez modyfikowania kodu źródłowego Problem Posiadamy klienta lub serwer, który nie obsługuje protokołu SSL, a chcemy zapewnić obsługę SSL bez konieczności modyfikowania kodu źródłowego. Rozwiązanie Stunnel to program wykorzystujący pakiet OpenSSL w celu tworzenia tuneli SSL między klientami a serwerami, które nie zapewniają wewnętrznej obsługi protokołu SSL. W czasie pisania niniejszej książki jego najnowsza wersja nosiła numer 4.04 i była dostępna dla syste- mów Unix oraz Windows pod adresem http://www.stunnel.org. W przypadku serwerów nasłu- chuje on na innym gnieździe połączeń SSL i przekazuje dane dwukierunkowo do prawdzi- wego serwera poprzez połączenie nieobsługujące SSL. Klienty obsługujące SSL mogą wówczas łączyć się przez port nasłuchu programu Stunnel i komunikować z serwerem, który nie ob- sługuje SSL. W przypadku klientów nasłuch odbywa się na gnieździe obsługującym połącze- nia niewykorzystujące SSL, a dane są przekazywane dwukierunkowo do serwera w ramach połączenia chronionego przez SSL. Stunnel powstał kilka lat temu i początkowo wykorzystywał przełączniki określane w wierszu poleceń w celu sterowania swoim działaniem. Zmieniło się to dopiero od wersji 4.00. Obecnie Stunnel wykorzystuje w tym celu plik konfiguracyjny i wszystkie wcześniej obsługiwane przełączniki wiersza poleceń zostały usunięte. Niniejsza receptura odnosi się do wersji 4.04. Analiza Chociaż w niniejszej recepturze nie zawarto żadnego kodu, znalazła się ona w książce dlate- go, że autorzy sądzą, iż Stunnel to narzędzie warte omówienia, szczególnie w sytuacji, gdy opracowuje się klienty i serwery wykorzystujące protokół SSL. Podjęcie próby opracowania od podstaw i usunięcia błędów z oprogramowania klientów i serwerów wykorzystujących SSL może okazać się bardzo frustrującym przeżyciem, szczególnie jeśli nie posiada się doświadcze- nia w zakresie programowania z użyciem SSL. Program Stunnel może pomóc w usuwaniu błę- dów z kodu SSL. 496 | Rozdział 9. Komunikacja sieciowa Plik konfiguracyjny programu Stunnel jest podzielony na sekcje. Każda z nich zawiera zestaw kluczy, zaś każdy klucz posiada związaną z nim wartość. Sekcje i klucze są nazwane i wielkość liter nie ma znaczenia. Plik konfiguracyjny jest analizowany od początku, sekcje są rozdzie- lone wierszami zawierającymi ich nazwy ujęte w nawiasy kwadratowe. Pozostałe wiersze za- wierają pary klucz-wartość, które należą do sekcji bieżącej. Ponadto przed pierwszą nazwaną sekcją występuje opcjonalna globalna sekcja nienazwana. Klucze i wartości oddziela znak równości (=). Komentarze mogą się rozpoczynać tylko od początku wiersza (dopuszczalne jest występo- wanie najpierw znaków odstępu) i ich pierwszym znakiem jest krzyżyk (#); cały taki wiersz jest traktowany jako komentarz. Wszelkie wiodące lub kończące znaki odstępu otaczające klucz lub wartość są pomijane. Wszelkie inne znaki odstępu mają znaczenie, w tym wiodące lub kończące znaki odstępu otaczające nazwę sekcji (występującą w nawiasach kwadratowych). Przykładowo, zapis [ moja_sekcja ] nie jest równoważny zapisowi [moja_sekcja]. Doku- mentacja dołączona do programu Stunnel szczegółowo opisuje obsługiwane klucze, więc tu- taj pominiemy ich opis. Jedną z przydatnych cech pliku konfiguracyjnego w porównaniu ze starym interfejsem wiersza poleceń jest to, że każda sekcja definiuje albo klienta, albo serwer, tak więc pojedyncza in- stancja programu Stunnel może być używana w celu uruchamiania wielu klientów lub ser- werów. Jeżeli chce się uruchamiać zarówno klienty, jak i serwery, potrzebne jest uruchomienie dwóch instancji programu Stunnel, ponieważ znacznik określający, w jakim trybie ma on działać, jest opcją globalną. W przypadku interfejsu wiersza poleceń wymaganych było wiele instancji programu — po jednej dla każdego klienta lub serwera, którego chciało się urucho- mić. Stąd, w przypadku chęci używania Stunnel dla połączeń serwerów POP3, IMAP oraz SMTP, należało uruchomić trzy instancje programu. Nazwa każdej sekcji definiuje nazwę usługi, która będzie używana z mechanizmem opakowa- nia protokołu TCP oraz w celach rejestrowania. Zarówno w przypadku klientów, jak i serwerów należy określić klucze accept oraz connect. Klucz accept określa port, na którym Stunnel będzie nasłuchiwać połączeń przychodzących, zaś klucz connect określa port, którym Stunnel będzie podejmował próby łączenia się w przypadku połączeń wychodzących. Klucze te muszą co najmniej określać numer portu, ale mogą również opcjonalnie zawierać nazwę hosta lub adres IP. W celu ich uwzględnienia należy poprzedzić numer portu nazwą hosta lub adresem IP i rozdzielić je znakiem dwukropka (:). Tryb działania programu Stunnel można aktywować na dwa sposoby. Tryb serwera W celu aktywowania trybu serwera należy ustawić klucz opcji globalnej client na war- tość no. W czasie działania w trybie serwera Stunnel oczekuje, że połączenia przychodzące będą się komunikować z użyciem protokołu SSL, zaś połączenia wychodzące będą obsłu- giwane bez niego. Należy również ustawić dwie opcje globalne cert oraz key na nazwy plików zawierających certyfikat oraz używany klucz. Tryb klienta W celu aktywowania trybu klienta należy ustawić klucz opcji globalnej client na wartość yes. W tym trybie Stunnel oczekuje, że połączenia przychodzące nie będą obsługiwać protokołu SSL, zaś połączenia wychodzące będą szyfrowane za pomocą tego protokołu. Można również określić certyfikat i klucz, ale nie jest to wymagane. Aktywowanie protokołu SSL bez modyfikowania kodu źródłowego | 497 Poniższy przykład powoduje uruchomienie dwóch serwerów. Pierwszy z nich to IMAP wyko- rzystujący SSL, który nasłuchuje połączeń SSL na porcie 993 i przekierowuje ruch bez użycia SSL do połączenia na porcie 143. Drugim jest serwer POP3 wykorzystujący SSL, który nasłu- chuje połączeń SSL tylko na porcie 995 interfejsu maszyny lokalnej (127.0.0.1). Połączenia wy- chodzące są przeprowadzane za pomocą portu 110 w ramach interfejsu lokalnego. client = no cert = /home/mmessier/ssl/servercert.pem key = /home/mmessier/ssl/serverkey.pem [impas] accept = 993 connect = 143 [pop3] accept = localhost:995 connect = localhost:110 W poniższym przykładzie Stunnel działa w trybie klienta. Nasłuchuje połączeń przychodzących w ramach interfejsu lokalnego na porcie 25 i przekierowuje ruch do portu 465 na serwerze o adresie smtp.secureprogramming.com. Przykład ten może być przydatny dla klientów poczty elektronicznej, które nie obsługują protokołu SMTP szyfrowanego za pomocą SSL. client = yes [smtp] accept = localhost:25 connect = smtp.secureprogramming.com Zobacz również Witryna internetowa programu Stunnel: http://www.stunnel.com. 9.6. Używanie szyfrowania standardu Kerberos Używanie szyfrowania standardu Kerberos Problem Musimy użyć szyfrowania w kodzie, który wykorzystuje już standard uwierzytelniania Kerberos. Rozwiązanie Kerberos to w głównej mierze usługa uwierzytelniająca stosowana w przypadku usług siecio- wych. Efektem ubocznym wymagań związanych z przeprowadzaniem procesu uwierzytelnia- nia jest to, że Kerberos oferuje interfejs API dla szyfrowania i deszyfrowania, aczkolwiek liczba obsługiwanych szyfrów jest znacznie mniejsza niż ma to miejsce w przypadku innych protokołów kryptograficznych. Proces uwierzytelniania powoduje utworzenie kryptograficz- nie silnego klucza sesji, który może być używany jako klucz szyfrowania. Bieżąca receptura może być stosowana w przypadku systemów Unix oraz Windows z im- plementacjami standardu Kerbero
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

C i C++. Bezpieczne programowanie. Receptury
Autor:
,

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: