PHP: Как да накараме бот да ни счупи сайта?

Posted by Асен on June 11th, 2009

Днес видях нещо което ме изуми! Един обикновен идексиращ бот (на търсеща машина) затри съдържанието на цял сайт, докато се е опитвал да индексира админ панела, при това без да вижда какво има вътре тъй като има логин система за администраторите. И всичко това заради малък пропуск в php кода.

Ето и по-дългият вариант на историята.

Един клиент ми се обади и се оплака, че сайта му е хакнат. Аз гледам, няма нищо в базата данни от съдържанието на сайта. В лога на сървъра се вижда как един бот се е опитвал да влезе в админ панела, но е получавал 302 редирект към страницата за вход. Никой друг не се е опитвал да влезе.

Какво се е случило на този далечен сървър???

След кратко разсъждение стана ясно, че има двама виновници. Първият е програмиста който е писал системата, а вторият е собственика на сайта който просто е инсталирал toolbar на търсеща машина на браузера си.

Проблема в кода може би може да бъде срещнат в повечето сайтове написани на php. Ето го и въпросното бъгче:

if (!isset($_SESSION['Admin'])){
header(“location:login.php”);
}

Тук голяма роля играе и настройката на сървъра, но тъй като това е известен проблем програмиста е трябвало да го съобрази.

Във въпросния код функцията header() казва на браузера  да се пренасочи към страницата за вход, но не казва на сървъра да спре изпълнението на текущата страница. Т.е. ако аз нямам права да изтрия дадена статия (например), но имам адреса чрез който може да се изтрие, след като го посетя ще видя страницата искаща парола, но сървъра ще изпълни и изтриването. Гадно а?

А как бота е разбрал адресите които трябва да се посетят за изтриване на съдържание от сайта? Отговора е още по-смешен и от php кода. Администратора, който има инсталиран toolbar на търсещата машина, без да знае е дал тези адреси на бота, като е влезнал да администрира сайта си.

Това отново повдига в съзнанието ми въпроса колко лична информация събират големите търсачки за нас, без дори да се замислим, че сме им позволили.

Извода за програмиста (който вероятно няма и да разбере какво е направил) е, че трябва да окаже на съвръра да спре да изпълнява команди ако има пренасочване, ето така:

if (!isset($_SESSION['Admin'])){
header(“location:login.php”);
exit(0);
}

Също така да се научи да използва robots.txt.

Извода за клиента е, че е по-добре да плати веднъж много пари за стабилен софтуер, отколкото да мине тънко и да го хакне тарсачка, преди да реши да даде много пари за отстраняването на проблема :D

Erlang

Posted by Асен on June 6th, 2009

Наложи ми се да търся информация за езика Erlang за да направя един проект за университета. Но се оказа че за този език цялата информация е на английски и е сравнително трудно да се намери на едно място.

Езика има добра документация, но иска доста ровене из нея за да се разбере добре Erlang.

Публикувам тук информацията която събрах и преведох. Не съм програмирал на този език, така че малки неточности са възможни ;)

История

Разработването на Erlang е започнало през 1986 г. в лабораторията за компютърни науки на Ericsson. Той е проектиран с ясна цел: „да осигури по-добър начин за програмирането на телефонни приложения”. По това време конвенционалните езици за програмиране не можели да се справят с проблемите с които се сблъскват телефонните приложения. Налагало се софтуера да се справя с десетки хиляди паралелни процеси и да може да бъде променян докато работи, за да не спират телефонните услуги докато се извършват обновления. Софтуера също така трябвало да работи в реално време строго за определени операции, и не толкова точно за други.

Основната задача на езика била да се справя с проблеми като:

§ Обработването на много паралелни задачи

§ Действия които да се изпълнят в точно определено време или за определено време

§ Системи които да се разполагат на няколко компютъра

§ Взаимодействие с хардуер

§ Много големи софтуерни системи

§ Сложна функционалност като взаимодействие между различни системи

§ Непрекъсната работа в продължение на години

§ Поддръжка на софтуера без да се спира системата

§ Да реагира добре при грешки в хардуера или софтуера

