wtorek, 25 sierpnia 2020

OWASP 2017 - 10 najczęstszych zagrożeń aplikacji internetowych (web) - A2. Broken Authentication

Drugim najczęściej występującym problemem bezpieczeństwa aplikacji internetowych jest błędnie zaimplementowana autoryzacja użytkowników. 

Kiedy aplikacja może być podatna na ten rodzaj ataku?

  • aplikacja zezwala na proste hasła, które są dobrze znane (np. admin123)
  • domyślne hasło dla administratora nie zostało zmienione podczas implementacji rozwiązania u klienta
  • brak zabezpieczenia przed atakami typu brute-force
  • brak możliwości użycia logowania wieloetapowego (np. za pomocą hasła i SMSa)
  • odzyskiwanie hasła za pomocą metod pamięciowych (np. pytania typu jak się nazywało Twoje pierwsze zwierze)
  • użycie słabych algorytmów szyfrowania haseł w bazie, przechowywanie ich w formie tekstu
  • Brak zarządzania sesjami użytkowników. Sesja nie wygasa co powoduje możliwość jej przejęcia przez atakującego.

W jaki sposób można się zabezpieczyć przed tym typem zagrożenia?
  • zmień domyślne hasła przed wgraniem systemu na produkcję
  • użyj algorytmów sprawdzających siłę i składnię hasła które chce użyć użytkownik, wymuś stosowanie silnych haseł
  • wprowadź możliwą ilość błędnych logowań do systemu, bądź też wprowadź czasowy odstęp miedzy ponownymi próbami logowania. Dodaj alerty w systemie monitorującym w przypadku gdy ktoś próbuje w kółko logować się używając błędnego hasła
  • zaimplementuj wieloetapową autoryzację
  • logika odzyskiwania hasła, podobnie jak autoryzacja może przebiegać wieloetapowo
  • zarządzanie sesjami użytkowników powinno odbywać się tylko na serwerze aplikacji. Nie twórz własnych rozwiązań do generowania sesji - użyj gotowych, dopracowanych rozwiązań.


poniedziałek, 24 sierpnia 2020

OWASP 2017 - 10 najczęstszych zagrożeń aplikacji internetowych (web) - A1. Injection

OWASP (Open Web Application Security Project) - otwarta społeczność tworząca artykuły, metodologie, narzędzia związane z bezpieczeństwem aplikacji internetowych. W ostatnich dniach miałem okazję wziąć udział w szkoleniu z tego zagadnienia i myślę, że warto się podzielić tą wiedzą.

Szkolenie dotyczyło 10 największych ryzyk bezpieczeństwa w zaktualizowanej wersji (2017). Jak można się domyślić co jakiś czas lista jest aktualizowana. Wynika to ze względu i na postęp technologiczny (frameworki rozwiązują część problemów) jak i nowe metodyki ataków na aplikacje internetowe. 

Jak przedstawia się lista OWASP 2017?

A1. Injection

A2. Broken authentication

A3. Sensitive data exposure

A4. XML External Entities (XXE)

A5. Broken access control

A6. Security disconfiguration

A7. Cross-site scripting (XSS)

A8. Insecure deserialization

A9. Using components with known vulnerabilities 

A10. Insufficient logging and monitoring


Listę rozpoczyna Injection który króluje na niej od 2010 roku. Najłatwiej można go opisać jako możliwość przesłania nieprawidłowych danych do zapytań. Konsekwencje tego zagrożenia:

  • dostęp do danych, których nie powinien widzieć atakujący
  • uruchamianie komend systemowych
Gdzie możemy spotkać się z tym typem ataków? Zapewne każdy kojarzy możliwość wstrzykiwania niepożądanego kodu do zapytań SQL. Oprócz SQL atakujący może eksplorować także:
  • LDAP
  • SMTP
  • OS Commands
  • Enviromental Variables
  • NoSQL
