Czym jest ASP.NET Core?

ASP.NET został zaprezentowany 15 lat temu. Od tego czasu był używany przez miliony programistów do tworzenia aplikacji internetowych. Prawie każdego roku Microsoft dodaje nowe możliwości i prezentuje funkcjonalności do tej pory niedostępne. Wszystkie te działania pozwalają na tworzenie lepszego i coraz bardziej przystępnego frameworka.

Podążając za trendami i chcąc przekonać do siebie coraz większą grupę programistów, Microsoft, w 2016 roku zaprezentował ASP.NET Core. Jest darmowym, wieloplatformowym frameworkiem webowym. Działa na systemach operacyjnych takich jak: Windows, Mac, Linux czy Ubuntu.

Dlaczego potrzebujemy ASP.NET Core?

Odpowiedzmy sobie teraz na pytania: do czego jest nam potrzebny ASP.NET Core, czemu powinniśmy go używać i w czym jest lepszy w porównaniu do ASP.NET? W dzisiejszych czasach aplikacje internetowe nie ograniczają się tylko do komputerów stacjonarnych i laptopów. Przeniosły się do inteligentych urządzeń, działają w chmurze, równie często są przeglądane za pomocą urządzeń mobilnych. Z obsługą tak wielu systemów i platform świetnie radzi sobie ASP.NET Core.

Zasadniczo, ASP.NET Core nie zależy już od biblioteki system.web.dll. Framework ma wbudowany manager pakietów NuGet, który pozwala zoptymalizować rozmiar aplikacji, wydajność czy bezpieczeństwo. Dodatkowo takie podejście pozwala na szybki rozwój a nawet przejście na tzw. motyw: „płać-za-to-czego-używasz”.

Poniżej niektóre z jego kluczowych funkcjonalności:

  • Open Source: darmowy i dostępny dla wszystkich framework;
  • Kompatybilność: z platformą .NET, Mono oraz Xamarin;
  • łatwość wdrażania nowych aplikacji;
  • lekkość i modułowość;
  • obsługa wielu platform;
  • obsługa IIS lub tzw. 'self-hosting'.

.NET Framework vs .NET Core

.NET to stabilna platform obsługująca miliony aplikacji internetowych od wielu lat. Jest to framework gotowy do użycia z wieloma, wstęnie załadowanymi bibliotekami. Niestety ma też swoje wady: obsługuje tylko system operacyjny Windows a duża kolekcja interfejsów API spowalnia wydajność.

Z kolei .NET Core jest lżejszy od platformy .NET, ponieważ nie zawiera żadnego pakietu API. Użytkownik korzystając z NuGet może przygotować aplikację “skrojoną na miarę”.

Uruchomienie kodu w .NET Core jest naprawdę proste. Wystarczy zainstalować SDK -> zainicjować kod -> uruchomić aplikację -> Gotowe!

Warto mieć na uwadzę fakt, że nie jest to pierwszy artykuł o .NET Core - nie będzie tutaj prostego przykładu wspomnianego powyżej. W tym celu proszę przeczytać jeden z poprzednich artykułów. My od razu przechodzimy do bezpieczeństwa danych.

Bezpieczeństwo aplikacji Webowej

Bezpieczenstwo

Walka o zabezpieczenie aplikacji internetowej i jej danych to niekończąca się walka. Co jakiś czas możemy usłyszeć o kradzieży danych logowania, bazie danych milionów osób czy haseł do ich kont. W tej dziedzienie jednak ciągle dochodzi do postępu i trzeba być świadomym tych zmian. Nie dotyczy to tylko framework’a o którym rozmawiamy ale np. biblioteki JQuery - z każdą kolejną wersją jest mniej podatna na różnego typu ataki. Warto być na bieżącą z tą tabelą: CVEdetails.com

Wymagania dotyczące bezpieczeństwa danych

Wymagania bezpieczeństwa
Oczekiwania związane z bezpieczeństwem danych są niezwykle proste – moje dane powinny być bezpieczne w momencie przesyłaniach ich pomiedzy serwerami, nawet jeżeli dane połączenie jest niezaufane. Oznacza to, że obie strony powinny mieć pewność, że dane są nietknięte.

Z uwagi na technikę modularnego przygotowywania aplikacji, poszczególne komponenty są później używane w dowolnym miejscu, niezależnie od systemu. W takim wypadku należy się upewnić, że użycie tych modułów nie doprowadzi do niepoprawnego zapisu/odczytu danych. Składniki odpowiedzialne za szyfrowanie danych, tj. ścieżki czy uprawnienia dostęp, dane przechowywane po stronie serwera, nie powinny być przekazywane klientom niezaufanym.

Ujmując to w jednym zdaniu: projekt bezpieczeństwa danych powinien być autentyczny, bezpieczny, prosty w implementacji, izolowany od zwykłych operacji a logika rdzenia powinna być ukryta przed programistą.

Strategie bezpieczeństwa