Когато Erlang се появил имало сравнително малко области където се изисквал софтуер който да работи непрекъснато. Но с нарастването на популярността на Интернет и на нуждата от предоставяне на постоянна възможност за достъп до него нараснали и проблемите които Erlang може да реши. Например, създаването на огромен уеб сървър, с динамично обновление на кода, обработващ милиони заявки на ден е сходно със създаването на софтуер, който да управлява телефонните разговори.

От началото Erlang е създаден като инструмент който да върши работа. Често нови функции били добавяни в езика за да се справи с определен проблем и функции които не се използвали били премахвани. Процеса на работа бил толкова бърз, че много от добавянията и премахванията на елементи от езика дори не били записвани.

Предназначение, тип, особености

Erlang е функционален език, създаден за писането на програми които работят постоянно. Използват се паралелни процеси за да се структуира програмата. Процесите нямат споделена памет и комуникират чрез несинхронизирано предаване на съобщения. Процесите в Erlang са много леки и пренадлежат на езика, а не на операционната система.

Езика има механизми които позволяват промяната на кода на програмите докато те работят. Има механизми и за реализирането на системи които никога не спират.

Повечето програми написани на Erlang ще вървят по-бързо ако се изпълняват на многопроцесорни машини.

Типове данни

Erlang има няколко различни типове данни. Всяка частица данни, независимо от какъв тип е се нарича терм.

Number

Има два типа числа: цели (integer) и с плаваща запетая (float).

Освен стандартните означения на числата има и две специфични за Erlang означения:

$char – връща ASCII стойността на знака
Например:

2> $A.
65

В примера $A връща ASCII стойността на знака А

base#value – където base е базата на бройната система на числото, а value е неговата стойност. Базата трябва да е цяло число от 2 до 36.
Например:

1> 2#101.
5
2> 16#1f.
31

Atom

Atom е константа с име. Атома трябва да е обграден с единични кавички ( ‘ ) ако не започва с малка буква или съдържа специални символи.

Примери за atom:

hello
phone_number
'Monday'
'phone number'

Atom-ите много приличат на дефинирането на символични константи в С. Но в Erlang тяхната стойност е самият atom. Т.е. ако се напише команда която е просто atom ще се изпише самият atom.
Пример:

1> hello.

hello

Reference

Това е терм който е уникален за цялата система. Създават се с erlang:make_ref(). Могат да бъдат сравнявани за еднаквост.

Port Identifier

Идентификатор на Erlang порт.

Портовете се създават с функцията open_port() и са средство за комуникация с програми извън системата на Erlang. Когато се създаде порт, Erlang програмата може да комуникира чрез него изпращайки и получавайки поредица от байтове.

Erlang процеса който създава порта се нарича негов собственик и цялата комуникация от и към порта трябва да минава през него. Ако собственика на порт прекрати съществуването си порта се затваря.

Синтаксис за отваряне на порт:

Port = open_port(PortName, PortSettings)

Pid

Идентификатор на процес.

Функциите spawn(), spawn_link() и spawn_opt(), които се използват за създаването на процеси, връщат стойност от този тип.

Boolean

В Erlang няма boolean тип. Вместо него се използват аtom-ите true и false за да се означават boolean стойности.

Пример:

1> 2 =< 3.
true
2> true or false.
true

Структури от данни

Bit Strings (Binaries)

Тази структура от данни се използва за да пакетира поредица от битове и да ги разположи в паметта. По този начин могат да се съхраняват големи количества данни в сравнително малко памет.

Синтаксиса е <<E1,…,En>> като Еi е сегмент от bit string-a. Еi е стойност, като може да съдържа и информация за размера и типа и.

Примери:

Bin1 = <<1,17,42>>.
Bin2 = <<"abc">>.

Fun

Това са анонимни функции (обекти). С помощта на тази структура функции без име могат да се дефинират директно в израз или като параметър към друга функция.

Пример:

1> Hypot = fun(X, Y) -> math:sqrt(X*X + Y*Y) end.

2> Hypot(3,4).

5.00000

Tuple

Това е съставен тип данни, който съдържа фиксиран брой терми.

{Term1,…,TermN}

Всеки терм в tuple се нарича елемент, броят на елементите се нарича размер на tuple-a.