W jaki sposób chronić się przed tym atakiem?
  • użycie frameworków (np. ORM przy dostępie do danych)
  • oddzielenie parametrów zapytań od samego zapytania (użycie parametrów zamiast łączenia zapytania za pomocą stringów)
  • jeżeli jest to możliwe, zabronienie używania użytkownikowi systemu specjalnych znaków w zapytaniach
  • przeglądanie kodu w poszukiwaniu słabych punktów
  • użycie statycznej/dynamicznej analizy kodu na serwerze CI (np. Veracode scan)

W kolejnych postach omówię kolejne typy zagrożeń

niedziela, 23 sierpnia 2020

ArraySegment, Span - dwa typy które warto poznać

 W ostatnim wpisie mowa była o nowościach C# 8.0 - Index oraz Range. Jak wtedy wspominałem typ Range tworzy nową tablicę i kopiuje do niej wartości z zadanego przedziału. Ma to swoje implikacje w przypadku aplikacji nastawionych na dużą wydajność. Czy istnieje więc sposób, który umożliwiłby dostęp do zadanego przedziału bez potrzeby kopiowania oryginalnego źródła? Oczywiście, że tak. Przychodzą nam z pomocą dwie klasy ArraySegment<T> oraz Span<T>. Klasy te pozwalają uniknąć dodatkowego alokowania pamięci i kopiowania danych. Zobaczmy na przykład:

        static void Main(string[] args)
        {
            int[] tab = { 1, 2, 3, 4, 5, 6 };
            ArraySegment<int> range = tab[2..4];
            PrintContent(range);
        }

        private static void PrintContent(ArraySegment<int> arraySegment)
        {
            Console.WriteLine(string.Join(", ", arraySegment));
            Console.WriteLine(new string('-', 40));
        }

Powyższy kod wyświetli trzeci i czwarty element tablicy tab. Klasa ArraySegment jest ograniczona tylko do tablic. Należy także pamiętać, że klasa ta nie blokuje nas przed modyfikacją oryginalnej tablicy. Na nasze szczęście istnieje drugi typ, który pozwala pracować nad sub - sekwencjami innych typów (tablic, ciągów znaków, pamięć, pamięć zadeklarowana przez inne biblioteki itp) - Span<T> oraz ReadOnlySpan<T>. Zobaczmy na przykład użycia:

            int[] numbers = { 1, 2, 3, 4, 5, 6 };
            Span<int> range = numbers[2..4];
            ReadOnlySpan<char> textSpan = "someString"[2..4];


sobota, 22 sierpnia 2020

Nowe operatory indeksu i zakresu dla tablic w C# 8.0

C# 8.0 wniósł dwie nowości dla tablic jak i wszystkiego co implementuje interfejs IList<T>. Te dwie nowości to Index i Range:


Pierwszą z nowości jest Index - jak sama nazwa wskazuje jest to pozycja elementu w tablicy. Index to typ wartościowy - struktura readonly. Tworzyć obiekt tego typu możemy jak dla każdej innej struktury:


            int[] tab = { 1, 2, 3, 4, 5, 6 };

            var index0 = new Index(1, fromEnd: true);
            Console.WriteLine(tab[index0]);


Dodatkowy parametr (opcjonalny) fromEnd pozwala zdefiniować czy szukamy elementu o indeksie 1 od początku kolekcji czy od końca. W tym przypadku otrzymamy 1 wartość od  końca czyli 6. Index nie musi być "ręcznie" tworzony w kodzie - możemy posłużyć się operatorem ^ i tak aby pobrać ostatni element wystarczy zapis:


            int[] tab = { 1, 2, 3, 4, 5, 6 };

            Console.WriteLine(tab[^1]);


Ktoś może zapytać: "zaraz, skoro tablice są numerowane od 0, to dlaczego ostatni element pobieramy za pomocą ^1 a nie ^0"?

Sprawa jest dosyć prosta patrząc na poniższy rysunek, przedstawiający 4 elementową tablicę:


