@mkramer@Fell-x27 я его использовал, чтоб исходный код страницы не был в каше. я немного запутался, то есть PHP_EOL использовать вообще нет смысла как и перевод строки \n?
Исходный код нет смысла причесывать. Браузеры умеют делать это постфактум при просмотре, DOM-дерево в отладчике и так всегда красивое, плюс можно копипастить код из ответа сервера в IDE и там применять автоформатирование. Как ни крути, отдавать браузеру символы переноса не нужно.
Ну хотя я это знаю, я не ставлю их удаление специально из HTML, мне кажется, сейчас это не сильно много смысла имеет. У меня они есть не потому, что я их специально вывожу, а потому что я выключаю режим PHP
Ну, я тоже их не выпиливаю, если они естественным путем образовались. Если где-то есть HTML, то в нем и отступы есть, и переносы, но это имеет свой смысл - так проще с ним работать в IDE. А вот добавлять специально переносы там, где смысла в них нет не нужно. Это, наверное, более правильная формулировка. Хотя, по-хорошему, советуют весь отдаваемый HTML минифицировать. Но это уже от ситуации плясать надо.
Я там так мельком глянул. Слово "эксперт" на странице ну просто в топ выведен, на сайте продают книжечки всякие с дисками, включая "Как продлить половой акт" за 2 тыщи. Бгг. А, ну гугл и яндекс в партнёрах, 50/50 делят прибыль походу. Угарненько.
Не знаю способа изменить значения кнопок (( И так же хотел через цикл с таблицей вывести много чисел 0 , чтобы они все записались в БД, но чтобы они были строго ограничены рамками экрана и замещали его полнстью.. Есть подсказки?) Только для новичка )
Не надо менять значения кнопок (хотя ничего сложного), просто - пользователь залогинился, вывелись другие кнопки А по-русски? И главное, как это всё соотносится с задачей суриката?
Что это должно дать? --- Добавлено --- Ну так там же будет отправка формы, с последующей перезагрузкой страницы, верно? Вот ты эти данные прими, и, если все ок и пользователь залогинился, ты отдавай новую страницу. С новыми кнопками. "Меняется" - это то, что видит пользователь, а не то, что прям интерактивно должно на странице меняться. В задании стоит отдельным пунктом ограничение на использование JS. Оно тоже не просто так. Если ты начал загоняться и видишь решение, для которого нужен код на клиенте, то, в данном случае, это решение неправильное. Ищи другое.
Всем доброго времени суток. Вот и я выполнил это задание и хочу критики. Мне интересно не только выполнил я задание или нет, но и если несложно, то прокомментируйте качество кода, в каких местах хочется выколоть себе глаза или отрубить автору руки. Это мой первый учебный проект на php и конструктивная критика важна. https://github.com/Wheiss/suricat https://suricat.000webhostapp.com/
Ну из первого, что бросается в глаза - пароль светишь даже в куках. Нафига? Во-первых, там не было задания на галочку "оставаться в системе", а во-вторых, это тоже не делается через засвечивание пароля в куках
Спасибо за коммент. Я думал это из само-собой разумеющегося. Сегодня редко увидишь сайт, где нет как минимум возможности "оставаться в системе". Как следствие я теперь точно знаю, что стоит погуглить, как же правильно сохранять пароли или данные входа в куках. Думал сначала хеш от пароля хранить, но потом почему-то подумал, что легкая доступность кук это не моя проблема, а того, кто их хранит
Я ни капли не самоуверен. Выполнил это задание как раз для того, чтобы разобраться, в какие стороны нужно копать, чтобы реализовывать вещи правильно. Пока что у меня в голове мысли о том, что нужно как можно глубже разбираться в том, как правильно строить архитектуру приложения, и как не делать дыры в безопасности.
Галочка "Оставаться в системе" реализуется генерацией случайного токена. Этот токен записывается в базу данный, ассоциированный с id пользователя, и в куки. --- Добавлено --- Хешь пароля тоже светить не стоит. Хотя с использованием современных методов разгадать его очень трудно, но вдруг у кого-то таки получится.
а если пойти дальше и постараться предотвратить атаку по вермени, то в базу надо писать случайный идентификатор токена, и токен. В бд писать идентификатор токена и токен. Выборку делать по идентификатору, а сравнивать значение токена постоянной по времени сравнивалкой. Иначе можно статистически анализируя задержки выборки подобрать. А если пойти ещё дальше, то не стоит хранить в базе идентификатор в открытом виде, ведь иначе тот, кто получит доступ к бд, сможет зайти под любым запомненым юзером. Нужно посчитать хеш пароля функцией password_hash(), и уже его сохранять в базу. Проверять как пароль.
1) Про пароль в куке уже было все сказано, так делать не надо. Но что еще не надо делать - так это хранить пароль в сессии. Даже хэш. Зачем? Каждый раз аутентификацию проводить? Проще в той же сессии поставить флаг, мол, юзер вошел, все ок, права такие-то. 2) PHP: spl_autoload_register(function ($class) { $array_paths = array( '/models/', '/components/' ); foreach ($array_paths as $path) { $path = ROOT . $path . $class . '.php'; if (is_file($path)) { include_once $path; } } }); Я, в общем, понимаю логику, но перебирать подряд все возможные пути, до тех пор пока не наткнемся на файлик - это не самое лучшее решение, как мне кажется. У меня, к примеру, архитектура такова, что каждый модуль хранится в папке...с именем модуля. И класс, который этот модуль реализует, тоже называется так же. При этом используется парадигма "все есть модуль". В итоге, если надо дернуть, хз, модуль "builder", я просто дергаю, грубо, "/modules/builder/main.php" Это решение уровня архитектуры. Еще иногда практикуется добавление в имя класса постфикса, определяющего его тип, чтобы по постфиксу определять путь. Еще можно отдельно в системе хранить массив зарегистрированных в системе классов и путей к ним. И в автолоаде просто дергать путь из такого массива. Вариантов много. Стучаться же по каталогам с вопросом "а такой-то файл тут живет, не?" - спорное решение. 3) PHP: $params = include($paramsPath); Вот так цеплять что либо вообще нежелательно. Использование include в таком ключе это экзотика и костыль сродни goto. В итоге получается какой-то недовызов недофункции. Include лучше использовать по назначению - для линковки кода. В твом случае, чем возвращать таким образом массив, правильнее было бы описать конфиг в виде статического класса и обращаться к его свойствам. Да и само по себе такое решение чище. 4) В логауте: PHP: header('Location: /auth'); die; return true; Ты уж определись IDE бы сразу на такой код ругнулась, потому что строчка с ретурном это "недостижимый код". 5) PHP: $query = "SELECT * FROM users WHERE login = '$this->login'"; $query = $db->prepare($query); $query->execute(); SQL-инъекция. И дальше везде, где юзаешь БД, у тебя дыры. То есть, ты, конечно, используешь PDO->prepare(), но от этого нет никакого толка. Как работает prepare? Что ты подготовил в этом запросе? И как ты использовал эту подготовку? Ничего и никак. Перечитай еще раз доку. 6) Ты в одной функции посылаешь запрос по всем полям users, чтобы узнать, существует ли такой логин. Потом в другой функции посылаешь запрос с теми же условиями к той же users, но только по полю password, чтобы узнать, какой у него был пароль. При том, что ты его и так вытащил в первом запросе, если тот оказался удачным. Так почему бы просто не тянуть все одним запросом? 7) PHP: if($this->loginExists($this->login, $error_array)) { // Проверяем соответствие пароля $this->passwordMatches($this->login, $this->password_hash, $error_array); if(empty($error_array)) { return true; } else { return false; } } Почему бы просто не поставить вызовы проверяющих функций в одно условие через && ? 8) Проверка на занятость логина тоже идет через select. Почему бы на уровне базы не сделать поле с логином уникальным, и, при попытке регистрации, просто не отдавать базе запрос на запись нового логина? Вернет окей, значит все хорошо. Вернет ошибку нарушения уникальности - упс, логин занят. Это же не ошибка синтаксиса. Этого бояться не надо. Вполне нормальная практика. 9) Бабах: PHP: $db = Db::GetConnection(); $query = "INSERT INTO users (login, password, birth_date) VALUES (:login, :password, :birth_date)"; // Подготавливаем запрос. В документации написано, что этот метод защищает от SQL-инъекций $query = $db->prepare($query); $query->bindParam(':login', $this->login); А тут ты как будто бы правильно используешь подготовку, но, почему-то, только на две трети. Странно, почему ты до этого так не делал. Но и странно, почему препейришь 3 параметра, а биндишь два. 10) PHP: public static function months() { // вывод месяцев в <select> return array(Январь, Февраль, Март, Апрель, Май, Июнь, Июль, Август, Сентябрь, Октябрь, Ноябрь, Декабрь); } Как-то странно это видеть в классе, отвечающем не за построение GUI. У тебя же есть отдельно вьюхи, прописал бы там Вердикт: в основном, на самом деле, к коду только придирки. Из фатальных недочетов только проблемы с безопасностью работы БД и хранение паролей в куках. В открытом виде.. Это вообще не круто. В остальном, код аккуратный, логичный, последовательный, хоть и имеет место оверинжиниринг для такой простой задачки. Вангую, что ты раньше писал на яве или шарпиках. Теперь по верстке... На дворе 2017 год. Верстать таблицами не круто уже лет 15 как. Таблицы - это элемент для подачи табличных данных, а не для выравнивания разметки. Таблицы в качестве макета это плохо. Очень плохо. Ну и ничего не говорилось про два поля ввода для паролей. Говорилось не просто так. Это лишняя мишура, она тут просто не нужна. Но это не принципиально. А вот от таблиц надо уходить.
Спасибо за все комментарии и такой детальный обзор. И правда перечитаю доки по PDO, токенам и отполирую сегодня.
Итак, проделал работу над ошибками, переписал часть кода. По пунктам: Я думал, что сессия ломается так же легко, как кука, поэтому думал, что лучше каждый раз проводить аутентификацию( хотя все-равно человек, который украдет сессию в данном случае будет иметь данные для входа и проверки будут только грузить сервер). Особо не углублялся, только прочитал, что в сессиях можно хранить данные и их не так легко сломать, особенно если привязывать к ip( этого пока не делал). А вопрос с куки я решил так: В куки записал чистый id и хешированный random_bytes(12), а в БД записал чистый random_bytes(12) и время его записи. PHP: public static function authCheckToken($id, $token_hash, &$error_array) { // В этом блоке достаем токен и время его создания try { $db = Db::GetConnection(); $query = 'SELECT token,token_time FROM users WHERE id=:id'; $query = $db->prepare($query); $query->bindParam('id', $id); $query->execute(); $result = $query->fetchAll(); $db_token = $result[0]['token']; // Вытаскиваем время создания токена из БД $token_time = new DateTime($result[0]['token_time']); // Вычисляем, когда кончается срок годности токена $token_time = $token_time->add(new DateInterval('PT1H')); } catch (PDOException $e) { echo 'Ошибка базы данных'; } $current_time = new DateTime(); // Если токен совпадает и срок его годности не подошел к концу - записываем ID в сессию if(password_verify($db_token, $token_hash) && ($current_time<$token_time)) { $_SESSION['user_id'] = $id; return true; } else { $error_array['false_cookies'] = 'Ложные куки'; return false; } } Естественно тут можно еще улучшить безопасность, каким-нибудь образом скрыть id, вычисляя-сопоставляя хеш. Но пароль в куках я скрыл от глаз Тут ничего не менял, еще потестирую разные способы. Исправил, теперь все настройки - статические свойста класса Config Тут недоглядел, исправил. Так уж получилось, что мне очень нравится Sublime, а настроить его как следует(чтобы ругался вот в таких случаях) пока руки не доходят. Подправил. Так и сделал. И тут тоже, даже не думал, что так можно. А вот тут все было правильно В случае когда забиндено кол-во параметров не равное кол-ву подготовленных, возвращается ошибка. У меня там чуть ниже был еще один bindParam, скорее всего вы его недоглядели. Это я отправил в конфиг, там как раз и хранятся массивы с различными данными. Я думал, что за формочку никто не наругает. Ошибался