W tym krótkim wpisie chciałbym napisać kilka słów o zaciemnianiu kodu HTML (przy okazji CSS i JavaScript), czyli tego co wyświetla się u klienta (Client-side).
Technika zaciemniania (obfuscation) polega na takim spreparowaniu kodu, by trudniej było go odczytać i zrozumieć osobom niepożądanym. Nie jest to najbezpieczniejszy sposób przechowywania danych, ale czasami jedyny możliwy.
Dlaczego mam zaciemniać swój kod?
Wpis powstał w wyniku sytuacji, w której czasami jestem i przed którą muszę się zabezpieczyć (choćby w tak banalny sposób jak obfuskacja kodu).
Jaka to sytuacja? Ano czasami wykonuję zlecenia dla nowych klientów. Nie znam ich osobiście (w końcu to współpraca zdalna), nie dostałem żadnej zaliczki (czasami dla mniejszych projektów pomijam ten etap), a sama umowa to moim zdaniem za słabe zabezpieczenie (kilka miesięcy tułać się po sądach o kilkaset złotych, jeśli zleceniem było pocięcie szablonu?).
W takich przypadkach postanawiam wykonać layout, wszystko ładnie pociąć i wykonać, a na koniec zaciemnić kod i przesłać klientowi. Po dokonanej zapłacie oczywiście podsyłam już oryginał dokumentu. Poniżej przedstawię kilka sposobów zaciemnienia kodu.
Sposób #1: papka JavaScript
Pierwszym sposobem zaciemniania, i jednocześnie najbardziej popularnym jest przeniesienie całego kodu HTML/CSS do JavaScript (najczęściej z wykorzystaniem document.write) i dopiero zaciemnienie.
Jest to chyba najłatwiejszy sposób do zaciemnienia swojego kodu – wystarczy napisać dwie proste metody do kodowania/dekodowania. Na serwer wrzucamy już zaciemniony kod wraz z funkcją dekodującą i po sprawie. Praktyczny przykład poniżej.
Funkcja do odkodowania zaciemnionego kodu:
1 | eval(unescape("%66%75%6E%63%74%69%6F%6E%20%68%70%5F%64%28%73%29%7B%73%3D%75%6E%65%73%63%61%70%65%28%73%29%3B%76%61%72%20%6F%3D%22%22%3B%66%6F%72%28%69%3D%30%3B%69%3C%73%2E%6C%65%6E%67%74%68%3B%69%2B%2B%29%7B%63%3D%73%2E%63%68%61%72%43%6F%64%65%41%74%28%69%29%3B%69%66%28%63%3C%31%32%38%29%63%3D%63%5E%32%3B%6F%2B%3D%53%74%72%69%6E%67%2E%66%72%6F%6D%43%68%61%72%43%6F%64%65%28%63%29%7D%3B%64%6F%63%75%6D%65%6E%74%2E%77%72%69%74%65%28%6F%29%3B%7D")); |
Zakodowany kod HTML:
1 | hp_d(">OGVC%22JVVR/GSWKT? Rpceoc %22AMLVGLV? Lm/Acajg <>OGVC%22LCOG? Pm`mvq %22AMLVGLV? LmKlfgz <>OGVC%22JVVR/GSWKT? Gzrkpgq %22AMLVGLV? /3 <>OGVC%22JVVR/GSWKT? KocegVmmn`cp %22AMLVGLV? Lm <>OGVC%22LCOG? OQQocpvVceqRpgtglvRcpqkle %22AMLVGLV? Vpwg <>vkvng<Rmpóul{ucpic%22ucicahk>-vkvng<>ogvc%22lcog? Fgqapkrvkml %22amlvglv? %22-<>ogvc%22lcog? Ig{umpfq %22amlvglv? %22-<>ogvc%22lcog? Pm`mvq %22amlvglv? klfgz.dmnnmu %22-<>ogvc%22lcog? amr{pkejv %22amlvglv? Uqxgnikg%22rpcuc%22xcqvpxgżmlg, %22-<>ogvc%22jvvr/gswkt? Amlvglv/Nclewceg %22amlvglv? rn %22-<>ogvc%22jvvr/gswkt? Amlvglv/v{rg %22amlvglv? vgzv-jvon9%22ajcpqgv?WVD/: %22-<>nkli%22pgn? qv{ngqjggv %22v{rg? vgzv-aqq %22jpgd? ,-ug`-qv{ng-pgqgv,aqq %22-<>nkli%22pgn? qv{ngqjggv %22v{rg? vgzv-aqq %22jpgd? ,-ug`-qv{ng-vgzv,aqq %22-<>nkli%22pgn? qv{ngqjggv %22v{rg? vgzv-aqq %22jpgd? ,-ug`-qv{ng-;42,aqq %22-<>nkli%22pgn? qv{ngqjggv %22v{rg? vgzv-aqq %22jpgd? ,-ug`-qv{ng-fgqkel,aqq %22ogfkc? cnn %22-<>#//Ykd%22nv%22KG%22:_<%22%22>nkli%22pgn? qv{ngqjggv %22v{rg? vgzv-aqq %22jpgd? ,-ug`-qv{ng-kg,aqq %22-<>#Yglfkd_//<"); |
Jak widać powyżej, całkiem fajny efekt :-) Krótko omówmy co tutaj się dzieje:
- w funkcji dekodującej korzystamy z dwóch wbudowanych funkcji: unescape oraz eval,
- do funkcji unescape trafia zakodowany łańcuch z kodem JavaScript – wykonujemy tutaj dekodowanie i przekazujemy do funkcji eval,
- eval zajmuje się wykonaniem odkodowanego kodu, co powoduje utworzeniem funkcji hp_d, która zajmuje się odkodowaniem kodu HTML/CSS,
- do funkcji hp_d trafiają zaciemnione dane, natomiast zwracany jest już normalny kod HTML/CSS z wykorzystaniem document.write,
- wszystko to dzieje się dynamicznie i nieświadomy użytkownik o niczym nie wie.
Zalety
Chyba główną zaletą jest prostota przeprowadzenia tego typu zaciemniania. Wystarczy napisać wspomniane dwie metody i po sprawie.
Jakby tego było mało, jest nawet program HTMLProtector (niestety płatny, choć w wersji Trial możliwe jest uruchomienie bodajże 30 razy programu). Program ten automatyzuje cały proces, a dodatkowo doda zabezpieczenia tj.:
- wyłączenie obsługi myszki w przeglądarce,
- zablokowana możliwość drukowania strony,
- niemożność otwarcia strony w ramce,
- dostęp do strony dozwolony tylko z wybranej domeny lub wybranego adresu,
- niemożność otwarcia strony na localhoscie,
- blokowanie klawisza PrintScreen i schowka,
- wyłączanie buforowania w przeglądarkach, indeksowania przez wyszukiwarki,
- i wiele innych.
Wady
Niestety wraz z prostotą zaciemniania przychodzi prostota dekodowania kodu (inżynieria odwrotna będzie chyba dobrym określeniem takiego procesu).
Wystarczy przeanalizować sposób dekodowania kodu i przechwycić wynik (kilka, może kilkanaście minut roboty).
Poza tym Firebug czy Opera Dragonfly widzą kod, jaki trafia bezpośrednio do przeglądarki. Wniosek z tego, że bardzo łatwo można pobrać ten kod (jako już zdekodowany HTML/CSS).
Kolejną wadą jest konieczność obsługi JavaScript, zwiększenie ilości kodu, konieczność dekodowania kodu w locie po stronie klienta, co może powodować chwilowe zawieszenie okna przeglądarki i inne.
I choć powyższe wady nie byłyby do przyjęcia w prawdziwym systemie, to na nasze potrzeby są w zupełności wystarczające (należy powiadomić klienta o takiej sytuacji i wyjaśnić na jakiej przeglądarce zadziała prawidłowo).
Sposób #2: papka HTML/CSS
Jak wiadomo każdemu, HTML jest zestawem znaczników do opisu prezencji z wykorzystaniem XML. Fakt ten można wykorzystać w bardzo wymyślny i ciekawy sposób.
Otóż można w całym dokumencie zastąpić wszystkie elementy HTML ich fałszywymi odpowiednikami.
Zamiast:
1 | <h1>Nagłówek strony</h1> |
wstawić:
1 | <n1>Nagłówek strony</n1> |
A następnie w CSS:
1 2 3 4 | n1 { display: block; font-size: 28px; } |
I tak dla każdego elementu strony. Na koniec powstałaby niezła papka w stylu:
1 2 3 4 5 6 7 8 9 | <d6h class="login"> <d6h id="topmenu"> Jesteś zalogowany jako: <ll url="konto.html">Kamil</ll> <e3>|</e3> <ll url="wyloguj.html">wyloguj się</ll> <enter> Transfer: 12259.58 MB </d6h> </d6h> |
Oczywiście należałoby jeszcze dopisać do tego właściwe style CSS oraz przy pomocy JavaScript poprzypinać zdarzenia (np. element ll byłby linkiem, a url adresem -> a href):
1 2 3 4 5 6 7 8 9 | $("ll").click(function(e) { // cancel the default action (navigation) of the click e.preventDefault(); // open link window.location.href = $(this).attr("url"); }); |
Oczywiście należałoby dodać tutaj obsługę innych atrybutów i elementów, co mogłoby być czasem męczące zapewne.
Dodatkowo można jeszcze zwykły tekst zastąpić znakami w kodowaniu ISO Latin-1 (textNode w drzewie DOM dokumentu). Grafikę można pociąć na setki elementów i przy pomocy CSS Sprites połączyć (lub przy pomocy Canvas, SVG i zablokować pobieranie).
Sposób ten jest nieco bardziej wymyślny i ciekawszy, choć dużo trudniejszy w implementacji. Ponadto nie widziałem żadnego gotowego generatora do takiego sposobu zaciemniania kodu, więc trzeba by pisać wszystko samemu (a jest trochę roboty).
Z drugiej strony, raz napisany skrypt (najlepiej wykonywany po stronie serwera: podstawiamy normalny dokument HTML, otrzymujemy papkę wraz ze specjalnym dokumentem CSS i plikiem JavaScript) załatwia całą sprawę – wysiłek popełniamy tylko raz, a potem już tylko korzystamy i wszystko idzie z górki.
Zalety
Główną zaletą takiego rozwiązania jest schowanie kodu przed narzędziami typu Firebug czy Opera Dragonfly. Aby więc dostać się do czystego i prawidłowego kodu należy poczynić znacznie więcej, niż tylko odpalić Firebuga :-)
Oczywiście można zdekodować taki kod, ale czy warto? Ktoś musiałby poświęcić mnóstwo czasu i raczej niechętnie podszedłby do takiego tematu.
Wady
Niestety to rozwiązanie ma kilka uporczywych wad, tj. :
- konieczność pisania własnego narzędzia do takiego zamieniania kodu,
- kod nie będzie semantyczny i zgodny ze standardami :-), co raczej nikogo nie będzie obchodzić na takim etapie pracy (w końcu strona nie trafi w takim stanie do użytkowników),
- nie wszystkim przeglądarkom uda się otworzyć taki dokument, a przynajmniej nie bez odpowiednio spreparowanego dokumentu DTD – możliwe więc, że będzie trzeba takowy utworzyć,
- konieczność pisania dedykowanych plików CSS i JavaScript (choć jest możliwość zautomatyzowania),
- strona zadziała tylko z przeglądarkami z włączoną obsługą JavaScript,
- jeśli oryginalny dokument operuje na JavaScript i drzewie DOM dokumentu to może być problem z przepisaniem tej funkcjonalności na nowe elementy (i to jest największy problem moim zdaniem).
Podsumowanie
Jak widać, do wyboru mamy kilka opcji, z czego ja przedstawiłem tylko dwie. Pierwszy sposób jest bardzo prosty do implementacji i bardzo prosty do odczytania, przez co niezbyt polecany.
Drugi sposób jest trudny do odczytania, lecz równie trudny do implementacji. Możliwe, że wkrótce napiszę swoje własne narzędzie do tego celu (i je udostępnię do publicznego użytku).
Zaciemnianie kodu HTML, CSS i JavaScript ma liczne wady, których nigdy nie powinien zobaczyć świat zewnętrzny. Tak stworzony kod jest niedostępny dla większości urządzeń mobilnych, osób chorych i mających utrudniony dostęp do strony, spowalnia wczytywanie się strony, czasem powoduje różne inne błędy.
Niemniej jednak powyższe sposoby są dobrym sposobem zabezpieczenia się przed nieuczciwymi zleceniodawcami. Robisz stronę dla klienta i nie masz pewności czy zapłaci za Twoją pracę? Zaciemniasz więc kod i możesz spać spokojnie – jeśli nie zapłaci to przynajmniej nie ukradnie (a jeśli ukradnie to niech się męczy z takim brzydkim kodem, który przyniesie więcej strat niż korzyści).
Ponadto, dla chcącego nic trudnego – jeśli ktoś się uprze to zdekoduje każdy zaciemniony kod. Zaciemnianie to tylko dodatkowe zabezpieczenie przed kradzieżą, nie można na tym sposobie polegać w 100%!
No i na sam koniec: życzę każdemu jak najmniej sytuacji, w których będzie musiał zaciemniać swój kod, jak największych zaliczek i samych uczciwych klientów :-).
Z czystego lenistwa wybrał bym opcję trzecią „nowy klient = zaliczka” :)
Nie chcę marudzić, ale jeśli robisz zlecenie za kilkaset PLN, to więcej czasu przeznaczysz na tworzenie tak spreparowanych plików (czy nawet automatu do ich tworzenia) niż to będzie warte.
Andrzej, ale dobrze wiesz, że takie rzeczy pisze się tylko raz :-) Fakt, za pierwszym razem napisanie takiego wynalazku może być bardziej czasochłonne niż wykonanie zlecenia. Jednak za każdym kolejnym razem wystarczy wykonać jedną operację, czasem tylko załączyć odpowiednią bibliotekę/klasę i po problemie :-)
Zgadzam się.
Zadam jednak bezpośrednie pytanie. Stosujesz te metody w praktyce? Jeśli tak to z jakim skutkiem? Jak często miewałeś informacje od klienta, że coś mu nie działa (bo ma tak skonfigurowaną przeglądarkę czy z innego powodu)?
Nie neguję pomysłu. Sam czasami kombinowałem, czy np. nie skompilować szablonu do EXE (są takie programy, ale te lepsze odpowiednio kosztują). Na szczęście teraz zazwyczaj robię całość, więc odpowiednia cena wymaga odpowiedniego zadatku, więc w razie czego nie tracę.
Jak na razie miałem tylko jedną sytuację, w której musiałem zaciemniać kod od strony klienta (zlecenie polegało wyłącznie na pocięciu szablonu). Oczywiście nie było żadnych problemów i klient uczciwie zapłacił. Niemniej jednak dobrze mieć jakieś zabezpieczenie :-) Pomysł z EXE jak najbardziej dobry, nie wpadłem na to :-)
Za to ostatnio trafiłem na klienta, który zapłacił mi połowę pieniędzy, później wysłałem resztę plików i nie zapłacił reszty. Na szczęście przewidziałem taką sytuację, w aplikacji zrobiłem uprzednio back-doora i po nieotrzymaniu reszty pieniędzy skasowałem zdalnie aplikację z jego serwera :))) Różni są klienci i czasem trzeba stosować tak drastyczne metody – lub ciągać się po sądach, ale to zależy już od wielkości zlecenia.
Pomysł z EXE jest łatwy do wykonania (pomijając koszt zakupu programu lub kombinowania z darmowymi). Nie każdy ma odwagę odpalić takiego EXE-ka i trzeba się z tym liczyć.
Ja miałem podobną sytuację z klientem, który chciał serwis na moim CMSie. Backdoora co prawda nie miałem, ale znalazłem kilka dziur we własnym kodzie, który pozwolił wejść do systemu (fajne uczucie hackować własny kod). Gość porobił kilka stron na tym CMSie, więc poszły wszystkie :)
Na szczęście u siebie nie musiałem szukać dziur w kodzie, tylko posłużyłem się backkdoorem, w którym dałem trzy opcje:
– usuń wszystkie możliwe pliki z serwera (czyli jeśli ktoś ma na danym hostingu więcej serwisów to poleci więcej :-)),
– pokaż wszystkie prywatne informacje z aplikacji – dostęp do bazy danych, dane administratora do panelu, loginy i hasła smtp,
– usuń skrypt powodujący dziurę – w przypadku, gdy klient zapłaci usuwamy – klient jest uczciwy, też jestem uczciwy i pomyślnie kończymy współpracę, czyli 99% przypadków :-).
Ponadto aplikacja w przypadku odpalenia gdzieś w sieci wysyłała sygnał do mnie, że pod takim i takim adresem jest stronka – cobym wiedział gdzie szukać swojej nieopłaconej pracy.
Już jakiś czas temu chciałem pisać o swoim backdoorze tutaj na blogu, ale nie było czasu – pewnie niebawem się pojawi, naprawdę przydatna zabawka, dzięki której można spać spokojniej (choć teraz staram się zawsze podpisywać umowy przed rozpoczęciem pracy).
Sprytne. Ja usuwając dziurę, też dodałem backdoor, ale trochę subtelniejszy. Mogę kasować i edytować wszystkie pliki na serwerze (zamiast kasowania całości, mogę gdzieś wrzucić info o uczciwości klienta albo właśnie założyć blokadę). Dodatkowo zrobiłem bardzo uproszczony phpMyAdmin – możliwość dowolnego edytowania każdego rekordu – oczywiście tylko w używanej przeze mnie bazie. W sumie to służy też podczas testowania serwisu, żebym nie musiał dodatkowo odpalać phpMyAdmin – czasami trzeba coś skasować, dodać, usunąć itp.
Muszę dodać to wysyłanie e-maila, żeby właśnie wiedzieć, czy ktoś sobie nie używa dowolnie mojego systemu :) Mam jednak nadzieję, że ludzie są rozsądni i chętniej sięgną po darmowego i udokumentowanego WP niż nieznanego nikomu Aliba :)
Jesteście okrutni! Kasujecie ludziom pliki!
Osobiście preferuję takie rozwiązanie:
– podczas produkcji i do czasu wpłynięcia całej należności – strona do wglądu na moim serwerze,
– po wpłynięciu należności – przerzut na serwer,
Na wszelki wypadek we frameworku dodałem takie paskudztwa:
– spowolnienie strony – request jest wykonywany w maksymalnym dopuszczalnym czasie wykonywania się skryptu – 5 sekund – w większości wypadków 25sekund patrzenia w pusty ekran :D
– blokowanie strony z tekstem „PAY UP!”
– zamiast wysyłania maili – leci informacja CURL’em na wskazany adres.
Hehe, dziękuję za inspirację do wymyślania sposobów karania nieuczciwych klientów w tak niekonwencjonalny sposób – z niektórych na pewno skorzystam :D fakt, to że wszystko usunąłem nie poprawia mi humoru. Przydałoby się więcej możliwości i niedługo nad tym ponownie pomyślę, gdy trafię na jakieś kolejne niepewne zlecenie (oby nigdy więcej takowych nie było).
osobiście daje zdalny pulpit i mam kontrolę nad tym co robi klient nawet zaznaczając cokolwiek widzę co robi czy kopiuje itp a to ma być tylko wgląd / lub też jak poprzednik apka exe po której wyłączeniu wszystko się kasuje łącznie z nią/ z informacją na wstępie oraz ewentualnym nałożeniu znaków wodnych jeżeli są to plakaty etc./// najlepsze rozwiązanie jakie widzę to właśnie apka