Mając na uwadze wszystkie powyższe punkty, ASP.NET Core opracował poniższe strategie bezpieczeństwa:

  • opracuj system bezpieczeństwa danych, która wymaga mniej konfiguracji;
  • kluczowa logika zabezpieczeń nie powinna być ujawniana programistom, co więcej, programista nie powinien mieć nawet dostępu do surowego tekstu szyfrowania;
  • klucz szyfrowania powinien być zarządzany i chroniony przez sam system.

ASP.NET Core dostarcza API, które pomaga nam wdrożyć mechanizm szyfrowania i hashowania danych.

Czym jest API ochrony danych i kto może z niego korzystać?

Zasadniczo, API ochrony danych, tworzy klucz i zarządza nim w celu szyfrowania danych. Klucz ten może być używany przez 3 typy użytkowników:

  • Programiści aplikacji i framework’u, którzy nie chcą zagłębiać się w działanie API czy jego konfigurację. Potrzebują jedynie przekazać dane wejściowe oraz uzyskać dane wyjściowe;
  • Administratorzy systemu i programiści, którzy nie chcą przechowywać tajnego pliku i kluczy w ścieżce z ustawieniami domyślnymi. Dzięki temu nie ma możliwości łatwego wyświetlania lokalizacji tego pliku. Prostym przykładem jest folder danych tymczasowych (TEMP), który nie powinien być przechowywany w domyślnej ścieżce, tj. %AppData%. Wbudowany interfejs IDataProtectionBuilder, pozwala nam zmienić całą domyślną ścieżkę i ustawienia;
W celu ochrony danych, środowisko ASP.NET Core, obsługuje zarówno technikę szyfrowania (encryption) jak i miesznia znaków (hashing). Zanim przejdziemy do przykładu odpowiedźmy sobie na kilka pytania: czym jest szyfrowanie a czym mieszanie znaków, jak ich użycie wpływa na wzrost bezpieczeństwa naszych danych oraz jaka jest różnica pomiędzy tymi dwiema technikami?

Szyfrowanie vs funkcja mieszająca

Szyfrowanie to proces przekształcania danych w serie nieczytelnych i niesekwencyjnych znaków, które nie mają stałej długości. Odszyfrowanie tych danych odbywa się za pomocą specjalnego klucza. Szyfrowany ciąg znaków zwany jest szyfrogramem. Ten ciąg znaków może zostać utworzony a pomocą algorytmu szyfrującego oraz klucza szyfrującego. Popularne algorytmy szyfrowania to m.in. DES, Triple DES, AES, etc.

Z kolei ‘hashowanie’ polega na użyciu specjalnych algorytmów mieszających, które powodują utworzenie tzw. funkcji skrótu, która jest przechowywana np. w bazie danych. W tym przypadku nie mamy już możliwości przywrócenia oryginalnego tekstu. W przypadku ponownego wpisania hasła dojdzie do wygenerowania funkcji skrótu, której wartość będzie porównana z wartością przechowywaną w bazie danych. Niektóre popularne techniki haszowania to MD5, SHA, itd.

Przestrzeń nazw i klasy

Bezpieczenstwo
Microsoft.AspNetCore.DataProtection jest główną przestrzenią, która dostarcza API ochrony danych. Jeżeli chcesz używac API ale nie chcesz uczyć się o mechaniźmie skorzytaj z Microsoft.AspNetCore.DataProtection.Abstractions. Jeżeli chcesz korzystać z niestandardowych pakietów skorzystaj z Microsoft.AspNetCore.DataProtection.Extensions. Z kolei Microsoft.AspNetCore.Cryptography.KeyDerivation jest używana do mieszania haseł.

Jak używać API?

Zdjęcie! Korzytanie z API jest niezwykle proste. Wystarczyć zaimportować dwie przestrzenie nazw: AspNetCore.DataProtection oraz Extensions.DependencyInjection, a w celu samej ochrony użyć metody protect. Jak to ma jednak miejsce na tym blogu, najskuteczniejsza metoda nauki polega na własnoręcznym napisaniu kodu odpowiedzialnego za daną funkcjonalność. Przejdziemy przez dwa przykłady: szyfrowanie oraz hashowanie.

Szyfrowanie w ASP.NET Core

