W ostatnich dwóch postach opisałem konfigurację środowiska pracy oraz generowanie projektu przy pomocy Yeomana. Po tym wszystkim nareszcie można usiąść do pisania kodu. W związku z tym, że nie czuje się mocny we frontendzie, a i mój projekt będzie się bardziej opierał na operacjach na plikach, postanowiłem zacząć właśnie od frontendu 🙂 żeby mieć to już z głowy i zdecydować się na jakiś układ, kolorystykę itp.
Zanim zacznę od opisania tego, co udało się zrobić, wyjaśnię na początku jaki mam pomysł na aplikację i co dokładnie ma ona robić:
- użytkownik będzie mógł przesłać plik graficzny na serwer, a następnie przeprowadzić na nim operacje steganograficzne
- będzie możliwość ukrycie dowolnego pliku binarnego (o ile zmieści się w pliku graficznym)
- do dyspozycji będą algorytmy
- LSB – ukrywanie danych w najmniej znaczących bitach pliku graficznego
- DCT – ukrywanie danych w blokach danych 8×8 z użyciem dyskretnej transformaty kosinusowej, która jest częścią algorytmu kompresji JPEG (czyli przesłany plik musi być w tym formacie)
- Wet Paper Codes Algorithm – połączenie DCT z Wet Paper Codes właśnie (więcej o tym algorytmie w kolejnych postach)
- plik z ukrytą informacją będzie można pobrać lub przekazać linkiem znajomemu
Dropzone.js
Za obsługę przesyłania pliku graficznego na serwer po stronie przeglądarki będzie odpowiadać dropzone.js. Jest to prosta w obsłudze biblioteka umożliwiająca opuszczanie pliku w wyznaczonym miejscu strony, a następnie wykonanie uploadu na serwer. Biblioteka została dodana do projektu jako kolejna zależność w pliku bower.json. Poniżej umieszczam moją konfigurację dropzone wraz z wyjaśnieniem. Być może komuś się przyda.
Dropzone.options.uploaddropzone = { paramName: "file", // nazwa elementu <input name="file" type="file" /> z formularza maxFilesize: 1, // maksymalny rozmiar pliku w MB maxFiles : 1, // maksymalna liczba plików previewTemplate: document.getElementById('preview-template').innerHTML, // szablon html dictDefaultMessage: "Drop your file image here (1mb max)", // informacja zachęcająca do dropowania plików acceptedFiles: "image/*", // akceptowane typy plików accept: function(file, done) { done(); }, init: function() { this.on("addedfile", function() { if (this.files[1]!=null){ this.removeFile(this.files[0]); } }); }, success: function(file, response) { // akcja wykonywana po wysłaniu pliku document.location = "/Home/ShowImage"; }, error: function (file, response) { // akcja wykonywana w przypadku wystąpienia błędu var html = '<div class="alert alert-danger alert-dismissible" role="alert">' + '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' + '<strong>Error! </strong>'+ response +'</div>'; $("#uploaddropzone").after(html); this.removeFile(file); } };
Powyższa konfiguracja podpinana jest pod formularz z odpowiadającym atrybutem id (w moim przypadku uploaddropzone).
<form method="post" enctype="multipart/form-data" asp-controller="Home" asp-action="Upload" asp-antiforgery="true" class="dropzone" id="uploaddropzone"> <div class="fallback"> <input name="file" type="file" /> </div> </form> <div id="preview-template" style="display: none;"> <div class="dz-preview dz-file-preview" style="margin: 0; width: 100%;"> <div class="dz-details"> <div class="dz-filename"><span data-dz-name></span></div> </div> <div class="progress"> <div class="progress-bar" role="progressbar" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100" style="width:0;" data-dz-uploadprogress> </div> </div> </div> </div>
Dropzone podczas uploadu pliku zmienia szerokość (w zakresie 0-100%) elementu z atrybutem data-dz-uploadprogress. Dzięki temu możemy wykorzystać w tym miejscu zwykły bootsrapowy progressbar.
Upload pliku w ASP.NET Core
Dropzone po wyborze lub opuszczeniu pliku wykonuje upload pod wskazany adres. Za sam odbiór i to co zrobimy z plikiem musimy zadbać sami. Do plików wysyłanych na serwer mamy dostęp poprzez Model Binding używając interfejsu IFormFile.
[ValidateAntiForgeryToken] [HttpPost] public async Task<IActionResult> Upload(IFormFile file) { var uploads = Path.Combine(_environment.WebRootPath, "uploads"); if (file.Length > 0) { using (var fileStream = new FileStream(Path.Combine(uploads, file.FileName), FileMode.Create)) { await file.CopyToAsync(fileStream); } } return Json("ok"); }
W powyższym przykładzie plik zapisywany do folderu uploads.
Na uwagę zasługuje, znany już programistom .NET z wcześniejszych wersji frameworka, atrybut [ValidateAntiForgeryToken]. Zapewnia on ochronę przez atakami typu CSRF. Działanie tego mechanizmu ochrony można opisać w trzech krokach:
- W formularzu, poprzez atrybut asp-antiforgery-„true”, umieszczany jest ukryty element __RequestVerificationToken, wraz z losowo wygenerowanym tokenem
- Token ten zapisywany jest również w ciasteczkach
- Podczas requestu do akcji w kontrolerze, o ile umieścimy atrybut [ValidateAntiForgeryToken], porównywana jest wartość z formularza i ciasteczka. Jeśli są równe to request zostaje przepuszczony
Udało się więc zrealizować pierwszy podpunkt z planu na projekt. Hura! 😀
Co dalej?
Na razie plik graficzny zapisywany jest w folderze uploads, być może to zmienię, gdy znajdę jakiś bezpieczniejszy sposób na ich przetrzymywanie. Może ktoś z Was ma jakiś lepszy pomysł? Nie widzę jednak w aktualnym rozwiązaniu sposobu na dostanie się do plików innego użytkownika. Muszę jedynie pomyśleć o usuwaniu pliku np. po dobie od jego utworzenia. W każdym razie, nie jest to teraz priorytetem, gdyż w końcu będę mógł zająć się biblioteką StegoCoreLib, czyli samą esencją projektu 🙂 Następy post z podsumowaniem prac nad projektem, będzie zawierał opis implementacji pierwszego algorytmu. Mam przynajmniej taką nadzieję 🙂
Wszystko co do tej pory zrobiłem, znajduje się oczywiście na githubie. A sama strona prezentuje się tak:
Tak wiem, dupy nie urywa, mocne 2/10 😀 ale działa i spełnia swoje wymagania.
1 Comment