używanie tajnych haseł w automatach

co jakiś czas pojawia mi się potrzeba użycia “tajnego" hasła w programie który ma działać w miarę automatycznie.

przez “w miarę" rozumiem, że mam kontrolę nad tym kiedy i jak go uruchamiam.

ostatnio pojawił mi się ten problem gdy chciałem napisać program który wykrywa zmiany na moim koncie w banku (mbank). wolałem (z oczywistych przyczyn) nie:

  • wpisywać konta i hasła do kodu programu
  • wpisywać konta i hasła do pliku konfiguracyjnego
  • podawać tych danych jako zmienne środowiskowe
  • podawać tych danych jako parametry wykonania

z drugiej strony – soft miał działać bez mojej dalszej ingerencji.

na początku chciałem by to był cron. ale z zastrzeżeniami z punktów powyżej chyba nie da się crona napisać.

więc stwierdziłem – nie może być cron? ok. niech będzie daemon.

co więc można zrobić?

program pyta się użytkownika przy uruchomieniu o dane (konto/ hasło). potem się loguje na stronę mbanku. jeśli proces logowania sie nie uda, trudno , program wypisuje błąd, i się kończy.

jeśli jednak się uda, to program kasuje z pamięci zawartość numeru konta i hasła, po czym przechodzi w tło i cyklicznie odpytuje mbank o saldo – korzystając z cookie.

oczywiście nadal istnieje ryzyko, że ktoś “ukradnie" mi cookie, ale ponieważ cookie są przechowywane w pamięci to jest to dosyć trudne.

pewnym utrudnieniem jest fakt, iż polegamy na cookie, gdyż cookie ma dosyć krótką żywotność. co oznacza, iż muszę cyklicznie co minutę odświeżać dane. testowanie konta co 5 minut wymagałoby już trzymania w pamięci hasła i numeru konta, co staje się potencjalnie niebezpieczne.

soft oczywiście nie jest idealny, ale może komuś z was się do czegoś przyda.

a może wskażecie mi błędy w moim rozumowaniu 🙂

obiektowy perl – jak?

na wstępie – ja wiem, że perl nie jest prawdziwie obiektowy. nie ma enkapsulacji. jeśli szukasz tutaj okazji do zwady to jej nie znajdziesz. wiem, że java, smalltalk, python, ruby i pewnie z 3 tony innych języków mają lepszą obiektowość.

nie musisz tego dalej czytać. to niebezpieczne. zawiera kawalki kodu perlowego, które (co dowiedli angielscy naukowcy) są niebezpieczne i powinny być traktowane tak jak niebezpieczne odpady z elektrowni jądrowych.

papa.

ok. teraz, odstraszywszy cieniasów można przejść do sedna.

programowanie obiektowe w perlu jakie jest każdy koń widzi. jest kilka metod, prostych, skomplikowanych, fajnych i totalnie niefajnych.

najprostszą metodą na zrobienie obiektowej klasy jest coś takiego:

package costam;<br />
use strict;<br />
sub new {<br />
my $class = shift;<br />
my $self  = {};<br />
bless $self, $class;<br />
return $self;<br />
}

i potem w każdej funkcji (która teraz jest metodą) piszemy na początku:

my $self = shift;

i już. dane obiektu trzymamy w $self, traktując go jako referencję na hasza. proste, miłe i skuteczne.

część mądrych ludzi stwierdziła, że jest to niebezpieczne bo z zewnątrz każdy może zobaczyć jakie zmienne (wartości w haszu) ma obiekt zadeklarowane. stąd pojawiła się idea obiektów inside-out, które są bezpieczne (poniekąd), modne i ogóle fajne. i których ni chu-chu nie lubię i polubić chyba nie zdołam.

trzymam się za to obiektów bazujących na haszach.

pisanie za każdym razem my $self = shift; jest upierdliwe.

brak akcesorów do zmiennych bywa niemiły.

konieczność pisania za każdym razem praktycznie tego samego sub new {} jest wkurzająca.

czy nie da się z tym nic zrobić?

da! na każdy problem jest lekarstwo (w przypadku perla jest to wiele lekarstw, gdzie każdy ma ulubione inne).

moim ulubionym lekarstwem jest spiffy. miły moduł, którego użycie wygląda tak:

use Spiffy qw( -Base );

i to wszystko.

co daje?

