Witaj w challengu Node.js, gdzie codziennie przez 7 dni zdobędziesz konkretną dawkę informacji dotyczących Node.js oraz wykorzystasz ją w praktyce. Pamiętaj żeby wykonywać dni challengu po kolei - od dnia pierwszego do ostatniego (dzięki temu Twoja wiedza będzie poukładana i kompletna).
Każdy dzień to jeden temat przewodni. W jego ramach stworzysz aplikację Node.js, która faktycznie będzie potrafiła coś zrobić - od razu zobaczysz wynik swojej pracy.
Kilka ważnych informacji
Przed przystąpieniem do rozwiązywania zadań przeczytaj poniższe wskazówki
Do pełnego i satysfakcjonującego doświadczania tego challengu jest potrzebna znajomość JavaScript z elementami ES6. Jeżeli potrzebujesz informacji z zakresu ES6 to znajdziesz je tutaj: tutorial ES6.
Poszczególne zadania rozwiązuj w odpowiednich plikach.
- Pierwszy dzień to wstęp do Twojej przygody z Node.js - dowiesz się w jaki sposób przygotować środowisko oraz jak pisać i testować programy Node.js.
- W kolejnych dniach dowiesz się w jaki sposób za pomocą Node.js wchodzić w interakcję z systemem operacyjnym (np. modyfikować pliki czy dokonywać szyfrowania).
- Druga część challengu jest poświęcona tworzeniu back-endu - dowiesz się jak stworzyć własny serwer.
- Pod koniec doświadczysz roli full-stack developera - stworzysz komunikujący się ze sobą front-end i back-end.
Wczoraj nauczyliśmy się wykorzystywać pierwsze wbudowany w Node moduł: fs
. Dzisiaj poznajemy kolejny, o nazwie crypto
. Jak sama nazwa wskazuje - kojarzy nam się z kryptografią - dokładnie tym się zajmuje. Dzięki wykorzystaniu crypto
jesteśmy w stanie używać pewnych szyfrów, czy funkcji skrótu. Dołączenie modułu następuje w standardowy, poznany wcześniej sposób:
const crypto = require('crypto');
Szyfrowanie, kryptografia i bezpieczeństwo to bardzo skomplikowane tematy. Nie daj się zrazić teoretycznym opisom dzisiejszego dnia :) Pod koniec każdego działu podajemy praktyczny kawałek kodu, który zrealizuje dane zadanie!
W praktyce nie zawsze te przygotowane dla Ciebie gotowce są w 100% bezpieczne! Wszystko zależy od konkretnego użycia. Żeby było jasne - ten dzień sprawi, że będziesz potrafić szyfrować, deszyfrować i pobierać "hash", ale nie zrobi z Ciebie eksperta ds. bezpieczeństwa :)
Funkcja skrótu, funkcja mieszająca lub funkcja haszująca – funkcja przyporządkowująca dowolnie dużej liczbie krótką, zawsze posiadającą stały rozmiar, niespecyficzną, quasi-losową wartość, tzw. skrót nieodwracalny.
Źródło: Wikipedia. Zaraz rozszyfrujemy tę definicję :)
Funkcją skrótu nazywamy taki algorytm, który jest w stanie z dowolnej długości danych stworzyć krótki ich "podpis".
Przykładowo dla pliku w app/data/someText.txt
funkcja skrótu sha256
zwróci jako hex (te dziwnie nazwy rozwikłamy już wkrótce): cc995beff4574d7acb384bf79f5527e1e0fbca926dcf7fa95c8bab06a5237ecf
. Niezależnie od tego jak długi tekst źródłowy podamy ta funkcja skrótu zwróci podpis o takiej samej długości. W dodatku każdy tekst wygeneruje zupełnie inny, unikalny podpis.
W praktyce bardzo często stosuje się funkcje skrótu przede wszystkim w dwóch miejscach:
- W celu przechowywania haseł. Funkcja skrótu ma bowiem ciekawą właściwość: jest unikalna* dla każdego tekstu, z drugiej strony nie pozwala odzyskać oryginalnego tekstu. (czyt: jeżeli ktoś nawet włamie się to nie pozna prawdziwych haseł użytkowników).
- Żeby określić czy pobrane pliki są takie same jak oryginał. Sprawdzając czy skrót z pliku jest taki jak był w oryginale możemy stwierdzić czy mamy do czynienia z prawdziwym i poprawnie pobranym plikiem.
Czas na praktykę - jak pobrać ten "podpis" dla stringa np. 'Hello, World!'
? Za pomocą funkcji crypto.createHmac()
. Cały kod jest dosyć złożony, ale spokojnie - tutaj podajemy gotowca:
const crypto = require('crypto');
const hash = crypto.createHmac(algorytmFunkcjiSkrotu, twojTekst)
.digest(format);
console.log(hash); //Wyświetl "hash" - czyli wynik funkcji skrótu
Na przykład dla algorytmu sha256
, formatu hex i tekstu 'Hello, World!':
const crypto = require('crypto');
const text = 'Hello, World!';
const hash = crypto.createHmac('sha256', text)
.digest('hex');
console.log(hash); //Zwróci '11febc4b352d7961ee0ff767b846dd87712a94f4ac3380d6c66d5a6f74bc3500'
Pierwszym argumentem funkcji crypto.createHmac()
jest algorytm. Masz kilka możliwości, m.in.:
'sha256'
(popularny i bezpieczny)'sha512'
(popularny i bardzo bezpieczny, ale dłuższy)'md5'
(bardzo niebezpieczny i przestarzały)'rmd160'
(uwaga: nie na każdym systemie działają wszystkie te algorytmy). W praktyce najczęściej wykorzystujemy sha256
lub sha512
.
Na koniec naszego kodu musieliśmy wywołać .digest()
, który wymaga jako argument formatu. Od tego zależy w jakiej formie zostanie przedstawiony nasz "podpis". Masz kilka możliwości, m.in.: 'hex'
czy 'base64'
. Nie ma to teraz większego znaczenia dla nas i możesz używać wartości 'hex'
.
Szyfrowanie w programowaniu jest zaawansowanym i dosyć długim tematem. Pokażemy Ci jednak jak w prosty sposób można coś szybko zaszyfrować lub rozszyfrować! Poznamy tylko najważniejsze informacje, żebyś szybko mógł/mogła zacząć.
Na początku używamy funkcji crypto.createCipher(algorytmSzyfrujacy, haslo)
, któremu podajemy odpowiednie dane - co zwróci nam obiekt typu Cipher
(zapisujemy go po prostu do zmiennej).
Później na tym obiekcie wykonujemy .update(tekstOryginalny, kodowanieWejsciowe, formatWyjsciowy)
. Ta metoda zwróci nam część zaszyfrowanego tekstu.
Na koniec na tym samym obiekcie wykonujemy .final(formatWyjsciowy)
.
Brzmi skomplikowanie? Trochę tak jest :) Dlatego przygotowaliśmy dla Ciebie wygodną funkcję, która realizuje szyfrowanie i korzysta z wyżej podanej wiedzy:
const crypto = require('crypto');
function encodeText(text, password, algorithm){
const cipher = crypto.createCipher(algorithm, password);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
Żeby jej użyć musisz jeszcze tylko wiedzieć jakie algorytmy masz do wyboru. Jest ich bardzo dużo i zależą od Twojego systemu, masz do dospozycji m.in.: 'aes192'
, 'aes-256-cbc'
, 'aes-256-ecb'
i inne.
Np. szyfrując wiadomość 'Hello, World!'
za pomocą algorytmu 'aes-256-cbc'
i hasła 'M0j3 has|0!'
otrzymamy 352c9abb9cd20b41766f352b2611bb7b
:
const crypto = require('crypto');
function encodeText(text, password, algorithm){
const cipher = crypto.createCipher(algorithm, password);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
console.log(encodeText('Hello, World!', 'M0j3 has|0!', 'aes-256-cbc')); //Wyświetli `352c9abb9cd20b41766f352b2611bb7b`
Ok. Mamy już zaszyfrowaną wiadomość... To w jaki sposób ją teraz rozszyfrować? Właściwie cały proces jest identyczny jak w przypadku szyfrowania - z tą różnicą, że używamy funkcji crypto.createDecipher()
.
Oto funkcja deszyfrująca dla Ciebie:
const crypto = require('crypto');
function decodeText(encodedText, password, algorithm){
const decipher = crypto.createDecipher(algorithm, password);
let decrypted = decipher.update(encodedText, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
Próbując zatem odwrócić poprzedni proces:
const crypto = require('crypto');
function decodeText(encodedText, password, algorithm){
const decipher = crypto.createDecipher(algorithm, password);
let decrypted = decipher.update(encodedText, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
console.log(decodeText('352c9abb9cd20b41766f352b2611bb7b', 'M0j3 has|0!', 'aes-256-cbc')); //Wyświetli `Hello, World!`
Pamiętaj: żaden szyfr nie jest w 100% bezpieczny. Bezpieczeństwo to skomplikowane zagadnienie. Dzisiejszy challenge nie jest przewodnikiem po bezpiecznym szyfrowaniu.
Ćwiczenia wykonuj w odpowiednich plikach. W folderze
app
są one ponumerowane tak samo jak poniżej - zadaniu1. Rozgrzewka
odpowiada plikapp/zadanie01.js
itd. Aby uruchomić zadanie podaj jego nazwę (pamiętaj, aby linia komend/terminal był otwarty na kataloguapp
tego repozytorium), np.:node ./zadanie01.js
Sprawdźmy Twoje umiejętności korzystania z funkcji skrótu.
W pliku app/zadanie01.js
znajduje się ciąg '5dca0fc4e306d92b2077ad85e7c4bd87a3e8648e'
. Twoim zadaniem jest określenie którego z poniższych haseł użyłem do jego wygenerowania:
'??TegoHasła'
'CodersLab'
'Node.js Szyfruje Pliki'
'Zaźółć Gęślą Jaźń'
'Moje Haslo 1@3!'
'111#$((@)n'
'Dzisiaj Szyfruje 83'
Nie wiadomo jaki został użyty algorytm funkcji skrótu. Zerknij do działu funkcji skrótu, aby przypomnieć sobie o jakich wspominaliśmy.
Zadanie możesz wykonać ręcznie, możesz też spróbować wykonać go za pomocą pętli - aby program sam znalazł, które z powyższych haseł zwróci dla danej funkcji skrótu '5dca0fc4e306d92b2077ad85e7c4bd87a3e8648e'
.
Połącz swoją wiedzę z pierwszego, drugiego i dzisiejszego dnia. Stwórz program, któremu można przekazać jako argument ścieżkę pliku tekstowego. Następnie Twój program ma wczytać zawartość tego pliku i na końcu wykonać funkcję skrótu 'sha256'
na nim i ją wyświetlić.
Przykładowo wykonanie:
node ./zadanieDnia1.js ./data/zadanieDnia1/testFile.txt
Powinno wyświetlić 4f7ae6569b55cb6275423ca1cdf31475e607da1d5204c110a58fb480c96e6eca
.
Takie narzędzie jest bardzo praktyczne i powszechnie stosowane np. do sprawdzania oryginalności pobieranych plików (autor na swojej stronie pobierania musi podać oryginalny sha256
, my po pobraniu generujemy swój i porównujemy czy są takie same).
Pobawmy się jak komputerowy Detektyw :)
Ostatnie zadanie polega na rozszyfrowaniu niżej podanego tekstu. Ten tekst znajduje się również w pliku app/zadanieDnia2.js
. Użyto jednego z algorytmów szyfrujących które wymieniliśmy. Hasłem jest pierwsza i ostatnia litera każdego słowa w pierwszym zdaniu tego zadania :)
4f9fa8f98650091c4910f5b597773c0a48278cfb001fe4eb3ff47ada85cbf0ed3dc17016b031e1459e6e4d9b001ab6e102c11e834a98dce9530c9668c47b76ee6f09d075d19a38e48b415e067c6ddcfad0d3526c405a4f4f2fb1e7502f303c40