За разлика от структурите в С, tuple в Erlang нямат име, нито пък елементите им имат имe. Затова е честа практика да се поставя atom като първи елемент на tuple-a, който описва за какво служи tuple-a. Така кода става много по-четлив. Tuple структурите могат да бъдат вложени.

Пример (създаване на tuple):

F = {firstName, joe}.

Пример (извличане на стойност от tuple):

1> element(2,F).

joe

Съществуват много вградени функции за различни операции с tuple. Например element(), setelement(), tuple_size().

List

Това е съставен тип данни, който съдържа променлив брой терми.

[Term1,...,TermN]

Всеки терм в list се нарича елемент, броят на елементите се нарича дължина на list-а.

Първият елемент от list-а се нарича глава на list-a, всичко останало се рарича опашка. Опашката може да бъде друг list. Всъщност синтаксиса на list е по-кратък вариант на:

[Term1|[...|[TermN|[]]]]

Елементите на list-а могат да бъдат от различни типове.

Пример:

1> L1 = [a,2,{c,4}].
2> [H|T] = L1.
3> H.
a
4> T.
[2,{c,4}]

String

String-овете се ограждат с двойни кавички ( “ ), но не са тип данни в Erlang.

Например string-a “hello” е кратък запис на [$h,$e,$l,$l,$o], което е [104,101,108,108,111].

Два стринга записани един до друг са съединяват по време на компилиране.

Record

Това е структура от данни с фиксиран брой елементи. Елементите имат имена и е сходна на структурите в С.

Record не е истински тип данни за Erlang. По време на компилиране record се преобразува в един или повече tuple.

Пример (деклариране на record):

X = #person{name=Name, age=Age}.

Пример (създаване на record):

-record(person, {name, age}).

Пример (извличане на стойност на поле):

1> X#person.name

Name

Управляващи конструкции

If

if

Guard1 ->

Expr_seq1;

Guard2 ->

Expr_seq2;

end

Всяко условие (Guard1, Guard1) в if оператора се проверяват едно по едно. Когато някое условие се оцени на true се изпълняват изразите след него. Резултата който връщат изразите е резултат на if оператора.

Ако нито едно условие не бъде оценено на true възниква грешка. Ако е необходимо, за последно условие може да се постави true, и така ако всички други не се изпълнят то ще се изпълни.

Case

case Expression of

Pattern1 [when Guard1] -> Expr_seq1;

Pattern2 [when Guard2] -> Expr_seq2;

end

Изразът (Expression) се сравнява с различните шаблони и при успех се изпълняват съответните изрази. Стойността която връщат изпълнените изрази е стойността която ще върне case оператора.

Ако няма съвпадение на сравняваните шаблони възниква грешка по време на изпълнението на програмата.

Function Calls

Тъй като Erlang няма цикли за постигане на цикличност се използват функции в комбинацията с операторите if и case.

С помощта на функции и рекурсия могат да се реализират повечето видове цикли на Erlang.

Пример за цикъл for реализиран на Erlang:

for(Max, Max, F) -> [F(Max)];

for(I, Max, F) -> [F(I)|for(I+1, Max, F)].

Send

Erlang е създаден за паралелно програмиране и потока на изпълнение на програмата зависи от съобщенията които процесите си разменят.

Expr1 ! Expr2

Тази команда изпраща стойността на Expr2 като съобщение на процеса определен от Expr1. Expr1 може да бъде pid, atom(име) или tuple. Ако се използва atom и не бъде намерен процес със съответното име възниква грешка.

Receive

Получава съобщение изпратено от друг процес със send (!) оператора.

receive
    Pattern1 [when GuardSeq1] ->
        Body1;
    ...;
    PatternN [when GuardSeqN] ->
        BodyN
end

Ако не бъде получено съобщение което да отговаря на някой от шаблоните изпълнението на процеса се прекратява докато не се получи подходящо съобщение.

Вход/Изход – съхраняване на информация

Erlang ами много модули които реализират различни начини за комуникация със системата му. Най-използваните са: asn1, crypto, gs, inets, jinterface, megaco, public_key, ssh, ssl, wx, xmerl, erl_interface.

Има възможност и за реализирането на всякакви други видове комуникация с устройства чрез байтови потоци.

Езика има модули които реализират всички възможни операции с файлове които операционната система поддържа.