no cóż. zacznijmy od przepisanie pierwszego przykładu (klasa co nic nie robi i ma tylko konstruktor). wersja spiffy:

package costam;<br />
use strict;<br />
use Spiffy qw( -Base );

i to wszystko. konstruktor jest tworzony automatycznie. co jeszcze?

otóż pojawia się kilka fajnych rzeczy:

  1. upierdliwe my $self = shift; znika – nie trzeba go pisać. spiffy dodaje to automatycznie do kodu w czasie kompilacji. niby pierdoła, ale jak ma się moduł z np. 40 metodami, to to już jest 40 linijek zbędnego kodu mniej!
  2. możliwość definiowania pól (z automatycznym tworzeniem akcesorów):
    field ‘logfile' => undef;
    zdefiniuje metodę $self->logfile która w zależności czy zostanie wykonana bez czy z parametrami to odpowiednio zwróci albo ustawi wartość pola ‘logfile' (podawanie wartości domyślnej => jest opcjonalne)
  3. pola stałe:
    const ‘debuglevel' => 3;
    zasadniczo generuje metodę debuglevel która zwraca wartość 3
  4. stub'y – metody które istnieją, ale próba wywołania skończy się błędem. służą do wymuszania przedefiniowania określonych metod w klasach dziedziczących:
    stub ‘save';
    musi zostać przedefiniowany, inaczej każde wywołanie $object->save zakończy się błędem (informującym o przyczynie, a nie czymś w stylu “undefined subroutine").
  5. uproszczone wołanie metod z klas dziedziczonych. bez spiffy'ego trzeba było robić coś w stylu $self->SUPER::metoda(@_); teraz wystarcza proste:
    super;
  6. syntactic sugar – w modułach które używają spiffy'ego nie trzeba podawać już tego “1;" na końcu.
  7. wszystkie zdefiniowane pola są honorowane przy wywołaniu konstruktora. tak więc można:
    package cos;
    use strict;
    use Spiffy qw( -Base );
    field ‘x' => 0;
    field ‘y' => 0;
    package main;
    my $object = cos->new( x=> 10 );
    i zadziała to w pełni logicznie 🙂

oczywiście nie ma róży bez kolców.

ponieważ dodanie kilku z powyższych punktów wymusza używania czarnej magii (filtry kodu), spiffy nie jest do końca zgodny ze wszystkim co chodzi po ziemi.

pojawiły się nawet opinie, że jakieś moduły są psute używaniem spiffy'ego – czego osobiście przez kilka lat spiffy'owania nie zauważyłem, ale nie twierdzę, że problem nie istnieje.

istotnym problemem jest coś takiego.

bez spiffy'ego można napisać klasę dziedziczącą z kilku klas tak:

use base qw( klasa::a klasa::b );

ale jeśli używamy spiffy'ego to sprawa się komplikuje i trzeba to rozbić:

use base qw( klasa::a );<br />
use base qw( klasa::b );

nie jest to w/g mnie wielkie utrudnienie – zwłaszcza biorąc pod uwagę ile rzeczy spiffy mi zaoszczędza.

na koniec drobna informacja o innych rzeczach jakie spiffy daje – z tym, że są to rzeczy z których zdecydowanie rzadziej korzystam:

  • prywatne metody. metoda zapisana poprzez:
    my sub jakas_metoda {
    staje się metodą prywatną i może zostać wywołana jedynie przez inne metody tego obiektu
  • dopisanie XXX pozwala na proste debugowanie kodu – poprzez wypisywanie ładnie sformatowanych (yaml) sturktur danych. przykład użycia z manuala:
    XXX my @stuff = grep { /keen/ } $self->find($a, $b);

tak to wygląda. dzięki temu jak to działa można napisać np. coś takiego:

package modulik;<br />
use strict;<br />
use Spiffy qw( -Base );<br />
field 'x' => 0;<br />
field 'y' => 0;<br />
const 'wsp_x' => 1.33;<br />
sub suma {<br />
return ( $self->x + $self->y ) * $self->wsp_x;<br />
}<br />
package main;<br />
use strict;<br />
my $object = modulik->new(x => 10, y => 12);<br />
print $object->suma, "\n";

nie jest to może najbardziej zaawansowany kod jaki można sobie wyobrazić. ale spróbujcie go zapisać bez spiffy'ego.

aha. no i spiffy, wewnętrzenie trzyma wszyskie pola (field) jako klucze w $self – czyli tak jak perl przykazał.

perl hacks – hit czy kit?

w firmie mamy kupioną książkę “perl hacks" napisaną przez chromatic‘a z damianem conwayem i curtisem poe.

książkę czytał znajomy, przeczytał. trafiła do mnie.

otworzyłem na chybił – trafił. i znalazłem taki o to kawałek:

zaczęło się od tego, że funkcja open() zwraca kod błędu. a lepiej by było by zwracała exceptiona.

rozwiązanie z książki:

use File::Exception;
sub open_file {
my ($name, $mode) = @_;
open my $fh, $mode, $name or File::Exception->throw( "file" => $name, "mode" => $mode, "error" => "$!" );
return $fh;
}

no i potem wszędzie w kodzie zamieniamy open(…) na open_file(…).

eh. i to ma być efektywne programowanie?

jakie widzę z tym problemy?

  1. konieczność doinstalowania nowego modułu. nie jest to problem per se, ale niepotrzebna czynność
  2. konieczność modyfikowania kodu – zamiana wszędzie open na open_file
  3. jeśli gdziekolwiek robimy własną obsługę błędu open() to nie możemy zamienić na open_file
  4. jeśli gdziekolwiek używamy open() do np. czytania z pipe'a, to też nie możemy zamienić

ogólnie – przerobienie istniejącego kodu w/g tej regułki uważam, za całkowitą porażkę.

a jak zrobić to sensownie? użyć standardowego w perlu modułu fatal:

use Fatal qw(open);

i to wszystko. nie trzeba przerabiać kodu – wystarczy jedna deklaracja. a jeśli gdzieś mamy własny handling? wystarczy zmodyfikować use'a:

use Fatal qw(:void open);

no i dodatkowo – fatal potrafi obsłużyć nie tylko open. zasadniczo można przez niego zrobić exceptiony do praktycznie każdej funkcji/builtin'a.

i w ten oto sposób zraziłem się do książki poprzez przeczytanie losowego kawałka. zobaczymy co będzie dalej.

przydatne moduły : io::prompt

jakiś czas temu dosyć przypadkowo dowiedziałem się o istnieniu modułu io::prompt.

gdy zacząłem go używać, nie był jeszcze widoczny “tak po prostu" w cpanie, trzeba było instalować przez install DCONWAY/IO-Prompt-

od tamtej pory moduł został już całkiem upubliczniony i wystarczy install IO::Prompt w shellu cpanowym by móc sie nim cieszyć.

co dokładnie moduł oferuje? uproszczone wprowadzanie danych.

czyli tak naprawdę co?

tak naprawdę do sporo. trzeba by poczytać mana by wszystko ogarnąć, ale tak z grubsza pokażę:

$ perl -e 'use IO::Prompt; my $q = prompt "się nazywasz, ? "; print "[$q]\n"'

miłe. zwracam uwagę, że $q nie ma znaku “enter" na końcu – prompt autoamtycznie chompuje. da się to wyłączyć oczwiście.

$ perl -e 'use IO::Prompt; my $q = prompt -p => "hasło: ", -e => "*"; print "[$q]\n"'

hmm .. proste i przyjemne wprowadzanie hasła. bez znajomości trybów terminali itd.

$ perl -e 'use IO::Prompt; my $q = prompt -p => "hostname: ", -d => "localhost"; print "[$q]\n"'

domyślna wartość odpowiedzi 🙂

$ perl -e 'use IO::Prompt; my $q = prompt -p => "wybierz: ", -m => ["depeche mode", "guns n roses", "metallica"]; print "[$q]\n"'

proste menu.

$ perl -e 'use IO::Prompt; my $q = prompt -p => "ile masz lat: ", -i; print "[$q]\n"'

prościutka walidacja typu – czy to integer.

możliwości jest więcej. można wstawiać własne walidatory odpowiedzi. można czyścić ekran przed pytaniem. można przyjmować wartości z stdin lub wymuszać przyjmowanie tylko z terminala (co powoduje, że nie działa coś jak echo 12 | perl -e …), można przyjmować tylko pojedynczy znak, można nawet emulować działanie wolnej linii transmisyjnej między kompem a klawiaturą.

w pełni polecam każdemu kto kiedykolwiek napisać coś co musiało odpytywać usera o cokolwiek na konsoli.