Category Archives: linux

linux webmastering

(W miarę) bezpieczne uruchamianie skryptów PHP poprzez shell_exec()

Zgod­nie z obiet­nicą dzisiaj część doty­cząca bezpiecz­nego urucha­mia­nia zewnętrz­nych skryp­tów PHP. Od razu mówię, że nie jestem jakimś guru dot. zabez­pie­czeń syste­mów Unix. Zebra­łem rozsy­pane po inter­ne­cie infor­ma­cje i spró­bo­wa­łem złożyć to w całość.

Gist został uaktu­al­niony o klasę ICMPPingProcesserrzuć­cie okiem. Posłuży jako baza do naszych działań.

Jak już wspo­mnia­łem w poprzed­nim wpisie, w Linuk­sie nie można wyko­nać pole­ce­nia socket_create() na typo­wym użyt­kow­niku Apache, czyli www-data. Aby obejść ten problem skorzy­sta­łem z pole­ce­nia posix_seteuid(0) zmie­nia­ją­cego użyt­kow­nika procesu na roota na czas życia obiektu ICMPPing. Z kolei aby wyko­nać to pole­ce­nie musimy stać się rootem :-). Z termi­nala nic trudnego:

sudo php icmp.php http://spiechu.pl

Zosta­niemy zapy­tani o hasło i dosta­jemy odpo­wiedź skryptu. Problem poja­wia się gdy chcemy wywo­łać pole­ce­nie z poziomu innego skryptu PHP. W Ubuntu nie da się/nie umiem (a próbo­wa­łem na wiele sposo­bów) wpisać hasła dla sudo z poziomu lini komend. Wobec tego trzeba zmusić sudo aby nie pytał o hasło. Oczy­wi­ście opcja aby nadać użyt­kow­ni­kowi www-data wszyst­kie upraw­nie­nia roota na stałe nie wcho­dzi w grę. Trzeba maksy­mal­nie zawę­zić „pole manewru” dla www-data.

Wobec tego przy­cze­piamy się do pliku /etc/sudoers, który prze­cho­wuje upraw­nie­nia zwią­zane z pole­ce­niem sudo. Przy­po­mi­nam, że plik sudo­ers należy edyto­wać wyłącz­nie za pomocą pole­ce­nia visudo, a więc w terminalu:

sudo visudo

i jedziemy z edycją:

# Ustawiamy kilka aliasow
# gdy skryptow zrobi sie sporo
# bedzie latwiej zarzadzac tym balaganem
User_Alias APACHE_USER = www-data
Runas_Alias ROOT_USER = root
Host_Alias PHP_MACHINE = dave-ubunciak
Cmnd_Alias ICMP_SCRIPT = /usr/bin/php /home/dave/icmp.php http\://*
 
# Wlasciwe polecenie
APACHE_USER PHP_MACHINE = (ROOT_USER) NOPASSWD: ICMP_SCRIPT

Pole­ce­nie można prze­tłu­ma­czyć tak:
Użyt­kow­nik www-data na maszy­nie dave-ubunciak jako root może wyko­nać skrypt php o nazwie icmp.php bez hasła z para­me­trem rozpo­czy­na­ją­cym się od http://. Wszel­kie odstęp­stwa będą trak­to­wane komu­ni­ka­tem sudo: no tty present and no askpass program speci­fied Sorry, try again.
Od tego momentu możemy w dowol­nym skryp­cie wywoływać:

// na czas developmentu warto na koncu polecenia wpisac
// 2>&1 dzieki czemu strumien stderr przekierujemy na wyjscie
$process = shell_exec('sudo php /home/user/icmp.php http://www.spiechu.pl');
echo $process;

Co jesz­cze można zrobić z samym plikiem icmp.php? Możemy zmie­nić mu użyt­kow­nika i grupę na root i ogra­ni­czyć możli­wość jego wyko­na­nia wyłącz­nie do roota, czyli:

sudo chown root icmp.php
sudo chgrp root icmp.php
sudo chmod 400 icmp.php