Интересни функции за работа с файлове и директории в модула file:

change_group, change_owner, change_time, close, consult, copy, del_dir, delete, eval, format_error, get_cwd, list_dir, make_dir, make_link, make_symlink, open, position, Pread, pwrite, Read, read_file, read_file_info, read_link, read_link_info, Rename, Script, set_cwd, Sync, Truncate, Write, write_file, write_file_info

Erlang има ODBC модул, който предоставя интерфейс за комуникация с релационни SQL бази данни.

Езика има и модул mnesia, който е система за управление на бази данни и е подходящ за телекомуникационни приложения.

Mnesia има способностите необходими за да се справя с проблемите за които е създаден Erlang.

Някой от най-важните и атрактивни качества на Mnesia са:

· Релационно-обектен хибриден модел на представяне на данните, който е подходящ за телекомуникационни системи.

· Специално създаден DBMS език за заявки.

· Таблиците могат да бъдат съхранявани както на диска, така и в паметта.

· Таблиците могат да бъдат разположени на различни машини.

· Групиране на серия от операции към таблица.

· Програмите могат да бъдат писани без знание къде точно се намират данните.

· Много бързо търсене в реално време.

· Може да бъде преконфигурирана без да бъде спирана.

Компилатори/Интерпретатори

Програмите на Erlang трябва да бъдат компилирани до обектен код. Компилатора генерира файл с обектния код и той се изпълнява от абстрактна машина наречена BEAM.

Компилатора може да генерира и директно изпълним файл.

Обектния код трябва да бъде зареден в системата на Erlang, което се прави от сървър. Кода се зарежда по два начина: когато е необходим или по предварително зададен начин.

Системата на Erlang позволява промяна на кода докато тя работи. Това става за всеки модул по отделно.

Кода на всеки модул може да съществува в два варианта в системата: текущ и стар. Когато модул се зареди за първи път той е текущ. Ако нов вариант на модула се зареди, кода от предния вариант на модула става стар, а новия вариант става текущ.

Ако трети вариант на модула се зареди сървъра ще изтрие стария вариант, третия вариант става текущ, а предишният текущ става стар.

Как да пишем по-добър PHP код

Posted by Асен on November 30th, 2008

