Odczytywanie informacji EXIF zdjęcia w PHP

Większość aparatów cyfrowych w pliku ze zdjęciem zapisuje nagłówki informujące o parametrach zrobionego zdjęcia, użytego aparatu. Dane te są zapisane w formacie IPTC i zawierają takie dane jak: datę i czas zrobienia zdjęcia, czas naświetlania, przysłonę, ogniskową, ISO, program paratu, nazwę producenta i modelu aparatu etc…

Informacje te można odczytywać i modyfikować w programach graficznych, standard IPTC szczególnie przydatny jest w fotografii stockowej (opis wykorzystania standardu IPTC).

Dane zapisane w pliku JPEG/TIFF możemy w prosty sposób odczytać w skrypcie PHP. Dostępna jest specjalna bibloteka EXIF, która nam to umożliwia. Aby móc korzystać z tej biblioteki PHP musi być skompilowane z opcjami –enable-exif i –enable-mbstring.

Poniżej przykładowa funkcja, zwracająca informacje o zdjęciu

/**
* Metoda zwraca informacje o zdjęciu
*
* @param string $file - sciezka do zdjecia
* @return array od mixed - informacje o zdjeciu
* @access public
*/
function getImageInfo( $file ) {
	$exif = exif_read_data( $file, "IFD0" );
	if( $exif ) {
		$return exif_read_data( $file, 0, true );
	}
}

Funkcja zwraca tablice z danymi, które możemy zaprezentować na stronie przy zdjęciu.
Poniżej przykładowy wynik powyższej metody.

Array
(
    [FILE] => Array
        (
            [FileName] => IMG_7378.jpg
            [FileDateTime] => 1198952415
            [FileSize] => 1240033
            [FileType] => 2
            [MimeType] => image/jpeg
            [SectionsFound] => ANY_TAG, IFD0, THUMBNAIL, EXIF
        )

    [COMPUTED] => Array
        (
             => width="2664" height="2304"
            [Height] => 2304
            [Width] => 2664
            [IsColor] => 1
            [ByteOrderMotorola] => 0
            [CCDWidth] => 17mm
            [ApertureFNumber] => f/18.0
            [Thumbnail.FileType] => 2
            [Thumbnail.MimeType] => image/jpeg
        )

    [IFD0] => Array
        (
            [ImageDescription] => Red Tomato isolated on white background
            [Make] => Canon
            [Model] => Canon EOS 350D DIGITAL
            [Orientation] => 1
            [XResolution] => 2400000/10000
            [YResolution] => 2400000/10000
            [ResolutionUnit] => 2
            [Software] => Adobe Photoshop CS3 Windows
            [DateTime] => 2007:12:29 19:20:14
            [Artist] => Marcin Mamala
            [Exif_IFD_Pointer] => 296
        )

    [THUMBNAIL] => Array
        (
            [Compression] => 6
            [XResolution] => 72/1
            [YResolution] => 72/1
            [ResolutionUnit] => 2
            [JPEGInterchangeFormat] => 798
            [JPEGInterchangeFormatLength] => 3028
        )

    [EXIF] => Array
        (
            [ExposureTime] => 1/20
            [FNumber] => 18
            [ExposureProgram] => 1
            [ISOSpeedRatings] => 100
            [ExifVersion] => 0221
            [DateTimeOriginal] => 2007:12:29 18:06:50
            [DateTimeDigitized] => 2007:12:29 18:06:50
            [ShutterSpeedValue] => 4321928/1000000
            [ApertureValue] => 833985/100000
            [ExposureBiasValue] => 0/2
            [MaxApertureValue] => 434375/100000
            [MeteringMode] => 5
            [Flash] => 16
            [FocalLength] => 43
            [ColorSpace] => 65535
            [ExifImageWidth] => 2664
            [ExifImageLength] => 2304
            [FocalPlaneXResolution] => 3456000/874
            [FocalPlaneYResolution] => 2304000/582
            [FocalPlaneResolutionUnit] => 2
            [CustomRendered] => 0
            [ExposureMode] => 1
            [WhiteBalance] => 0
            [SceneCaptureType] => 0
        )

)

Działanie response w FireBug

Co to jest Firebug nie będę wyjaśniał, każdy webdeveloper powinien wiedzieć o co chodzi. W dwóch słowach: jest to bardzo przydatny plugin do Firefoxa umożliwiający m.in wyświetlenie wszystkich request-ów wygenerowanych przez wygenerowaną stronę. Dotyczy to zarówno żądań wygenerowanych przez odwołania do obrazków, css-ów jak i żądań wygenerowanych przez skrypty javascript-owe i flash-owe. (inspect->net-all)