Na koniec przy­cze­pię się do samego icmp.php. Napi­sa­łem klasę ICMPPingProcesser, która spraw­dza czy podany adres www jest prawi­dłowy i odsiewa wszystko poza podsta­wo­wym adre­sem hosta. Poni­żej przy­ta­czam w całości:

class ICMPPingProcesser
{
    /**
     * @var string
     */
    protected $urlAddress;
 
    /**
     * @param string $urlAddress
     */
    public function __construct($urlAddress)
    {
        $this->urlAddress = $urlAddress;
    }
 
    /**
     * Returns 'Trying {$urlAddress}: PING RESPONSE: Everything OK'
     * when url address replied correctly
     *
     * @return string
     */
    public function ping()
    {
        try {
            $message = '';
            $urlToPing = $this->processUrl($this->urlAddress);
            if (!$this->isUrlExists($urlToPing)) {
                throw new Exception("{$urlToPing} doesn't exist!");
            }
            $icmp = new ICMPPing();
            $respond = $icmp->sendPacket($urlToPing, 'Everything OK');
            $message = "Trying {$urlToPing}: ";
            $message .= "PING RESPONSE: {$icmp->analyzeRespond($respond)}";
 
            return $message;
        } catch (Exception $e) {
            $message .= $e->getMessage();
 
            return $message;
        }
    }
 
    /**
     * Returns sanitized url host from param
     *
     * @param  string    $urlAddress
     * @return string    url host
     * @throws Exception when url address is not valid
     */
    protected function processUrl($urlAddress)
    {
        $sanitizedUrl = filter_var($urlAddress, FILTER_SANITIZE_URL);
        if ($sanitizedUrl === false || filter_var($sanitizedUrl, FILTER_VALIDATE_URL) === false) {
            throw new Exception("{$urlAddress} is not valid URL");
        }
 
        return parse_url($sanitizedUrl, PHP_URL_HOST);
    }
 
    /**
     * Checks if url address exists
     *
     * @param  string  $urlAddress
     * @return boolean
     */
    protected function isUrlExists($urlAddress)
    {
        if (gethostbyname($urlAddress) === $urlAddress) {
            return false;
        }
 
        return true;
    }
 
}
 
if (PHP_SAPI === 'cli' && isset($_SERVER['argv'][1])) {
    $pingProcesser = new ICMPPingProcesser($_SERVER['argv'][1]);
    echo $pingProcesser->ping();
} else {
    echo 'Not in cli mode or agument not set';
}

Ostat­nie kilka lini­jek spraw­dza czy skrypt odpa­lany jest w trybie lini komend i czy istnieje jakiś argument.

Reasu­mu­jąc:

  1. Właści­cie­lem pliku powi­nien być root i tylko root powi­nien mieć prawo odczy­tać zawartość.
  2. W /etc/sudoers prawo wyko­na­nia skryptu powinno być maksy­mal­nie zawę­żone (żadnych ALL).
  3. Skrypt przed wyko­na­niem powi­nien dokład­nie spraw­dzać podane argumenty.
linux

Zapisywanie plików flash oglądanych w przeglądarce

Nie wiem w którym dokład­nie momen­cie „popsuli” flasha. Do tej pory było tak: oglą­dam filmik na jakimś YouTu­bie, jeśli chcę go zapi­sać — patrzę w /tmp i stam­tąd kopiuję do swojego kata­logu domo­wego. Wczo­raj też tak chcia­łem, a tu psikus. Pusto. Utrud­nie­nie zapewne zrobiono właśnie w celu ukró­ce­nia kopio­wa­nia. Mimo to spró­bu­jemy sobie jakoś pora­dzić. Metoda, którą Wam zapro­po­nuję będzie nawet prost­sza od zaglą­da­nia do /tmp. Działa z każdym serwi­sem stre­amin­go­wym, zarówno na Fire­fok­sie jak i Chrome.

Teraz w kontek­ście ACTów warto sobie zrobić trochę kopii zapa­so­wych ulubio­nych filmików. ;-)