Провокиран от блога на един колега, реших да напиша моите виждания над това, как е най-добре да бъде написан един сайт на php.

  • Не използвайте framework-ове - ако питате мен това си е смъртен грях. Да се инклудне такова огромно количество код, само защото програмиста не знае как да си реализира сам решението на задачата си, е направо истинско мъчение за всеки сървър.Не ме разбирайте погрешно, Аз харесвам някои framework-ове, но те винаги реализират много сложни неща и спестяват на програмиста много дни/седмици/месеци работа. Пример за php такъв не мога да дам, защото не съм използвал, но за JavaScript имам добри впечатления от ExtJs.

    Тези които използват framework за да се свържат към базата данни, или каквото и да нормално нещо изискващо 10-15 реда код да се напише (например функции за свързване/изпращане на заявки към база данни), според мен не са php програмисти, защото ако трябва да напишат нещо без framework-а ще го направят по-зле от кучето ми.

    Знам че има хора които просто спестяват 4 мин. работа за тях за сметка на сървъра, и всъщност могат да си го напишат сами на php, така че тук направих голяма генерализация.

    И все пак, по-добър код е този специално написан за даденото приложение, а не framework направен да работи във всяка ситуация, независимо колко знания има използващия го.
  • Не използвайте register_globals и magic_quotes – Има ли нужда от коментар? Много по-добре е да работите без подобни екстри от страна на сървъра. Използването им води до огромни дупки в сигурността, а до някаква степен и натоварва повече сървъра.

    Радвам се че в PHP 6 тези двете ще бъдат премахнати. По-този начин броят на некадърните програмисти би трябвало да намалее. Презирам дори хостове които ги държат включени по подразбиране ;)
  • Отваряйте по една връзка към базата данни – Не мислех че ще се наложи да напиша толкова интуитивно нещо, но напоследък виждам голяма част колеги, които явно от незнание, но се оказва че имат 10-15 връзки към базата по едно и също време от един скрипт. Без коментар!

    Също така е добре да затворите връзката към базата данни ако сте приключили с нея по-рано отколкото ще завърши скрипта ви.
  • Не извиквайте функции в тялото на цикъл – Освен ако не е наложително разбира се. Добър програмист не би трябвало да пише код от рода на:
    for($i=0;$i<count($arr);$i++)
    особено когато има толкова по приятен вариант:
    $arr_len=count($arr);
    for($i=0;$i<$arr_len;$i++)

    Това е често срещан случай, но същото се отнася и за извиквани на функция в while и каквото и да е друго нещо, което ще се изпълни много пъти. Ако е възможно извикването трябва да се извърши преди цикъла.
  • Заграждайте стринговете с единични кавички – При използването на двойни кавички php търси дали в стринга няма променливи които трябва да постави. Това е много бавен процес. Винаги затваряйте стринга с единични кавички и го прекъсвайте когато трябва да включите променлива.

  • Когато достъпвате елемент от масив с ключ стринг, винаги го заграждайте в единични кавички – Имам в предвид, че $arr[key] е няколко пъти по-лош вариант от $arr['key']. Второто е около 7 пъти по-бързо. Разбира се ако имате числов индекс не го заграждайте.
  • Не използвайте операторе за подтискане на грешки - Много бавно и тежко решение да прикриете зле написан код. По-добре го пренапишете без грешки. В малък процент от случаите @ е полезен оператор.
  • Използвайте echo с няколко параметъра – Вместо да напишете нещо от рода на
    echo $x.’ plus ‘.$y.’ equals ‘.($x_$y);
    Можете да го напишете така:
    echo $x,’ plus ‘,$y,’ equals ‘,($x_$y);

    Това което се случва тук е, че в първият вариант свързвате няколко стринга и тогава ги изпращате на функцията echo, а във втория извиквате echo с няколко параметъра и избягвате свързването на стринга. Това е доста по-бърз вариант да се направи същото нещо.

    Това поведение е валидно само за функцията echo.

  • Използвайте isset() вместо strlen() – Това на много места е казвано. isset() може да се използва много умно и да направи кода ви по-бърз на много места.
  • Избягвайте require_once() и include_once() – Следенето дали даден файл вече е бил инклуднат невинаги е цена която си заслужава да се плати. По добре напишете така кода, че винаги да се инклудва веднъж. Разбира се понякога е невъзможно или прекалено трудно.
  • Избягвайте регулярни изрази – Когато можете да направите нещо без регулярен израз е по добре да го направите така. Избягвайте функции очакващи регулярни изрази като split и ги заменяйте с по-бързите им алтернативи.
  • Използвайте вградените функции на php – Ако има вградена функция която прави това което прави и вашата, по добре е да използвате вградената. Има голяма вероятност вградената да го прави по-ефективно.
  • Декларирайте променлива преди да я използвате – Може да не се вижда, но използването на недекларирана променлива е доста по бавно от варианта в който я декларирате преди да я използвате.
  • ++$i и –$i са по-бързи от $i++ и $i– – Това е така защото вторите, освен че увеличават с едно, запазват старата стойност за да я върнат. Ако знаете какво правят тези оператори ще се досетите защо първите са по-бързи.
  • Не разделяйте кода си на прекалено много функции - Освен ако наистина имате код който ще използвате на много места, разделянето му във функции е безсмислено. Извикването на една функция отнема време колкото 7-8 инкрементации. (само извикването, изпълнанието на е включено ;) )
  • Използвайте ООП умерено – Забелязвам че по-неопитните колеги като научат какво е ООП го използват навсякъде, т.е. където не трябва. Добре е да се знае, че използването ООП не е предимство, освен ако не се решават важни въпроси които не могат да станат по-добре в процедурен стил.

    Извикването на метод на обект е 3 пъти по-бавно от извикването на обикновена функция. Сега си представете какво става при наследяване, защото извикването на метод от базов клас е по-бавно от извикването на метод от производния клас.

Това са част от нещата които се старая да спазвам на продуктивни сайтове. Ако смятате че греша не се колебайте да ме поправите, но по-добре да знаете какво говорите ;)


Copyright © 2012 Асен Сотиров, ДевСпел ООД
Theme by Correos masivos