zwiń / rozwińProgramowanie
  • Java [ 0 ]
  • C [ 0 ]
  • PHP [ 1 ]
  • Modelowanie [ 0 ]
zwiń / rozwińLayout
  • CSS [ 0 ]
  • xHTML [ 0 ]
  • Photoshop [ 0 ]
zwiń / rozwińArtykuły
  • Artykuły [ 0 ]

[TEST!!]Sposób na walkę ze spamem w formularzach POST

Obrona przed spamem w formularzach kontaktowych.

Dzisiaj chciałem opisać kilka metod dotyczących obrony przed wysyłanym spamem przez roboty. Jedna z najczęściej stosowanych metod to używanie tokenów, czyli obrazków z napisem, które przepisuje osoba wysyłająca wiadomość. Minusem tej metody jest to że wymaga się od użytkownika każdorazowo wpisywania dodatkowych znaczków, a jak ktoś źle zaprojektuje formularz to po błędnym przepisaniu trzeba ponownie wprowadzać dane. Nie zmienia to faktu, że jest to obecnie jedna z lepszych(najczęściej używanych) metod i jeżeli wszystko jest z głową zaprojektowane i nie dopuszczamy do sytuacji opisanej wcześniej możemy śmiało korzystać z tej metody.

Jeżeli chodzi o tokeny to znajdziemy bardzo dużo tutoriali czy gotowych bibliotek do użycia, dlatego opiszę inne metody obrony przed spamem. Metody tak naprawdę działają na bardzo podobnej zasadzie jednak korzystają z różnych elementów php dlatego dla początkujących programistów ten artykuł może okazać się pomocny.

Opiszę zatem metody wykorzystujące poniższe opcje:

  • - Cookies
  • - Sesje
  • - Pliki

Uwagi:

  • 1. Kod przykładów podam w 'czystym' php jak i dla frameworka Code Igniter (jest to dość stary framework - jednak działa jeszcze na serwerach z php4), użycie w Kohanie czy Zend Framework nie powinno się wiele różnić.
  • 2. Metody nie nadają się raczej do wykorzystania w formularz komentarzy - bardzo często można zobaczyć zażartą dyskusje między komentującymi(dobrym przykładem jest portal wykop.pl), daje możliwość komentowania co godzinę raczej ograniczamy użytkowników.

Cookies

Cookies czyli tzw. ciasteczka tworzone są po stronie klienta. Tworząc ciasteczka możemy ustawić czas ich życia - ustawiając czas życia na 1 godzinę, a następnie na stronie sprawdzamy istnienie naszego ciastka - jeśli jest tzn. że osoba wysłała już wiadomość i musi poczekać. W ciasteczku możemy przechować takie dane jak czas wysłania - dzięki temu możemy poinformować użytkownika za ile będzie mógł wysłać kolejną wiadomość.

Minusami tej metody jest to że użytkownik może ręcznie skasować stworzony przez nas cookies i wysyłać dalej wiadomości.

Troszkę kodu:

Tworzenie ciasteczka - należy użyć w momencie wysłania formularza (po jego obsłużeniu).

  1. // ładowanie pomocnika obsługującego ciastka
  2. $this->load->helper('cookie');
  3.  
  4. // Używamy tabeli jakbyśmy chcieli coś jeszcze dołożyć
  5. $user_sended_data = Array('time' => time()+ 3600);
  6.  
  7. // tworzenie tabeli cookie która jest wykorzystana do ustawiania cookies
  8. // serialize() jest konieczne ponieważ podajemy tablicę,
  9. // która jest zamieniana na jeden łańcuch
  10. $cookie = array(
  11. 'name' => 'email_confirm',
  12. 'value' => serialize($user_data_sended),
  13. 'expire' => time()+ 3600,
  14. 'domain' => '.wlodi.net',
  15. 'path' => '/',
  16. 'prefix' => 'wlodinet_',
  17. );
  18.  
  19. // ustawienie ciasteczka
  20. set_cookie($cookie);