Dla niecier­pli­wych: ścią­gamy skrypt w perlu. Włączamy inte­re­su­jący nas film(y) w prze­glą­darce. Czekamy aż film wczyta się do końca. Klikamy dwukrot­nie na skrypt i urucha­miamy go. W magiczny sposób otrzy­mu­jemy wszyst­kie odpa­lone flashe w formie plików foundflash.#.flv.

Jeśli kogoś inte­re­suje co robi skrypt to jest tak:

  1. Pole­ce­niem lsof listuje wszyst­kie pliki otwarte przez procesy.
  2. Szuka w nich ciągu Flash.
  3. W kata­logu /proc/nr_znalezionego_procesu/fd szuka numerka pliku, który ma dowią­za­nie do pliku /tmp/FlashXXA_coś_tam_dalej rezy­du­ją­cego w pamięci RAM.
  4. kopiuje znale­ziony plik i dodaje mu rozsze­rze­nie flv.

Z konsoli można sobie pora­dzić tak:
$ lsof | grep Flash
chromium- 2930 nazwa 24w REG 8,1 12050048 529078 /tmp/FlashXXA6m3J2 (deleted)
$ cd /proc/2930/fd
$ ls -l | grep Flash
l-wx------ 1 nazwa nazwa 64 2012-02-07 20:04 24 -> /tmp/FlashXXA6m3J2 (deleted)
$ cp 24 /home/nazwa/znaleziony_flash.flv

linux

Jak poprawnie uczestniczyć w rozwoju projektu na Github

Najpierw rzecz niezwią­zana z tema­tem: Używa­cie Google Plus? Dodaj­cie mnie do swoich kręgów. Jak ktoś nie ma zapro­sze­nia to napi­sać maila — podeślę.

Od kilku dobrych miesięcy GitHub jest moim ulubio­nym serwi­sem z dzie­dziny progra­mo­wa­nia. Najlep­sze w nim jest to, że możemy za całko­witą darmo­chę tworzyć nowe projekty Open Source i obser­wo­wać rozwój istnie­ją­cych. A co, jeżeli chcemy dorzu­cić do kodu coś od siebie? Wtedy musimy prze­strze­gać pewnych proce­dur, o których dzisiaj będzie wpis.

Zanim jednak to nastąpi, musimy nauczyć się obsługi Gita, czyli systemu kontroli wersji, który obowią­zuje na GitHu­bie. Przy­znam, że trzeba się trochę pomę­czyć z konsolą żeby to ustroj­stwo zaczęło nas słuchać. Jest oczy­wi­ście kilka nadbu­dó­wek graficz­nych, jednak aby dobrze poznać Gita, trzeba używać konsoli (analo­gicz­nie do stuka­nia HTMLCSS w notat­niku, bez żadnych podpo­wia­da­czek). Nie będę w tym miej­scu zasu­wał wam porad­nika obsługi Gita, zamiast tego skie­ruję do książki Pro Git o Gicie, rozwi­ja­nej… w Gicie :-D Pierw­sze trzy rozdziały są ładnie prze­tłu­ma­czone na polski, ja trochę tłuma­czę kawałki czwartego.

Zakła­dam, że macie zało­żone konto na GitHub.com. Zakła­dam również, że na swojej lokal­nej maszy­nie macie zain­sta­lo­wa­nego Gita. W Ubuntu proces składa się z wpisa­nia w terminalu

sudo apt-get install git-core

na Window­sie nie wiem, nie znam się, nie chcę wiedzieć, nie używam ;-)

Do rzeczy:
1. Znaj­du­jąc się w projek­cie, który chcemy wspo­móc, klikamy przy­cisk Fork, co utwo­rzy naszą osobi­stą wersję projektu, nad którą mamy całko­witą władzę.

2. Tworzymy na naszym kompu­te­rze jakiś kata­log, w którym trzy­mana będzie kopia projektu. Następ­nie inicju­jemy Gita i klonu­jemy naszego forka na lokalną maszynę.

git init
git clone https://nasz_login@github.com/nasz_login/forkowany_projekt.git