Kiedy chcemy pobrać element kolekcji (np. tablicy) poprzez indeks - żądamy pobrania elementu który zaczyna się na danej pozycji. Czyli dla przykładu jeżeli chcemy pobrać element na pozycji 0 to znaczy że chcemy odczytać wartość zawartą między indeksami 0 i 1. Dlatego własnie, patrząc na rysunek jeżeli chcielibyśmy pobrać wartość elementu ^0 oznaczałoby to chęć pobrania elementu tablicy który jest za ostatnim elementem tablicy, czyli chcemy odczytać pamięć która już nie należy do bloku tablicy. Próba taka zwróci oczywiście błąd. W drugą stronę działa to tak samo - jeżeli chcielibyśmy odczytać element na pozycji 4. Ponieważ tablica ma tylko 4 elementu, nie ma ona wartości dla indeksu między 4 i 5. 


Drugą nowością jest operator zakresu. Zakres to nic innego jak dwa indeksy: początek i koniec. Najłatwiej będzie zaprezentować ten operator na przykładach:


        static void Main(string[] args)
        {
            int[] tab = { 1, 2, 3, 4, 5, 6 };
            var range1 = new Range(2, 3);
            var range2 = 2..3;
            var range3 = 1..^2;
            var range4 = Range.All;
            var range5 = Range.EndAt(2);
            var range6 = 0..^1;
            var range7 = ..;
            var range8 = 2..;
            var range9 = ..2;
            PrintContent(tab, range1, nameof(range1));
            PrintContent(tab, range2, nameof(range2));
            PrintContent(tab, range3, nameof(range3));
            PrintContent(tab, range4, nameof(range4));
            PrintContent(tab, range5, nameof(range5));
            PrintContent(tab, range6, nameof(range6));
            PrintContent(tab, range7, nameof(range7));
            PrintContent(tab, range8, nameof(range8));
            PrintContent(tab, range9, nameof(range9));
        }

        private static void PrintContent(int[] tab, Range range, string rangeName)
        {
            Console.WriteLine(rangeName);
            Console.WriteLine(string.Join(", ", tab[range]));
            Console.WriteLine(new string('-', 40));
        }

Po zapoznaniu się z częścią poświęconą indeksom bardzo łatwo zrozumieć operator zakresu. Warto zapamiętać domyślne wartości:
  • jeżeli nie podamy lewego zakresu (startowego) będzie to zawsze 0
  • jeżeli nie podamy prawego zakresu (końcowego) będzie to zawsze -1
  • jeżeli stworzymy pusty obiekt zakresu (new Range()) zostanie stworzony zakres 0..0, czyli nie pobierzemy żadnego elementu. Jest to o tyle istotne, że domyślnie dla zakresu ".." dostajemy całą tablicę.
W jaki sposób działa Range? Działanie jest proste - zostaje stworzona nowa tablica i przekopiowane do niej wartości z zadanego zakresu. Zakres działa także dla typu string. Stosując go na obiektach łańcuchowych otrzymujemy sub-string. 

piątek, 21 sierpnia 2020

Modyfikowanie pola klasy, które jest strukturą

W jednym z ostatnich postów pisałem o użyciu słowa kluczowego ref w kontekście metod, pól i właściwości. Dzisiaj coś praktycznego, aczkolwiek raczej mało spotykanego zachowania. Zobaczmy na poniższy przykład:

    class Program
    {
        static void Main(string[] args)
        {
            var rectangle = new Rectangle(new Point3D(1, 2, 3));
            rectangle.A.X = 10; //Error CS1612  Cannot modify the return value of 'Rectangle.A' because it is not a variable
        }
    }

    public class Rectangle
    {
        public Point3D A { get; }

        public Rectangle(Point3D a)
        {
            A = a;
        }
    }

    public struct Point3D
    {
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }

        public Point3D(double x, double y, double z)
        {
            X = x;
            Y = y;
            Z = z;
        }

        public double DoSomeCalculation()
        {
            return X * Y * Z;
        }
    }