Pierwszym wymaganiem jest posiadania klucza szyfrującego. Tym nie musimy się martwić ponieważ jest on tworzony i obsługiwany przez sam interfejs API. Klucze są generowane z domyślną długością życia wynoszącą 90 dni a przechowywane w wydzielonej (odpowiedniej, bezpiecznej) lokalizacji. Utworzony klucz jest kluczem tymczasowym i służy do zabezpieczania danych w krótkim okresie. Do takich danych możemy zaliczyć query string, pliki cookie, etc.

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace WebApplication3
{
	public class Program
    {
        public static void Main(string[] args)
        {
            //BuildWebHost(args).Run();
			// usługa ochrony danych (przestrzen nazw: Microsoft.Extensions.DependencyInjection
			var serviceCollection = new ServiceCollection();
			// dodajemy usługę ochrony
			serviceCollection.AddDataProtection();
			var serviceProvider = serviceCollection.BuildServiceProvider();
			// utworzenie instancji klasy za pomocą metody CreateInstance
			var instance = ActivatorUtilities.CreateInstance<ProviderClass>(serviceProvider);
			instance.getPutOut();
		}
		public class ProviderClass
		{
			IDataProtector _iPro;
			// parametr 'provider' jest dostarczony przez Dependency Injection
			public ProviderClass(IDataProtectionProvider provider)
			{
				_iPro = provider.CreateProtector("ProviderClassText");
			}
			public void getPutOut()
			{
				string input = "Witaj Świecie";
				// zaszyfrowany łańcuch znaków
				string protectedString = _iPro.Protect(input);
				// odszyfrowany łańcuch znaków
				string unProtectedString = _iPro.Unprotect(protectedString);
			}
		}
		//public static IWebHost BuildWebHost(string[] args) =>
		//    WebHost.CreateDefaultBuilder(args)
		//        .UseStartup<Startup>()
		//        .Build();
    }
}

W powyższym przykładzie utworzyliśmy obiekt ServiceCollection, który służy do tworzenia i dodawania usług ochrony danych. Następnym krokiem jest utworzenie instancji naszej klasy za pomocą metody CreateInstance(...). Metoda ta jest wystawiona przez klasę ActivatorUtilities - jest metodą rozszerzającą klasy Microsoft.Framework.DependencyInjection, która pozwala na obsłużenie parametrów konstruktora. Ostatnim krokiem jest wywołanie metody Protect oraz Unprotect.

Zauważyłeś coś ciekawego w powyższym kodzie? Do metody CreateProtector przekazaliśmy łańcuch znaków. Wiesz z czego wynika taki zapis? Jest to dodatkowe zabezpieczenie mające na celu poprawę bezpieczeństwa danych. Jest to tzw. purpose string używany w ASP.NET Core.

Hashowanie w ASP.NET Core

Z pomocą paczki Microsoft.AspNetCore.KeyDerivation możemy zaimplementować hashowanie za pomocą ASP.NET Core. Możemy skorzystać z metody Pbkdf2 używającej algorytmu PBKDF2 do mieszania danych. Prześledź proszę poniższy przykład:

using System;
using System.Security.Cryptography;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using Microsoft.AspNetCore.Hosting;
namespace WebApplication4
{
	public class Program
    {
        public static void Main(string[] args)
        {
			//BuildWebHost(args).Run();
			string text = "Text do miesznia";
			// tworzenie tzw. 'ciągu zaburzającego', tj. ciąg losowych znaków
			byte[] salt = new byte[128 / 8];
			using (var randomNumber = RandomNumberGenerator.Create())
			{
				randomNumber.GetBytes(salt);
			}
			// tworzenie 256-bitowego klucza używając algorytmu HMACSHA1 z 1000-cem iteracji
			string secureHash = Convert.ToBase64String(KeyDerivation.Pbkdf2(
				password: text,
				salt: salt,
				prf: KeyDerivationPrf.HMACSHA1,
				iterationCount:1000,
				numBytesRequested:256/8));
        }
		//public static IWebHost BuildWebHost(string[] args) =>
		//    WebHost.CreateDefaultBuilder(args)
		//       .UseStartup<Startup>()
		//        .Build();
    }
}
W powyższym przykładzie, korzystając z ciągu mieszającego i algorytmu HMACSHA1 z 1000-cem iteracji dokonaliśmy hashowania naszego przykładowego tekstu.

Co jest nie tak z algorytmem Hash w .NET Framework?

.NET Framework w swojej implementacji używa klasy Rfc2898DeriveBytes, która ma kilka ograniczeń:

  • obsługuje tylko algorytm HMACSHA1, podczas gdy, korzystając z KeyDerivation.Pbkdf2 mamy do wyboru: HMACSHA1, HMACSHA256, HMACSHA512;
  • KeyDerivation.Pbkdf2 posiada wbudowaną obsługę poprawy wydajności w zależności od system operacyjnego (sam wykrywa system operacyjny i wdraża najbardziej zoptymalizowane rozwiązanie);
  • implementacja w .NET Framework dostarczała domyślne wartości parametrów, w najnowszej implementacji to my je definiujemy.

Podsumowanie

ASP.NET Core dostarcza interfejs API, który pomaga nam szyfrować dane za pomocą dwóch technik: Encryption (szyfrowanie) oraz Hashing (mieszanie). Co równie istotne, w przypadku szyfrowania, klucz jest tworzony i obsługiwany przez system.

ASP.NET Core Encryption jest dobrym rozwiązaniem do zarządzania stanem klienta. Mam na myśli Querystring oraz Cookies. Przy tym wskazaniu ważną role odgrywa automatycznie tworzony klucz szyfrowania.

Z kolei technika ASP.NET Core Hashing dobrze nadaje się do przechowywania haseł ponieważ nie mamy możliwości powrotu do oryginalnego tekstu.