Sprawdzenie czy istnieje ciasteczko i odpowiednie działanie, należy wstawić przed obsłużeniem formularza. Pamiętamy, że korzystaliśmy z serialize() i aby odtworzyć dane musimy wykorzystać funkcje unserialize().

  1. // do wyboru jeden z if-ów
  2. // zawiera filtr XSS
  3. if ($user_data_sended = get_cookie('email_confirm', TRUE))
  4. // bez filtra XSS
  5. if ($user_data_sended = get_cookie('email_confirm'))
  6. {
  7. $tmp = unserialize($user_data_sended->value);
  8.  
  9. // obliczenie czasu pozostałego w sekundach
  10. $time_info = time() - $tmp['time'];
  11.  
  12. // do sformatowania czasu można posłużyć się funkcją date()
  13. // nie będę już pisał tej części pozostawie
  14. // jako ćwiczenie dla czytelników
  15. }
  16. else
  17. {
  18. //użytkownik nie posiada ciastka - nie wysyłał jeszcze wiadomości
  19. // w tym miejscu można wrzucić obsługę wysłanej wiadomości
  20. }

Część bez frameworka - jest analogiczna, cały kod w jednym bloku podzielony komentarzami /**/ na dwie części

  1. /* --- Ustawienie ciasteczka --- */
  2. // Używamy tabeli jakbyśmy chcieli coś jeszcze dołożyć
  3. $user_sended_data = Array('time' => time()+ 3600);
  4.  
  5. // Ustawiamy cookies
  6. setcookie( 'email_confirm',
  7. serialize($user_data_sended),
  8. time()+ 3600,
  9. '.wlodi.net',
  10. '/');
  11.  
  12. /* --- Sprawdzenie czy istnieje ciasteczko --- */
  13. if (isset($_COOKIE['email_confirm']))
  14. {
  15. $tmp = unserialize($_COOKIE['email_confirm']);
  16.  
  17. // obliczenie czasu pozostalego w sekundach
  18. $time_info = time() - $tmp['time'];
  19.  
  20. // do sformatowania czasu mozna posluzyc się funkcją date()
  21. // nie bede już pisał części fronend'u
  22. // jak pisalem wczesniej mozna wypisac za ile czasu
  23. // uzytkownik będzie mógł wysłać wiadomość
  24. }
  25. else
  26. {
  27. //uzytkownik nie posiada ciastka - nie wysylal jeszcze wiadomosci
  28. // w tym miejcu mozna wrzucic obsluge wyslanej wiadomosci
  29. }
  30.  

Sesje

Kolejną metodą będzie użycie sesji - działanie i kod jest bardzo zbliżony. Tworzymi sesję oraz czas jej wygaśnięcia - usatwiając czas na 1 godzinę, a następnie na stronie sprawdzamy istnienie aktywnej sesji - jeśli jest tzn. że osoba wysłała już wiadomość i musi poczekać. W sesji i jak to było w ciasteczku przechowujemy takie dane jak czas wysłania - dzięki temu możemy poinformować użytkownika za ile będzie mógł wysłać kolejną wiadomość.

Minusami tej metody jest to że użytkownik może ręcznie skasować aktywną sesję.

Uwaga:

  • 1. W Code Igniter w pliku application/config/config.php możemy ustawic żywotnosc sesi - jednak bedzie sie on odnosił do wszystkich sesji, nie jest to raczej pozadane - nawet jesli nie uzywamy na razie innej sesj w przyszlosci jesli bedziemy chcieli cos zmienic powstanie problem - i trzeba bedzie zmienic kodzik w wielu miejsca - dlatego nalezy zadbac o to odrazu, przechowujemy czas przesłania wiadomości, więc skorzystajmy z niego.
  1. /* Tworzenie sesji */
  2. // biblioteka obsługująca sesje
  3. $this->load->library('session');
  4.  
  5. // analogicznie jak w Cookies przechowujemy czas
  6. $user_sended_data = Array('time' => time());
  7.  
  8. //na koniec ustawienie sesji
  9. $this->session->set_userdata($user_sended_data);
  10.  
  11. /* Obsługa formularza */
  12.  
  13. $sended_time = $this->session->userdata('time');
  14.  
  15. if($sended_time + 3600 < $time())
  16. {
  17. // uzytkownik poczekal godzinke mozemy dac mu wyslac e-mail
  18. }
  19. else
  20. {
  21. // uzytkownik nie poczekal wystarczajaco dlugo
  22. // podobnie jak w cookies blokujemy wysylanie po stronie frontendu
  23. }