Klasa Rectangle ma pole typu struct Point3D. Pola struktury Point3D pozwalają na zmianę ich wartości (posiadają settery). W metodzie Main tworzymy obiekt klasy Rectangle i chcemy zmodyfikować wartość właściwości X struktury. Niestety kompilator na to nie pozwala. Dlaczego?
Właściwości to nic innego niż pole plus metody get/set - dlatego właśnie kompilator nie pozwala na przypisanie wartości do pola. Wywołując metodę tak na prawdę tworzymy kopię której wartość została by zmieniona. Kompilator nie pozwala nam zrobić takiego błędu.

Pozostaje pytanie w jaki sposób więc umożliwić zmianę wartości tego pola? Możemy zastosować słowo kluczowe ref:

    class Program
    {
        static void Main(string[] args)
        {
            var rectangle = new Rectangle(new Point3D(1, 2, 3));
            rectangle.A.X = 10; //Error CS1612  Cannot modify the return value of 'Rectangle.A' because it is not a variable
            Console.WriteLine(rectangle.A.X);
        }
    }

    public class Rectangle
    {
        private Point3D _a;
        public ref Point3D A => ref _a;

        public Rectangle(Point3D a)
        {
            _a = a;
        }
    }

    public struct Point3D
    {
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }

        public Point3D(double x, double y, double z)
        {
            X = x;
            Y = y;
            Z = z;
        }

        public double DoSomeCalculation()
        {
            return X * Y * Z;
        } 
} 

czwartek, 20 sierpnia 2020

Readonly struct w C# 7.2

Począwszy od C# 7.2 możemy deklarować struct który jest readonly:

    public readonly struct Point3D
    {
        public double X { get; }
        public double Y { get; }
        public double Z { get; }

        public Point3D(double x, double y, double z)
        {
            X = x;
            Y = y;
            Z = z;
        }
    }

Jakie zalety powoduje dodanie słówka readonly w przypadku struktur?
  • struktura jest całkowicie niemodyfikowalna z zewnątrz i wewnątrz
  • wszystkie pola muszą być typu readonly
  • właściwości nie mogą posiadać setterów

Oprócz powyższych zalet jest jeszcze jednak o której warto wspomnieć a dotyczy ona - wydajności. Jeżeli w jakimś innym typie zadeklarujemy obiekt struktury tylko do odczytu jako pole / właściwość readonly kompilator może uniknąć kopiowania jej gdy ktoś z niej skorzysta. Przykład:

    class Program
    {
        static void Main(string[] args)
        {
            var rectangle = new Rectangle(new Point3D(1, 2, 3));
            var calculationResult = rectangle.A.DoSomeCalculation();
            Console.WriteLine(calculationResult);
        }
    }

    public class Rectangle
    {
        public Point3D A { get; }

        public Rectangle(Point3D a)
        {
            A = a;
        }
    }

    public readonly struct Point3D
    {
        public double X { get; }
        public double Y { get; }
        public double Z { get; }

        public Point3D(double x, double y, double z)
        {
            X = x;
            Y = y;
            Z = z;
        }

        public double DoSomeCalculation()
        {
            return X * Y * Z;
        }
    }

Normalnie kompilator C# stworzyłby kopię obiektu Point3D podczas dostępu do niego ze zmiennej rectanble. Kompilator nie wie czy struktura może spowodować zmianę w niej np. poprzez wywołanie metody DoSomeCalculation(). Jednak jako że struktura jest oznaczona słowem readonly nie zostanie wykonana jego kopia i kompilator wie, że ma wykonać metodę DoSomeCalculation bezpośrednio na obiekcie składowy rectangle.

środa, 19 sierpnia 2020

Dlaczego niektóre metody przyjmujące tablice obiektów params oferują także przeładowania dla pojedynczych parametrów?

Zastanawialiście się może kiedyś dlaczego string.Format oferuje oprócz przeładowania przyjmującego dowolną ilość argumentów - params - także wersje pozwalające przesłać 1, 2 lub 3 argumenty?
Czy nie wydawało Wam się to dziwne?