3. Przy­jęło się, że repo­zy­to­rium, z którego forku­jemy nazywa się upstream, a nasz fork nazywa się origin. Repo­zy­to­rium origin tworzy się auto­ma­tycz­nie przy klono­wa­niu. Upstream musimy sobie stwo­rzyć sami.

git remote add upstream https://github.com/projekt_glowny/projekt_glowny.git

4. Tworzymy sobie gałąź zwią­zaną z proble­mem, którym chcemy się zająć (np. tłuma­cze­niem, zgło­szo­nym bugiem itp.) i prze­cho­dzimy do niej (w moim przy­padku polish-translation). Nigdy nie wpro­wa­dzamy zmian w kodzie gałęzi master!

git branch polish-translation
git checkout polish-translation

W tym momen­cie możemy dziel­nie walczyć z kodem. Pamię­tamy jednak, że inni w tym samym czasie również pracują nad projek­tem i wpro­wa­dzają zmiany. Musimy co jakiś czas zsyn­chro­ni­zo­wać się z tym co wytwo­rzyli. Proce­dura składa się z kilku czyn­no­ści:
– pobra­nie aktu­al­nej wersji repo­zy­to­rium upstream
– przej­ście do naszej głęzi master i synchro­ni­za­cja jej z upstream
– synchro­ni­za­cja naszej gałęzi robo­czej z naszym masterem

git fetch upstream
git checkout master
git rebase upstream/master
git checkout polish-translation
git rebase master

Do synchro­ni­za­cji zawsze używamy pole­ce­nia rebase, nigdy merge! Rebase opisany w podręcz­niku.

5. Zbijamy nasze cząst­kowe zatwier­dze­nia zmian (brzydko zwane commi­tami) w jeden. Osoba, która będzie włączała naszą gałąź do głów­nego projektu nie będzie miała raczej ochoty wrzu­cać 10-ciu naszych commi­tów w stylu WTF it isn’t working?, fancy addi­tion here, maybe this one will work itp. Wystar­czy poje­dyn­cze Fixed issue #5.
Począt­ku­ją­cym może spra­wić nieco trud­no­ści, dlatego krok po kroku wytłu­ma­czę:
Powiedzmy, że nasze tłuma­cze­nie składa się z 5 commi­tów. Jeżeli nie wiemy to używamy pole­ce­nia git log. Musimy cofnąć się o te 5 zmian i skleić je w jedną. Wpisu­jemy w terminalu

git rebase -i HEAD~5

W edyto­rze pick zosta­wiamy tylko w pierw­szym naszym commi­cie. Resztę zamie­niamy na squash. Zapi­su­jemy zmiany poprzez Ctrl+O, Enter i Ctrl+X. Następ­nie w pierw­szej linijce wpisu­jemy ładny komu­ni­kat, a resztę lini­jek możemy poprze­dzić haszem # Znowu Ctr+O, Enter, Ctrl + X. Spraw­dzamy czy komu­ni­kat jest taki jak trzeba poprzez git log. Jeżeli nie, zawsze możemy użyć

git commit --amend

i popra­wić treść ostat­niego commita.

6. Na wszelki wypa­dek wyko­nu­jemy ponow­nie proce­durę synchro­ni­za­cji z upstream. Jeżeli wszystko poszło dobrze, jeste­śmy gotowi do prze­sła­nia naszych zmian na serwer origin (nasz fork na GitHubie).

git push origin polish-translation

7. Wcho­dzimy na stronę ze swoim forkiem i spraw­dzamy czy w sekcji Switch Bran­ches poja­wiła się nasza gałąź. Jeżeli tak to klikamy na pull requ­est, wybie­ramy naszą gałąź i zatwier­dzamy prośbę o połą­cze­nie z gałę­zią master forko­wa­nego projektu.
Teraz pozo­staje tylko cier­pli­wie czekać i mieć nadzieję, że auto­rom się spodoba i zatwier­dzą zmiany. Jeżeli nie, w sekcji pull requ­ests głów­nego projektu na pewno dosta­niemy komu­ni­kat co jest nie tak.