Część bez frameworka, cały kod w jednym bloku podzielony komentarzami /**/ na dwie części

  1. /* tworzenie sesji - powinno wystąpić na samym początku kodu */
  2.  
  3. session_start(); // można pominąć jeśli jest włączona opcja auto_start
  4.  
  5. // sprawdzenie i ustawienie czasu wyslania
  6. if (!isset($_SESSION['time']))
  7. {
  8. $_SESSION['time'] = time();
  9. }
  10. else
  11. {
  12. $sended_time = $_SESSION['time'];
  13. }
  14.  
  15. if(isset($sended_time) && ($sended_time + 3600 < $time()))
  16. {
  17. // uzytkownik poczekal godzinke mozemy dac mu wyslac e-mail
  18. }
  19. else
  20. {
  21. // uzytkownik nie poczekal wystarczajaco dlugo
  22. // podobnie jak w cookies blokujemy wysylanie po stronie frontendu
  23. }

Pliki

Ostanią metodą jaką opiszę jest wykorzystanie plików i tworzenie "bazy" adresów IP i adresów e-mail. Kilka słów o samej koncepcji:

Do przechowywania danych użyjemy pliku z zachowaniem formatu csv. Przykład zawartości takiego pliku

  1. ip;czas_wyslania;email;ilosc_wiadomosci
  2. 127.0.0.1;12323423;mail@wlodi.net;2

Pierwszy wiersz jest opisem, ale rowniez w wypadku CI musi zostac - poniewaz wykorzystamy biblioteke ktora parsuje i obsluzy za nas plik csv i wymaga takiego formatu.

Dużym minusem tej metody jest to że większość surferów posiada zmienne IP i teraz ktos kto naprawde by chcial pospamowac, skrzynie odnawia polaczenie(jest to naszczecie dosc czasochlonne - nawet jak robil by to z automatu mozna zalozyc ze czas polaczenie to okolo 5s-10s) Jednak trzeba założyć, że wiele osób korzysta z sieci zatem blokada całego IP też nie jest najlepszym rozwiązaniem - chodź dość często stosowana przez serwisy WWW.

Jak widac z przechowywaniem danych w pliku wiaze sie wiele problemow, jednak oprócz tych minusow, takie przechowanie(dotyczy rowniez bazy danych) daje nam ogromne mozliwosci jesli chodzi o ich analize danych(adresy ip, e-mail)

Minusem dosc waznym w przypadku uzywania plikow do przechowywania danych jest ich przeglad jak posiadamy strone ktora jest przegladana przez wiele osob i dostajemy wiele wiadomosci to plik nam sie powieksza i jego przeszukiwanie moze trwac zbyt dlugo, dlatego powinnismy sie pokusic o tworzenie jednego duzego pliku - z wczesniej opisana baza adresów ip, email oraz jeden ktory jest czyszczony np. raz dziennie.

Jak wcześniej wspomniałem do parsowania pliku CSV posłużmy się biblioteką napisaną dla Code Igniter (jednak w przykładzie bez CI) też jej użyjemy ponieważ jest zgrabnie napisana i posiada jedynie te elementy których potrzebujemy. Autorem jest Pierre-Jean Turpeau strona biblioteki na CI http://www.codeigniter.com/wiki/CSVReader oraz trochę szerszy opis i przykłądy użycia http://blog.insicdesigns.com/2009/03/reading-csv-file-in-codeigniter/

Instalacja biblioteki w CI jest bardzo prosta wystarczy przekopiowac zawartość do folderu library w naszym projekcie.