Odpowiedź na to pytanie nie jest trudna. Operacje na stringach to dosyć popularne operacje. korzystając za każdym razem z wersji params powodujemy utworzenie tablicy. Tworzenie obiektów na stosie jest mimo wszystkich procesem kosztownym. Dzięki przeładowaniom z poszczególnymi argumentami, nie jest konieczne tworzenie tablicy za każdym razem.  

wtorek, 18 sierpnia 2020

Referencje do zmiennych i zwracanych wartości

Poprzedni artykuł omówił sposoby przekazywania przez referencję argumentów do metod. Teraz zobaczmy w jaki sposób możemy uzyskać referencję do zmiennych oraz wyników metod. 

            string s1 = "Ala ma kota";
            ref string ref1 = ref s1;

            ref1 = "Override value";
            Console.WriteLine(s1);

Jakie są ograniczenia lokalnych referencji?
  • Po pierwsze kompilator nie pozwoli stworzyć referencji do zmiennej, która zostanie usunięta przed referencją (zakres widzialności zmiennej i referencji musi być taki sam).
  • Nie można stosować ref dla metod asynchronicznych (async)
  • nie można używać w iteratorach
  • nie można używać w funkcjach anonimowych

Warto jednak zaznaczyć, że ref można zastosować dla właściwości. Dzieje się tak dlatego, że właściwości są przekształcane do metod get i set. Generalna zasada jest taka, że kompilator musi mieć pewność że referencja nie "przeżyje" obiektu do którego ta referencja się odnosi.

Zobaczmy teraz kilka przykładów:

    public class Sample
    {
        public ref int WillFail()
        {
            int i = 42;
            return ref i;
        }
    }
Kod ten nie skompiluje się gdyż, zmienna i przestanie istnieć po opuszczeniu zasięgu metody WillFail.

    public class Sample
    {
        public ref int GetSameRef(ref int arg) => ref arg;

        public ref int WillFail()
        {
            int i = 42;
            return ref GetSameRef(ref i);
        }
    }

Kod ten także nie zadziała. Próbujemy poprzez dodatkową funkcję zwrócić referencję do zmiennej i która po opuszczeniu zasięgu metody WillFail zostanie usunięta. 

    public class Sample
    {
        public ref int GetSameRef(ref int arg) => ref arg;

        public ref int WillWork(ref int i)
        {
            return ref GetSameRef(ref i);
        }
    }

Ten przypadek zadziała poprawnie. Zwracamy referencję do zmiennej, która zostanie przekazana z zewnątrz a więc "przeżyje" lokalny zasięg. 

    public class Sample
    {
        private int i;
        private int[] tab = new int[5];
        public ref int ReferenceToField => ref i;
        public ref int ReferenceToArrayElement(int index) => ref tab[index];
    }

Powyższy przykład także będzie kompilował się poprawnie - zostanie zwrócona referencja do pola oraz elementu tablicy. Jest to możliwe dlatego, że składowe klasy żyją na stosie i garbage collector jest świadomy, aby ich nie usuwać. 

Jaka jest realna zaleta używania ref dla lokalnych zmiennych i wartości zwracanych? Tak samo jak w przypadku przesyłania dużych struktur danych możemy zaoszczędzić moc, która byłaby marnowana na kopiowanie obiektów. 

poniedziałek, 17 sierpnia 2020

Przesyłanie argumentów przez referencje

Temat dobrze znany, ale wraz z rozwojem C# zwłaszcza siódmej i ósmej wersji postanowiłem ponownie podejść do tematu. 

Przed wprowadzeniem Tupli w siódmej wersji języka, aby zwrócić więcej niż jedną wartość z metody mogliśmy zastosować słowo kluczowe out. Ponieważ słowo kluczowe out istnieje od początku języka C# możemy się z nim spotkać w wielu miejscach - zwłaszcza przy próbach konwersji:

            //1. Parsing string to int
            string someText = "5";
            bool canParse = int.TryParse(someText, out int a);
            if (canParse)
            {
                Console.WriteLine($"The value is {a}");
            }
            else
            {
                Console.WriteLine("Can't parse");
            }

            //2. Try get value from dictionary
            var data = new Dictionary<string, int>
            {
                ["Adam"] = 30,
                ["Sebastian"] = 50,
                ["Marek"] = 35
            };
            string searchKey = "Adam";
            var canGetValueByKey = data.TryGetValue(searchKey, out int age);
            if(canGetValueByKey)
            {
                Console.WriteLine($"{searchKey} is {age}");
            }
            else
            {
                Console.WriteLine("Key not present in dictionary");
            }