Przy każdym requescie można podglądnąć 'response’ – czyli odpowiedź skryptu na wysłane żądanie. Jest to szczególnie przydatne w przypadku debugowania serwerowych skryptów aplikacji, które dostarczają dane skryptom działającym po stronie przeglądarki (np. javascript, flash).

Ale uwaga! Należy pamiętać, że w momencie gdy w Firebugu przy danym requescie klikniemy na response to prezentowany response niestety nie jest tym oryginalnym tylko skrypt, którego dotyczy jest wywoływany ponownie.

Wszystko byłoby fajnie gdyby nie to, że niektóre skrypty serwerowe wykonują operacje, które nie można powtórzyć (np. INSERTy, DELETy w bazie) i w takim przypadku kolejne wywołania zwrócą już błędne dane. W takim wypadku jedyna sensowną opcją debugowania pozostaje zapis do pliku. Nie jest to niestety zbyt przyjazna metoda, ale cóż…jedyna skuteczna.

Bot alexa.com i znikające dane w serwisach.

Dostałem ostatnio zgłoszenie, że w aplikacji, którą jakis czas temu napisałem zniknęło część kluczowych danych. Mniejsza z tym, jakie to były dane, powiedzmy ze chodzi o dane kont użytkowników, których usunięcie pociągnęło za sobą kaskadowe skasowanie się wielu innych danych z tabel zależnych.Sytuacja wydała się dosyć dziwna i podejżana. Pierwsza myśl, która zawitała w mej głowie to „pewnie, któryś z adminów niechcąco skasował” było to prawdopodobne tym bardziej, że kiedyś miało juz miejsce podobne zdarzenie. Tym razem jednak żaden z administratorów nie przyznał się do omyłkowego skasowania danych.

Kolejny powód zniknięcia danych o jakim pomyślałem to włam jakiegoś pseudo-hakera i celowe zniszczenie danych. Zaglądam do access-logów serwera z tego feralnego dnia, grepuje po pliku logowania i widze, że logowania pochdziły jedynie ze sprawdzonych IP. Hmm…gerpuje więc po pliku zawierającym sktypt kasujący głowne dane i oczom moim ukazuje się kilka lini, każda postaci:

user 64.208.172.180 - - [29/Oct/2007:17:28:46 +0100] "GET /skrypt_kasujacy.php?userID=15 HTTP/1.0" 302 - "-" "ia_archiver"

Jakim cudem ktoś wywołał (z powodzeniem) skrypt kasujący dane bez wcześniejszej poprawnej autoryzacji logowania? Od razu sprawdzam źródła aplikacji, czy aby jest dobrze zabezpieczona i… jest zabezpieczona poprawnie. wtf?

Totalnie zdziwiony stwierdzilem, że trzeba troche poguglać na temat 64.208.172.180 i „ia_archiver”. Po krótkiej chwili okazało się, że nazwa user agenta „ia_archiver” odpowiada nazwie z jaką się przedstawia bot alexa.com, aby mieć pewność, że ktoś się pod niego nie podszywa weryfikuje także IP, które potwierdza, że była to wizyta z serwera crawlera alexa.

Dobra to juz wiem, że dane nie skasował żaden haker tylko bot, ale to wcalenie jest pocieszająca myśl, to jest tragedia gdyż wynka, że moja aplikacja jest tak dziurawa, że nawet boty potrafią ją zaindeksować! Masakra…

Drążąc temat dalej dowiedziałem się, że bot alexa nie jest zwykłym botem jakich na pęczki w internecie gdyż oprócz latania jak głupek po wszystkich stronach www bieże informacje o stronach do odwiedzenia z Alexa Toolbar, który jaki dodatek można zainstalować w przeglądarce internetowej, także w ten sposób może zaindeksować strony, do których nie prowadzi żaden link a także te, które wymagają autoryzacji.
I tak właśnie było w tym wypadku, jeden z administratorów aplikacji miał zainstalowany alexa toolbar w swojej przeglądarce i przez to nawet gdy poruszał się w sekcji wymagającej autoryzacji to cały czas alexa była poprzez toolbar informowana o URLach przez niego odwiedzanych. W ten sposób w pewnym momencie bot zaindeksował stronę z listą użytkowników, przeparsował ją i wyciągnął linki prowadzące do usunięcia kont a następnie je wywołał i tym samym usunął konta.

Jak się okazało wywołanie linków kasujących dane zawsze miało miejsce w momencie gdy administrator był zalogowany w systemie. Domyślam się, że jakimś cudem bot poprzez toolbar przechwytywał zautoryzowaną sesje użytkownika i dzięki temu uzyskiwał dostęp do stron, do których dostępu mieć nie powinien!

Jak się zabezpieczyć?
Najlepiej stworzyć plik robots.txt w katalogu glównym aplikacji i wpisać w nim instrukcje nie pozwalającą botom na odwiedzani plików administracyjnych.

user-agent: *
Disallow: /admin_pages*