Uwaga:

  • 1. Ze względu na dość rozrastający się pierwszy artykuł opuszcze tutaj zapis do pliku danych, założe że plik już istnieje i mogę z niego korzystać
  1. $this->load->library('csvreader');
  2. $ip = $this->input->ip_address();
  3. $filePath = 'file.csv';
  4.  
  5. // sparsowanie pliku i przekazanie danych do $data;
  6. $data = $this->csvreader->parse_file($filePath);
  7.  
  8. // uzytkownik moze wysylac wiadomosc
  9. $check_ability = true;
  10.  
  11.  
  12. foreach($data as $field){
  13. if($field['ip'] == $ip && $field['email'])
  14. // jesli mamy w bazie juz taki mail lub adres IP
  15. // sprawdzamy czas i okreslamy dostęp
  16. {
  17. if($field['czas_wyslania']+3600 > time())
  18. $check_ability = false
  19. }
  20. }
  21.  
  22. if($check_ability)
  23. {
  24. // mozemy obsluzyc wyslana wiadomosc
  25. // dodajemy dane(adres IP, e-mail) do pliku
  26. }
  27. else
  28. {
  29. // możemy obsluzyc kolege podając mu za ile bedzie mogl wyslac e-mail
  30. }

W pliku na początku podałem takie dane jak ilość_wiadomości - w tym przykładzie z tego nie korzystam, ale można założyć że dopiero po dwóch wiadomościach blokujemy dalsze wysyłanie, należy zatem skorzystać z $field['ilosc_wiadomosci'] w odpowiednim warunku(linia 45). Zostawiam to jako zadanie dla czytelników.

Wersja bez CI nie bedzie sie wiele roznic - biblioteka chodz w wersji dla CI moze byc wykorzystana w dowolnym miejscu. Poniewaz klaska jest bardzo zgrabnie napisana i bezsensu byloby pisac cos takiego na nowo, lepiej uzyc cos co juz jest i dziala sprawnie. Zatem do dziela - podobnie jw. trzeba sciagnac biblioteczke(jedna klasa). Plik bedzie mial identyczna postac:

  1. ip;czas_wyslania;email;ilosc_wiadomosci
  2. 127.0.0.1;12323423;mail@wlodi.net;2
  3. 192.168.0.1;23324423;mail@wlodi.net;2
-------------------------- -- PHP File -- --------------------------
  1. required_once('CSVReader.php');
  2.  
  3. $parser = new CSVReader();
  4. $data = $parser->parse_file($file_path);
  5.  
  6. // a reszta identyczna jw, jednak dla porządku skopiuje kod
  7.  
  8. $check_ability = true;
  9.  
  10.  
  11. foreach($data as $field){
  12. if($field['ip'] == $ip && $field['email'])
  13. // jesli mamy w bazie juz taki mail lub adres IP
  14. // sprawdzamy czas i okreslamy dostęp
  15. {
  16. if($field['czas_wyslania']+3600 > time())
  17. $check_ability = false
  18. }
  19. }
  20.  
  21. if($check_ability)
  22. {
  23. // mozemy obsluzyc wyslana wiadomosc
  24. // dodajemy dane(adres IP, e-mail) do pliku
  25. }
  26. else
  27. {
  28. // obslugujemy podając za ile bedzie mozna wyslac e-mail
  29. }

Na zakończenie dodam że w ostatniej metodzie można stosować bazę danych - rezygnujemy wtedy z CSV Readera i dane dotyczące użytkowników zapisujemy i odczytujemy z bazy. Jeśli ktokolwiek to przeczyta i nie będzie wiedział jak to zrobić może śmiało pisać, powiększe artykuł o tą metodę.

Chciałbym jeszcze dodac że jest to mój pierwszy artykuł dlatego proszę słowa krytymi(te motywujące) dotyczące artykułu.

W najbliższym artykule będę chciał pokazać swoją koncepcję wykorzystania biblioteki GD oraz tworzenie tekstów(przykład e-mai, gg na stronie) oraz tworzenie wykresów.

wlodi [ 2009-09-28 16:09:47 ]