# WA(F)_3 **План занятия на сегодня** -В функциональном плане: * Динамический сайт, отправляющий данные серверу и хранящий данные в БД -В методическом плане: * PHP * Вопросы безопасности(песочница браузера и злые хакеры) * Формы * СУБД и их роль **Перед началом занятия выполните подготовку:** * Необходимо открыть страницы, которые мы делали на предыдущем занятии, в браузере (localhost:/demo2/) * Запустить Apache через контрольную панель XAMPP **Вопросы безопасности** Надо понимать, что безопасность JS - это иллюзия. Да, если JS работает в составе браузера, то он безопасен. Но не из-за того, что сам язык очень защищен, а из-за того, что браузеры создают вокруг него "Песочницу безопасности" Браузеры просто не позволяются обращаться JS к файловой системы, к БД, просчитать некоторые данные и другое. Хостом для JS могут быть не только браузеры, но и NullJS или встроенный в windows движок JS. **Демонстрация опасности JavaScript** Сейчас создадим опасный файл, для наглядности слов. Создадим новый файл dangerous.js На самом деле, JS может запустить совершенно любую программу на Вашем компьютере, если он запущен в необходимым хосте. Напишем код, который запустит калькулятор. *p.s. наша программа будет работать только в случае, если она запущена windows движком* ``` var shell = new ActiveXObject("Wscript.shell"); shell.run("calc.exe"); ``` Вот и всё: мы имеем опасный код, который запустит программу на нашем компьютере. Запуск кода: * Если windows сконфигурирован стандартно, то мы можем запустить нашу программу банальным двойным кликом. * В ином случае необходимо воспользоваться командной строкой ![](https://i.imgur.com/lofSZ3v.png) `C:\Users\Addministrator>cscript C:\Users\Administrator\Desktop\dangerous.js` Готово. Запустился наш калькулятор ![](https://i.imgur.com/mhtfI3z.png) Если этот код засунуть в браузер, то он не будет исполнен, так как он считается опасным. Из этого следует вывод, что JS опасен так же, как и любой исполняемый код. Его опасность зависит от хоста, но он опасен как любой скомпилированный код. **Почему нельзя пробрасывать через рутер наш XAMPP?** Потому, что он сконфигурирован без соображений безопасности. Чтобы показать его опасность (косвенно) косвенно. Есть вот такая вот табличка Exel ![](https://i.imgur.com/3cHlLxL.png) Это информация о заходах пользователей на сайт за определенный период Здесь отображаются "благопристойные" пользователи - которые заходили на сайт с определенной целью и знали, что там увидят. А теперь я поменяю фильтр и можно будет увидеть, сколько было плохих пользователей: ![](https://i.imgur.com/TdVnyre.png) Итого: 229 заходов. В основном, это боты, которые пытаются найти уязвимость в сайте. Крайне редко это оказываются настоящие люди, которые просто не нашли существующую нужную страницу. Эти боты искали много разных строчек. В том числе phpmyadmin ![](https://i.imgur.com/2m9v3uZ.png) phpmyadmin - утилита, которая есть в составе нашего XAMPP. Стандартные пароли от phpmyadmin известны - зачастую отсутствие пароля. Таким образом, если бы у меня была эта утилита, то, при её нахождении, вероятнее всего, бот бы сразу запустил другую программу, которая просмотрела бы все мои базы данных, которые были бы доступны под этой учетной записью. Из этого необходимо сделать вывод, что перед выкладыванием чего-либо в интернет, необходимо тщательно проверять, все ли учетки закрыты, везде ли используются надежные пароли. **Использование форм** Введенные данные пользователем нужно отправлять на сервер. Сегодня будем использовать формы. Чтобы данные были отправлены пользователем, нужно сделать формы. Чтобы сервер получил данные, необходимо написать некоторый сервисный код. В нашей папке, где хостируется наш сайт, создадим новую папку "demo3" Сейчас сделаем кусок login-системы. Откроем редактор и создадим новую страницу Страница может отправить данные либо себе (post back), либо другим страницам (прямая отправка). Сейчас разберем прямую отправку, так как она, в некотором плане, проще. Сама страница, которая будет отправлять данные, будет чисто на html, но страница, которая принимает данные, должна быть уже с серверным кодом. Начнем с клиентской части. **-Клиентская часть** Назовем страницу login.html и сохраним её в созданную ранее папку. За основу можно взять что угодно, что уже писали ранее. Я возьму код нашего калькулятора и удалю всё лишнее: ``` <html> <head> <title> </title> <head> <body> <h1> </h1> </body> </html> ``` Начнем писать уже необходимый код ``` <html> <head> <title>Login</title> <head> <body> <h1>Login, please</h1> <form> Name: <input type="text" name="user" /> <input type="submit" value="Go!" /> </form> </body> </html> ``` Имеются следующие виды input формы: * text (стандартный Input) * pass (как текстовый, только со скрытыми символами) * submit (работает как кнопка, только имеет больше функционала, обычно именно Input type="submit" используется для отправки данных на сервер) value отвечает за то, что будет на нем написано. `name="user"` мы задали для того, чтобы сервер мог понять, где лежат данные, которые ему нужны. Именно для сервера необходим отрибут name. Всё, мы создали простейшую страницу, которая отправляет данные серверу. Запустим её и введем в форму имя Yuri. Нажмем нашу кнопку. Получили следующий эффект: ![](https://i.imgur.com/sGN4o5h.png) Поле очистилось, в адресной строке появился пользователь. Произошло следующее: страница отправила данные себе и перезагрузилась, и страница отправила данные на сервер. Мы понимаем, что данные отправились на сервер из-за того, что в адресной строке появлиась следующая надпись "?user=Yuri". Это выполнился метод get нашего сервера user - это имя нашего input, а Yuri - введенная пользователем строка. Сама страница этими данными распределиться не может, так как у нее нет серверного кода и она просто перезагрузилась. Так что всё это бесполезно. Чтобы это имело смысл необходимо добавить в наш код атрибут action. ``` <html> <head> <title>Login</title> <head> <body> <h1>Login, please</h1> <form action="welcome.php"> Name: <input type="text" name="user" /> <input type="submit" value="Go!" /> </form> </body> </html> ``` Теперь нажатие кнопки будет нас перекидывать на другую страницу и передавать на неё введенные данные ![](https://i.imgur.com/6mHaJWK.png) У нас страницы пока что нет, поэтому закономерно высветилась надпись Not Found. **-Серверная часть** Создадим ту самую страницу с названием welcome.php и помистим всё в ту же нашу папку. Начальную заготовку возьмите из любой другой страницы php. Теперь добавим функционал в наш блок <?php?> Создадим переменную $user_name Вероятно, Вы часто слышате слова request и response. Это не спроста. Данные, которые отправила наша страница пришли как раз через request запрос. Чтобы к нему обратиться необходимо обратиться к переменной $_REQUEST. Именно в ней хранятся все данные, введенные пользователем. $_REQUEST - это массив, поэтому нужно обращаться к его элементам по их названиям, которые мы как раз задаем в клиентской части (name="user"). Обратимся к нашему user ``` <html> <head> <title>Welcome</title> <head> <body> <?php $user_name = $_REQUEST["user"]; $greeting = "Welcome, $user_name!"; ?> <h1><?=$greeting?></h1> </body> </html> ``` После получения имени пользователя, наш сервер создает приветствие, а html его выводит. Так мы написали простейшую серверную часть, которая обрабатывает наш request. ![](https://i.imgur.com/D6V2E0v.png) Теперь усложним нашу страницу логина, чуть-чуть приблизив её к реальности. Добавим еще одну форму - ввод пароля ``` <html> <head> <title>Login</title> <head> <body> <h1>Login, please</h1> <form action="welcome.php"> Name: <input type="text" name="user" /> <br /> Pwd: <input type="text" name="pwd" /> <br /> <input type="submit" value="Go!" /> </form> </body> </html> ``` Серверную часть писать не буду, сейчас важно другое: Если использовать для пароля инпут типа "text", сразу же будут видны недостатки этого типа. Во-первых, пароль могут подсмотреть сторонние люди, так как вводимые данные не скрываются формой, а во-вторых, наш пароль показывается в адресной строке ![](https://i.imgur.com/J45x0la.png) ![](https://i.imgur.com/AMBbvGn.png) Давайте их устраним. Для этого будем использовать другой тип формы - "password". `Pwd: <input type="password" name="pwd" />` Теперь мы видим, что пароль во время ввода закрыт звездочками ![](https://i.imgur.com/Q5TnOjI.png) Но это не решит вторую проблему: в адресной строке пароль всё равно виден Для решения второй проблемы нужно знать следующие: Существуют несколько методов отправки данных. Из них самые распростроненные всего два: * `method="get"` (именно им по-умолчанию и отправляются данные) * `method="post"` (Он тоже отправляет данные серверу, но не через адресную строку) Именно метод "post" мы и будем использовать. ``` <html> <head> <title>Login</title> <head> <body> <h1>Login, please</h1> <form action="welcome.php" method="post"> Name: <input type="text" name="user" /> <br /> Pwd: <input type="text" name="pwd" /> <br /> <input type="submit" value="Go!" /> </form> </body> </html> ``` Теперь данные приходят на сервер, но в адресной строке мы их не видим ![](https://i.imgur.com/s6fSD7E.png) Необходимо учитывать, что хоть данные и не видны пользователю, они слишком уязвимы. Их может перехватить любой хакер. Поэтому в реальности отправлять данные через http нельзя. Такой способ отправки данными допустим только в случае, если сервер использует протокол https - протокол взаимного шифрования **Работа с базами данных** Для работы с базами данных используется SQL. Не просто так мы устанавливали XAMPP. Внутри него уже имеется база данных MySQL. Чтобы её подключить необходимо перейти в conrol panel и запусть MySQL ![](https://i.imgur.com/Y0kFenI.png) Так же, как и web-server, сервер БД - это сервис, который работает в фоне. Чтобы эффективно работать с MySQL неоходимо использовать клиентское орудие. Для каждого сервера оно свое, но в нашем случае XAMPP предоставляет нам такое "орудие" - phpmyadmin. Чтобы открыть его нужно нажать кнопку "Admin", правее от кнопки запуска MySQL внутри самого XAMPP. Откроется страница в браузере с визуальным интерфейсом, который очень удобен в использовании для работы в БД ![](https://i.imgur.com/SF4Uxo9.png) В меню слева уже имеются некоторое количество баз данных, которые являются ++служебными++, поэтому трогать их не нужно. Создадим новую базу, нажав на кноку "New" в том же меню. Необходимо ввести её *(в моем случае myshop)* название и нажать на кнопку "Создать" ![](https://i.imgur.com/ql7ObN0.png) Данные в БД, по крайней мере в MySQL, лежат в таблицах. Представлять такую таблицу, как таблицу Exel - не совсем правильно. Так как в Exel вы можете в любую ячейку писать любые данные, а вот в MySQL необходимо заранее указывать структуру таблицы :колонки с названиями и тип данных, который могут хранить те или иные колонки. Теперь необходимо создать таблицу внутри базы. Назовем её products и выберем количество столбцов в ней. Заранее продумаем, сколько столбцов нам нужно. Это не значит, что после создания таблицы мы не сможем изменить этот параметр, но всё равно стоит указывать количество столбцов обдуманно. В современных таблиц должна быть как минимум одна колонка, которая хранит ключевые поля, значение которых уникально для каждой строки (id товара). Создаем таблицу products с 4 колонками. ![](https://i.imgur.com/4onrfpx.png) Теперь зададим все необходимые настройки таблицы. Назовем первую колонку ID и, прокрутив страницу правее, выберем индекс для нашего столбца ID, установив primary. Это сообщит нашей базе, что именно этот ID будет первичным ключем этой таблицы. И правее, в графе A_I (Auto increment), установим галочку, чтобы наш ID считался автоматически. ![](https://i.imgur.com/nKjudPE.png) Вторую колонку назовем ProductName (не стоит называть просто Name, так как такое название много где зарезервированно). Типом хранимого значения выберем VARCHAR. Да, там имеет тип "Text", но его не стоит его использовать, так как его максимально хранимое значение будет 2^15, что в нашем случае будет лишь тратой драгоценного места. Так как мы выбрали тип VARCHAR, укажем максимальную длинну строки, которую мы сможем положить в ячейку этого столбца (к примеру, 20). Следующий столбец назовем Articul, он тоже будет VARCHAR, длину сделаем 10 И последняя колонка будет Price, а вот тип выберем Float. Таким образом, мы определили структуру нашей таблицы. Её нужно сохранить и заполнить какими-нибудь пробными данными ![](https://i.imgur.com/fXRYIlI.png) После нажатия кнопки "Save" открывается наша созданная таблица. ![](https://i.imgur.com/oiK432T.png) Заполнить таблицу пробными данными можно нажав на вкладку Insert в верхней панеле инструментов. Откроется страница, куда мы можем заполнить произвольные данные ![](https://i.imgur.com/k7DU9Pj.png) Заполним поля какими-то данными и нажмем на нижнюю кнопку "go",чтобы данные отправились в таблицу ![](https://i.imgur.com/FQC3gQ3.png) И теми же инструментами добавим еще пару товаров. На вкладке Browse можем увидеть все введенные данные ![](https://i.imgur.com/q6I2bSG.png) **Познание синтаксиса SQL** phpmyadmin дает возможность писать sql запросы в базу данных. Изучим минимальный набор синтаксиса Выберем вкладку SQL и напишем следующий запрос: `SELECT * FROM products` Этот запрос позволит выбрать(SELECT) все записи(*) из (FROM) таблицы (products). Если нам нужно выбрать не все данные, а только некоторые, добавим к нашему запросу: `WHERE Price > 1000` так мы сможем выбрать все записи, цена на товар в которых больше 1000 *(p.s. чтобы команда выполнилась, нам нужно выделить наш запрос левой кнопкой мыши и нажать "go")* ![](https://i.imgur.com/JxBwpT0.png) Как мы видим, выполнение этого запроса привело к тому, что бд нам вернула 2 товара, цена которых больше 1000 ![](https://i.imgur.com/tCIyXSq.png) Чтобы отсортировать выводимые данные нам необходимо дописать `ORDER BY` и выбрать критерий, по которому будем сортировать. Если сортировать по убыванию, то нужно дописать ключевое слово DESC (возрастанию ASC) `ORDER BY название_столбца DESC` **Создание визуального интерфейса для взаимодействия с продуктами** Создадим новую страницу и сохраним в нашей папке. Назовем её products.php Необходим именно php, так как только на стороне сервера мы сможем работать с базой данных. Без sql запроса всё равно никуда не деться, так что напишем запрос, который будет брать все данные из таблицы `$sql = "SELECT * FROM products";` Для работы с БД будем использовать библиотеку mysqli. Для подключения к базе данных используем функцию: `mysqli_connect("адрес_БД:порт","учетная_запись", "пароль", "БД");` *(Порт можно узнать в контрольной панеле XAMPP)* Выполнить запрос можно командой `mysqli_query(сессия, запрос);` Результат запроса сохраним в переменную $products. Чтобы проверить, есть ли хоть какие-то данные после запроса в нашей переменной, введем команду `var_dump($products);` Открыв нашу страницу в браузере, сможем убедиться, что запрос выполнился и переменная живая ![](https://i.imgur.com/95JXg9i.png) Выполнив дамп, мы увидели, что наша переменная имеет тип mysqli_result Это сложный тип(множественный), поэтому для вытаскивания данных из него необходимо пробежаться по нему циклом while(условия для повторения цикла) while(до тех пор, пока) $products - это поток (в php для работы с ним используется fetch) ``` while($row = mysqli_fetch_assoc($products)) { var_dump($row) } ``` этот цикл закончится, когда функция mysqli_fetch_assoc($products) вернет false. Это произойдет, когда она наткнется на конец файла. Запись `$row = mysqli_fetch_assoc($products)` позволит нам сохранить логический результат выполнения функции сохранить в переменную. While будет проверять именно переменную $row После того, как мы увидели структуру переменной $row, становится понятно, что мы теперь можем вытаскивать из неё нужные нам данные. Для этого необходимо обратиться к интересующему нас полю по названию. ``` while($row = mysqli_fetch_assoc($products)) { $name = $row["ProductName"]; $price = $row["Price"]; echo($name." ".$price."<br />"); } ``` Сохраним интересные для нас данные в переменные и выведем через `echo();` Так мы вывели данные товаров и пользователь уже может что-то посмотреть. ![](https://i.imgur.com/BH3Xtrz.png) ``` <html> <head> <title>Shop</title> <head> <body> <h1>Price list</h1> <?php $sql = "SELECT * FROM products"; //Никогда не используйте root в пользовательских приложениях //Это нарушение принципа наименьших привилегий $conn = mysqli_connect("localhost:3306","root","","myshop"); $products = mysqli_query($conn, $sql); //echo($products); не прокатит, так как эта переменная - не строка //var_dump($products); а вот это работает while() { echo(var_dump($row) + <br />) //выполним команду, чтобы посмотреть, из чего состоит наш $row. И каждую новую запись будем переносить на новую строчку $name = $row["ProductName"]; $price = $row["Price"]; echo($name." ".$price."<br />"); } mysqli_close($conn); ?> </body> </html> ``` *(Не оставляйте открытые сессии, это может ронять производительность!)* Закроем сессию командой `mysqli_close(сессия);` **Домашнее задание:** * Необходимо пронумеровать товары в пользовательском интерфейсе * Сделать, чтобы товары выводились в таблице