Sama deklaracja funkcji Parse dla typu int wygląda następująco:

        public static bool TryParse(String s, out Int32 result) 
        {
            return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
        }

Zarówno w funkcji jak i jej wywołaniu musimy jawne podać słówko out

Warto w tym miejscu zaznaczyć, że metody oznaczone słówkiem kluczowym async nie mogą posiadać argumentów out. Jest to spowodowane faktem, że metody async nie determinują jednoznacznie kiedy zostaną wykonane w pełni. Metoda może wykonać się w części i zwrócić kontrolę do głównej funkcji, która z kolei także może zwrócić kontrolę do metody wołającej. Tym sposobem zmienne przekazane przez out mogłyby już nie istnieć. Ta sama zależność jest związana z funkcjami anonimowymi, do których także nie możemy przesłać parametru za pomocą słówka out.

Czasami może się zdarzyć, że wartość parametru out nie jest nam potrzebna. Przykładowo chcemy tylko sprawdzić czy wartość można skonwertować czy też nie. Jeżeli nie interesuje nas w takim przypadku wartość możemy zastosować tzw. discard operator:

            string someText = "5";
            bool canParse = int.TryParse(someText, out _);

Przed erą C# 7.0 musielibyśmy zadeklarować zmienną, której wartość byłaby po prostu nie używana. 


Drugą metodą przekazywania zmiennych przez referencję jest użycie słówka ref. Działa podobnie jak słówko out z tą różnicą, że zmienna przesyłana do metody musi zostać zainicjowana przed wysłaniem:

        static async Task Main(string[] args)
        {
            int value = 20;
            DoSomething(ref value);
            Console.WriteLine(value);
        }

        public static void DoSomething(ref int value)
        {
            value = 10;
        }


Trzecią opcją jest nowe słówko (począwszy od C# 7.2) in. Słówko to pozwala na przesłanie obiektu przez referencję ale tylko do odczytu. Zachodzi tu relacja out - wysyłamy zmienną aby ją nadpisać, in - zmienna tylko do odczytu. Można zapytać po co wprowadzono słówko in? Na pewno nie przyda się ono do przesłania obiektów typu int. Obiekty wartościowe (np. duże struktury) przesyłane są przez wartość, czyli krótko mówiąc przesyłamy ich kopię do metody. Kopiowanie dużej ilości obiektów może obniżyć wydajność naszej aplikacji. In pozwala uniknąć kopiowania - wszak prześlemy kopię referencji (32 lub 64 bity):

        static async Task Main(string[] args)
        {
            var point3D = new Point3D(1, 2, 3);
            DoSomething(point3D);
        }

        public static void DoSomething(in Point3D point)
        {
            Console.WriteLine(point);
            
            //point = new Point3D(3, 4, 5); //Error CS8331  Cannot assign to variable 'in Point3D' because it is a readonly variable
        }
    }

    public readonly struct Point3D
    {
        public double X { get; }
        public double Y { get; }
        public double Z { get; }

        public Point3D(double x, double y, double z)
        {
            X = x;
            Y = y;
            Z = z;
        }

        public override string ToString()
        {
            return $"X:{X} Y:{Y} Z:{Z}";
        }
    }

Należy zauważyć, że struktura została oznaczona jako readonly. Jeżeli by nie została, kompilator przyjąłby agresywniejszą metodę i stworzył mimo wszystko kopię struktury Point3d.

W kolejnej części zajmę się referencjami do zmiennych i zwracanych wyników.