Страницы

Поиск по вопросам

Показаны сообщения с ярлыком sql-injection. Показать все сообщения
Показаны сообщения с ярлыком sql-injection. Показать все сообщения

среда, 29 января 2020 г.

Защита сайта от взлома [закрыт]

#php #защита #взлом #xss #sql_injection


        
             
                
                    
                        
                            Закрыт. Данный вопрос необходимо конкретизировать. Ответы
на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он был сосредоточен только на одной проблеме, отредактировав его.
                        
                        Закрыт 4 года назад.
                                                                                
           
                
        
Создал, наконец, свой первый проект. Начитался кучу информации по защите сайта (sql-инъекции
и т.д., и т.п.). Но я понимаю, что в сети не рассматриваются все нюансы по защите,
поэтому я создал шуточный клон сайта, залил на сервер и пытаюсь найти дыры в безопасности.
Пока безуспешно - стоит строгая проверка на url-адрес, админка скрыта в таинственной
папке))) 

Вопрос очевиден - я хочу понять, есть ли возможность взломать сайт, открыть структуру
папок и попасть в админку.

Уважаемые программисты, помогите, пожалуйста! 
Я прописал в .htaccess вывод любых предупреждений и ошибок, поэтому буду премного
благодарен за взлом собственного сайта! Фишка в том, что весь проект написан собственноручно
в Коделобстере без использования готовых шаблонов.

P.S. Для админ-части аутентификация написана собственноручно, пожалуйста, взломайте
и ее. Попыток входа - 3. Сейчас я увеличу до 20.

Псевдосайт - http://mars.fh38095o.bget.ru/

P.P.S. Кто действительно заинтересуется и покажет, что невозможно найти ссылку на
админку, я скину url в личку.
    


Ответы

Ответ 1



Я не знаю, что такое "коделобстер" но, судя по всему, 'то какая-то платформа для создания уязвимых сайтов. Выкладывать на проверку код со столь хрестоматийной SQL инъекцией должно быть стыдно. Ну и просить протестировать на безопасность игрушечный сайт, в котором всего 5 таблиц (block_pages,category,feedback,simple_pages,temple) и нет практически никакой информации - это как бы смешно. Ты бы еще выложил программу Hello world и попросил ее взломать. Что там ломать? Зачем? Кому нужен этот сайт? Если бы, скажем, сайт хранил номера кредиток, то они бы уже уплыли, как эти глубокомысленные вопросы из таблицы feedback: alert('hello'); hello ddddddd dddd ddddddddddd dddddddddddddddd dd dddd ddddddddd ddd dddddddd dd dddddd dddddddddddd ddddddddd ddddddddddd dd dddd dddddddddddd dd ddd ddddddd dddddddddd dddddddddddd ddd dd dddd dddddddd dddd ddddd dddd ddddddd d dddd dddddddd ра дывал п у дм жчсдялыпыа adfasdf asadf adf asdf asdf adf asdfasdfasfdasdfagfsdghsdbcxasdf Опять же, интерфейса к этой таблице на сайте нет. То есть, в последний момент автор забоялся, и прикрутил вместо нее к сайту Disqus. Очень, очень остроумный способ тестирования. Судя по тому, что в комментариях автор путает XSS с SQL инъекцией (собираясь защищаться от первой с помощью "stmt"), а "скрытая админка" почитается им как верх хитроумия в защите сайта, то ему просто рано выклыдывать что-либо на тестирование. По поводу намека на дыры. Дело в том, что никакие намеки здесь не нужны. Все, что тебе нужно знать - это то, что была SQL инъекция. Ну так об этом было сказано безо всяких намеков, открытым текстом. А вот какая конкретно - для защиты знать не нужно, от слова "совсем". Фишка в том, что для обеспечения защиты про дыры знать не нужно. Защита от атак иррелевантна самим атакам. Про то что нужно для защиты - написано было во всех учебниках (ну, кроме устаревших): надо, чтобы любые переменные попадали в запрос не напрямую, а через плейсхолдеры. Вот это и надо было делать. Ты об этом знал (как следует из твоих комментариев, когда ты собрался XSS лечить через "stmt"), но не придавал значения. Вот теперь, после того как ты закрыл эту дыру, надеюсь, будешь придавать. И опять же, сам видишь - чтобы закрыть дыру тебе не понадобилось узнавать, как был произведен взлом. Чтобы строить защиту, надо уметь строить, а не ломать. "Намекать на дыры" бесполезно. Здесь смысл не в том, чтобы залатать одну конкретную, а в том, чтобы все запросы без исключения выполнялись по единому безопасному принципу. И тогда хакер пусть хоть в лепешку разобьётся, но ни одной дыры не найдёт. По поводу XSS. Ситуация довольно забавная. Действительно, где-то в недрах сайта режется, почему-то, прямой слеш. Но, разумеется, это не может помещать, и XSS можно внедрить и без слеша. Но тут уже, увы, на твою защиту встают браузеры, которые научились отфильтровывать такие явные XSS-через-запрос за горе-программистов. Но опять же, надеяться на браузер - это значит гарантированно налететь на инъекцию. Такие вещи надо делать самому. Причем делать, опять же, не "как бог на душу положит", а сис-те-ма-ти-чес-ки! Вот ты вырезал теги в фидбеке и успокоился. А в идентификаторе страницы - нет. А здесь должна быть та же система, что и с SQL инъекциями - не надо сидеть и думать, где знадо защищаться, а где не надо. Защищаться надо везде! В защите от XSS аналогом плейсхолдеров в SQL может являться шаблонизатор с автоискейпингом. То есть, мы можем считать себя в безопасности, если Любой, абсолютно любой вывод производится только через шаблонизатор. По умолчанию шаблонизатор форматирует ВСЕ выводимые переменные. И только те, для которых указано явно, что они должны выводиться как есть - выводятся как есть. Таким шаблонизатором, в частности, является Twig Да, и еще одно замечание. Код or die("Ошибка в запросе $zapros"); это просто подарок взломщику. Скажем, если бы его не было, то я бы просто не взялся ломать - это потребовало бы куда больше времени, а время - деньги. Судя по всему, ты прямо в коде обращаешься к функциям mysqli. Этого делать тоже не надо, но к безопасности это отношения не имеет, а для практики сойдёт. Но эти свои чудовищные or die() постирай все до единого. Вместо этого перед коннектом к mysql напиши одну строчку, mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

вторник, 31 декабря 2019 г.

Защита от SQL инъекций в jdbc java

#java #безопасность #jdbc #preparedstatement #sql_injection


Часто вижу утверждения, что надо использовать PreparedStatement вместо обычного Statement,
чтобы защититься от sql инъекций. Как он защищает?
    


Ответы

Ответ 1



Коротко, для нетерпеливых: При использовании Statement строки запроса и значений складываются. При использовании PreparedStatement имеется шаблон запроса и данные в него вставляются, с отражением кавычек. Ниже подробнее с примерами. Вступление. Имеем такую простую таблицу с данными. +-----------+----+--------+ | userName | id | pass | +-----------+----+--------+ | admin | 1 | admin | | user | 2 | pass | | chuchelo | 3 | elli | +-----------+----+--------+ Модель User, будет содержать имя и пароль, а так же метод логин, который спросит данные с консоли. class UserLogin { String name; String pass; public UserLogin() { } public void login() { BufferedReader reader = null; try{ reader = new BufferedReader(new InputStreamReader(System.in)); System.out.println("user name: "); name = reader.readLine(); System.out.println("pass: "); pass = reader.readLine(); } catch (IOException e) { e.printStackTrace(); }finally { if (reader != null) try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } Метод, который будет работать с обычным Statement: UserLogin user = new UserLogin(); user.login(); try (Connection connect = MyConnection.getConnection()){ Statement statement = connect.createStatement(); String query = "SELECT userName, id, pass FROM users WHERE userName='" + user.name + "' AND pass = '" + user.pass + "'"; System.out.println(query); ResultSet resultSet = statement.executeQuery(query); while (resultSet.next()){ System.out.printf("User: id=%d name=%s pass=%s\n", resultSet.getInt("id"), resultSet.getString("userName"), resultSet.getString("pass")); } MyConnection.closeConnect(); } catch (SQLException e) { e.printStackTrace(); } Теперь если мы запустим этот метод и введем в консоль данные без инъекции: user name: admin pass: admin User: id=1 name=admin pass=admin При этом сам запрос выглядит так: SELECT userName, id, pass FROM users WHERE userName='admin' AND pass = 'admin' Если допустить ошибку в имени или пароле, то данные выведены не будет. Теперь попробуем использовать инъекцию(' or'1'='1), т.е. введем такие данные: user name: admin' or'1'='1 pass: blabla То мы все равно получаем результат, несмотря на то, что пароль неверный: User: id=1 name=admin pass=admin При этом сам запрос теперь выглядит так: SELECT userName, id, pass FROM users WHERE userName='admin' or'1'='1' AND pass = 'blabla' т.к. выражение or'1'='1' всегда равно true, то даже без указания пароля мы получим все данные. Как от этого защитит PreparedStatement? Метод который будет получать данные из базы с помощью PreparedStatement: UserLogin user = new UserLogin(); user.login(); try (Connection connect = MyConnection.getConnection()){ String query = "SELECT userName, id, pass FROM users WHERE userName=? AND pass=?"; PreparedStatement statement = connect.prepareStatement(query); statement.setString(1, user.name); statement.setString(2, user.pass); System.out.println(statement); ResultSet resultSet = statement.executeQuery(); while (resultSet.next()){ System.out.printf("User: id=%d name=%s pass=%s\n", resultSet.getInt("id"), resultSet.getString("userName"), resultSet.getString("pass")); } MyConnection.closeConnect(); } catch (SQLException e) { e.printStackTrace(); } Все тоже самое, только заменили обычный Statement на PreparedStatement. Надеюсь вы на слово поверите, что при правильных данных мы получим верный результат, если нет то вот лог в консоли: user name: user pass: pass User: id=2 name=user pass=pass Запрос: SELECT userName, id, pass FROM users WHERE userName='user' AND pass='pass' А теперь попробуем использовать инъекцию: user name: user' or'1'='1 pass: inject И ответа не получаем, потому что запрос выглядит так: SELECT userName, id, pass FROM users WHERE userName='user\' or\'1\'=\'1' AND pass='inject' Т.е. все кавычки были отражены слешем, инъекция не удалась. Отличие Statement от PreparedStatement: Statement - вы должны заботиться о кавычках в запросе и ставить их там где они нужны. PreparedStatement - вставляет значения в запрос и за счет методов setString setInt и прочих. Он сам понимает где нужны кавычки, а где нет. Соответственно все входные данных оборачивает ими.

суббота, 7 декабря 2019 г.

Защита от SQL иньекций в php

#php #sql #json #sql_injection


Пишу API, требуется обезопасить его от SQL инъекций. Как лучше и проще защититься от них?

Данные приходят в формате JSON и обрабатываются функцией json_decode(); После чего:


Используются в запросах как числа: "... WHERE id='{$id}' ". Требуется функция, которая
проверит, что это $id целое число. Или вернет число, к примеру: $id = real_int($id);
В результате должно состоять из знака минуса или символов 0-9.
Используются как строки: "... '{$text}' ... ".  По идее тут достаточно заменить '
на \', хотя помнится были варианты обхода такой защиты. В общем тоже нужна функция,
которая надежно защитит от инъекций.
Используются как список идентификаторов: "... WHERE id IN({$ids}) ". Полагаю их проще
распарсить и каждый id обработать функцией из первого пункта.

    


Ответы

Ответ 1



Разумеется, вся работа с чистым SQL должна производиться только через плейсхолдеры. Конечно, более предпочтительным вариантом является ORM (идеальный вариант для примитивных запросов, нужных автору) либо прочие квери билдеры - в этом случае пользователь РНР не видит SQL совсем, и следовательно, инъекцию при всем желании устроить не может. Но поскольку новичков эти слова обычно пугают до смерти, то этот вариант подходит только опытным пользователям. При работе же с чистым SQL, повторюсь, любые данные должны попадать в запрос только через плейсхолдеры. Разумеется, защита от инъекций должна производиться способом, отличным от "мы тут сейчас на жувачку прилепим пару функций, там из гуана и палок сварганим другую, потом наваяем ещё кода - и все полетит!". Защита должна быть единообразной и систематической. Разумеется, слой работы с БД должен быть отделен от слоя работы с прикладными данными, и предоставлять удобные и безопасные методы для работы с БД, чтобы не приходилось в бизнес-логике видеть какие-то непонятные вложенные по 30 раз друг в друга array_map, implode, intval и пр. ORM здесь опять на первом месте, а сырой SQL, по-хорошему, имеет смысл использовать только для сложных запросов. Но даже и для чистого SQL DB-layer обязан предоставлять безопасные методы, основанные на плейсхолдерах. Разумеется, в общем случае, использование PDO - это на самом деле, не лишний код, а сокращение кода. Поскольку PDO - единственный из драйверов mysql в РНР, который является высокоуровневой абстракцией, позволяющей сокращать рутинные операции. Разумеется, "implode(',', $ids) в подготовленных запросах" НИКАК НЕ спасёт ситуацию, а лишь вернёт неверные данные. Разумеется, все эти прекрасные рассуждения разбиваются о простой случай с WHERE id IN()... Можно, конечно, наваять кода, как предлагается по ссылке в комментариях. Но это, на самом деле, будет профанация. Нам не нужны на самом деле эти плейсхолдеры для каждого значения. Нам нужен способ тупо безопасно поместить массив в запрос, не перемешивая бизнес-логику с логикой защиты от инъекций. Для этого надо просто распространить практику работы с плейсхолдерами на несколько дополнительных типов данных. Этим и должен заниматься DB-layer. поскольку ПДО, увы, не предоставляет нужного функционала, можно воспользоваться другими библиотеками. Такими как DBSimple или Safemysql. В обоих случаях мы сможем решить все поставленные выше задачи. Вот, к примеру, для safemysql: Нужен запрос с WHERE id=1 с проверкой на int? $name = $db->getOne("SELECT name FROM users WHERE id=?i", $id); Будет выброшена ошибка, если в $id - не число. Нужен запрос со строковой переменной? $row = $db->getRow("SELECT * FROM users WHERE email=?s", $email); Переменная $email будет корректно отформатирована, и никакой мусор в ней не приведет к инъекции. Нужен запрос с пресловутым WHERE id IN()? Ради бога - всего лишь указываем нужный тип: $data = $db->getAll("SELECT * FROM users WHERE id IN(?a)", $ids); И опять драйвер сам отформатирует массив так, что инъекция не пройдет. Но сделает это все незаметно для пользователя. В этом и состоит смысл программирования - делегировать различные задачи разным сервисам, чтобы каждый занимался своим делом. Чтобы "лишний код" был инкапсулирован в свой сервис, а не писался в одной большой куче, как это принято в похапе. Нужно добавить в БД строку прямиком из джейсона? Нет проблем. $json = '{"name":"John","lastname":"Doe"}'; $db->query("INSERT INTO table SET ?u", json_decode($json,true)); здесь будут правильно отформатированы для запроса не только данные, но и имена полей, про которые обычно вообще не думают, а если и думают, то форматируют все равно неправильно.

пятница, 29 ноября 2019 г.

Каким образом избежать SQL-инъекций в PHP?

#php #mysql #sql #sql_injection


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

$unsafe_variable = $_POST['user_input'];

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");


Причина этому – пользователь может ввести любую цепочку символов, к примеру: value');
DROP TABLE table;--, и запрос приобретет следующий вид:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')


Что можно сделать, чтобы такого не происходило?

Перевод вопроса «How can I prevent SQL-injection in PHP?» @Andrew G. Johnson. 
    


Ответы

Ответ 1



Используйте подготовленные операторы и параметризованные запросы. Существуют SQL-выражения, которые отправляются и обрабатываются на сервере баз данных отдельно от параметров. Таким образом, злоумышленник не сможет внедрить вредоносный SQL-код. В общем случае, у вас есть два способа достичь задуманного. Используйте PDO (для любого поддерживаемого драйвера баз данных): $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name'); $stmt->execute(array('name' => $name)); foreach ($stmt as $row) { // do something with $row } Используйте MySQLi (для MySQL): $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?'); $stmt->bind_param('s', $name); $stmt->execute(); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()) { // do something with $row } Если база данных, к которой вы подключаетесь, управляется не MySQL, вы можете воспользоваться вторым вариантом (в зависимости от используемого драйвера, например, pg_prepare() и pg_execute() для PostgreSQL); универсальный вариант – PDO. Корректное установление соединения Учтите, что при использовании PDO для установления соединения с базой данных MySQL реальные подготовленные операторы по умолчанию не используются. Чтобы исправить ситуацию, вам нужно будет отключить эмуляцию подготовленных операторов. Пример установления соединения с помощью PDO: $dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass'); $dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); В приведенном выше примере режим обработки ошибок не является необходимым, но мы настоятельно рекомендуем его использовать. В этом случае при возникновении неполадок сценарий не остановится на Fatal Error. Также это дает разработчику шанс «отловить» ошибку (одну или несколько), которая вызывает исключение PDOException. Тем не менее, обязательным условием здесь является строка setAttribute(), которая «говорит» PDO отменить эмулированные подготовленные операторы и использовать реальные подготовленные операторы. Это гарантирует, что как оператор, так и значения не анализируются PHP перед отправкой на MySQL-сервер (что не дает возможности злоумышленнику внедрить вредоносный код). Вы, конечно, можете выбрать кодировку (charset) в опциях конструктора, но важно помнить, что «более старые» версии PHP (< 5.3.6) просто игнорировали этот параметр в DSN. Объяснение Происходит следующее: SQL-оператор, который вы передаете, чтобы «подготовить», анализируется и компилируется сервером баз данных. Указывая параметры (? или именованный параметр типа :name в вышеприведенном примере), вы сообщаете механизму базы, где вы хотите произвести фильтрацию данных. После этого, когда вы вызываете execute, подготовленный оператор комбинируется с указанными вами значениями параметров. При этом важно, что значения параметров комбинируются со скомпилированным оператором, а не с SQL-строкой. SQL-внедрение «обманывает» сценарий путем отправки в базу данных вредоносных SQL-строк. Поэтому посылая SQL отдельно от параметров вы уменьшаете риск получения неожиданного и нежелательного результата. Любые параметры, отправляемые при использовании подготовленного оператора, воспринимаются как строки (хотя, конечно, механизм базы данных может произвести определенную оптимизацию и параметры, в конце концов, могут быть преобразованы в численный формат). В вышеприведенном примере, если переменная $name содержит 'Sarah'; DELETE FROM employees результатом будет поиск строки "'Sarah'; DELETE FROM employees", а не пустая таблица. Еще одно преимущество использования подготовленных операторов – если один и тот же оператор исполняется много раз в течение одной сессии, он будет проанализирован и скомпилирован только один раз, что даст вам еще и оптимизацию в скорости. Да, и поскольку вы спросили о том, как поступить с кодом инъекции, вот пример (используется PDO): $preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)'); $preparedStatement->execute(array('column' => $unsafeValue)); Можно ли использовать подготовленные операторы для динамических запросов? Несмотря на то, что вы можете использовать подготовленные параметры в качестве параметров запроса, структура динамического запроса не может быть параметризована – равно как и некоторые особенности запроса. Для таких особых сценариев лучше всего использовать фильтр «белый список», который будет ограничивать диапазон возможных величин. // Value whitelist // $dir can only be 'DESC' or 'ASC' $dir = !empty($direction) ? 'DESC' : 'ASC'; Перевод ответа «How can I prevent SQL-injection in PHP?» @Theo.

вторник, 26 ноября 2019 г.

Грамотная защита от SQL-Injection


Уже давно меня мучает вопрос: Как защитить свой сайт от SQL-Injection. В былые времен
я использовал mysql_real string_escape для экранирования кавычек. Но прошло некое время
и я узнал про PDO, говорится, мол, эта тема неплохо поможет в защите сайта. Скажем, я не великий хакер и никак это не могу проверить, я знаю, что при mysql_real string_escape все кавычки и прочая ерунда экранировались - и вуаля, вы уже в домике. 

Сейчас я работаю над своим проектом и решил проверить сайт на защиту проникновения. В результате, что я делал:

Я просто брал и вводил данные с кавычками, например, Infor`mat'ion. Как я уже говорил
я не великий хакер, но точно знаю, что вся эта дрянь должна экранироваться, но она так и заносилась в базу данных, что натолкнуло меня на мысль, что никакой защиты у меня, по сути, и нету. А данные я заносил вот таким вот образом: 

$sql = "INSERT INTO chat (time, login, private, text) values(?, ?, ?, ?)";
$db->prepare($sql)->execute(array($time, $login, $private, $text));`


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

Как можно защититься от этих инъекций?
    


Ответы

Ответ 1



При использовании подготовленных выражений (prepared statement) параметры внешнег происхождения отправляются на сервер отдельно от самого запроса либо автоматически экранируются клиентской библиотекой. Создаём таблицу drop_table. Далее $name = "Evil'); DROP TABLE drop_table;--"; $sql = "INSERT INTO `test` (`name`) values('{$name}');"; $statement = $db->prepare($sql); $statement->execute(); Таблица drop_table успешно удалена. :) Теперь тот же запрос, но только через prepared statement: $sql = "INSERT INTO `test` (`name`) values(?);"; $statement = $db->prepare($sql); $statement->execute([$name]); В таблицу test добавилась запись Evil'); DROP TABLE drop_table;-- UPDATE Таким образом, мы обезопасили себя от SQL injection, но не защитили от XSS. В последнем случае требуется: валидация. К примеру, если необходимо оповестить пользователя о недопустимости введёных им данных либо для логирования; санитизация. Приведение данных к соответствующему типу (int)$age. В PHP существует набор фильтров, созданных именно для таких случаев; использования облегченного языка разметки. Markdown - именно им Вы и пользуетес на хэшкоде, либо можно задействовать старичка bbCode. В идеале заставить не только рядовог пользователя, но и администрацию сайта пользоваться им. В целях оптимизации, (чтобы каждый раз не парсить), можно воспользоваться key-value storage (memcached, redis,...) для хранения html версии. Если по каким-то причинам требуется хранить в базе html от источника, который н вызывает доверие, то можно воспользоваться HTML Purifier. Немного рекламы: :) для валидации: Rock Validate для санитизации: Rock Sanitize

Ответ 2



Все правильно: в таблицу и должна добавляться строка «Infor`mat'ion». Экранировани должно происходить именно в момент запроса, а не храниться в таком виде. И PDO прекрасн с этим справляется. И mysql_real_escape_string (и mysqli_real_escape_string) — тоже И даже str_replace — это тоже способ защититься. (См. UPD) То новое, что нам дает PDO, так это слежение за типами передаваемых данных и разграничение действия и данных. Популярная ошибка, которая приводила к плачевным результатам заключалась в том, что некоторые забывали проверять нестроковые типы: $q = "SELECT * FROM `table` WHERE `id` = ".$_GET['post_id']; Разница налицо: $q = "SELECT * FROM `table` WHERE `id` = ".intval($_GET['post_id']); Или просто всегда обрабатывай входные данные, следи за кавычками, за возможными представлениями чисел, за представлениями bool-данных... Или просто положись на готовую обертку, к примеру, PDO. :) UPD: WARNING! mysqli_real_escape_string (вероятно), mysql_real_escape_string, а тем более str_replac — небезопасны (\xbf\x27 и другие проблемы), используйте MySQL только в режиме настоящих подготовленных запросов! PDO нужно правильно использовать (спасибо @romeo за ссылку).

Ответ 3



PDO, и его prepare. $temp = $mysql->prepare('DELETE FROM `unigies` WHERE `idUser` = :idUser Limit 1;'); $temp->bindParam(':idUser', $id, PDO::PARAM_INT); $temp->execute();

среда, 17 апреля 2019 г.

Защита от SQL инъекций в jdbc java

Часто вижу утверждения, что надо использовать PreparedStatement вместо обычного Statement, чтобы защититься от sql инъекций. Как он защищает?


Ответ

Коротко, для нетерпеливых:
При использовании Statement строки запроса и значений складываются.
При использовании PreparedStatement имеется шаблон запроса и данные в него вставляются, с отражением кавычек.
Ниже подробнее с примерами.

Вступление.
Имеем такую простую таблицу с данными.
+-----------+----+--------+ | userName | id | pass | +-----------+----+--------+ | admin | 1 | admin | | user | 2 | pass | | chuchelo | 3 | elli | +-----------+----+--------+
Модель User, будет содержать имя и пароль, а так же метод логин, который спросит данные с консоли.
class UserLogin { String name; String pass;
public UserLogin() { }
public void login() { BufferedReader reader = null; try{ reader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("user name: "); name = reader.readLine();
System.out.println("pass: "); pass = reader.readLine(); } catch (IOException e) { e.printStackTrace(); }finally { if (reader != null) try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } }

Метод, который будет работать с обычным Statement
UserLogin user = new UserLogin(); user.login(); try (Connection connect = MyConnection.getConnection()){ Statement statement = connect.createStatement(); String query = "SELECT userName, id, pass FROM users WHERE userName='" + user.name + "' AND pass = '" + user.pass + "'"; System.out.println(query); ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()){ System.out.printf("User: id=%d name=%s pass=%s
", resultSet.getInt("id"), resultSet.getString("userName"), resultSet.getString("pass")); } MyConnection.closeConnect(); } catch (SQLException e) { e.printStackTrace(); }
Теперь если мы запустим этот метод и введем в консоль данные без инъекции:
user name: admin pass: admin
User: id=1 name=admin pass=admin
При этом сам запрос выглядит так:
SELECT userName, id, pass FROM users WHERE userName='admin' AND pass = 'admin'
Если допустить ошибку в имени или пароле, то данные выведены не будет.
Теперь попробуем использовать инъекцию(' or'1'='1), т.е. введем такие данные:
user name: admin' or'1'='1 pass: blabla
То мы все равно получаем результат, несмотря на то, что пароль неверный:
User: id=1 name=admin pass=admin
При этом сам запрос теперь выглядит так:
SELECT userName, id, pass FROM users WHERE userName='admin' or'1'='1' AND pass = 'blabla'
т.к. выражение or'1'='1' всегда равно true, то даже без указания пароля мы получим все данные.

Как от этого защитит PreparedStatement?
Метод который будет получать данные из базы с помощью PreparedStatement
UserLogin user = new UserLogin(); user.login(); try (Connection connect = MyConnection.getConnection()){ String query = "SELECT userName, id, pass FROM users WHERE userName=? AND pass=?"; PreparedStatement statement = connect.prepareStatement(query); statement.setString(1, user.name); statement.setString(2, user.pass); System.out.println(statement); ResultSet resultSet = statement.executeQuery();
while (resultSet.next()){ System.out.printf("User: id=%d name=%s pass=%s
", resultSet.getInt("id"), resultSet.getString("userName"), resultSet.getString("pass")); } MyConnection.closeConnect(); } catch (SQLException e) { e.printStackTrace(); }
Все тоже самое, только заменили обычный Statement на PreparedStatement. Надеюсь вы на слово поверите, что при правильных данных мы получим верный результат, если нет то вот лог в консоли:
user name: user pass: pass User: id=2 name=user pass=pass Запрос: SELECT userName, id, pass FROM users WHERE userName='user' AND pass='pass'
А теперь попробуем использовать инъекцию:
user name: user' or'1'='1 pass: inject
И ответа не получаем, потому что запрос выглядит так:
SELECT userName, id, pass FROM users WHERE userName='user\' or\'1\'=\'1' AND pass='inject'
Т.е. все кавычки были отражены слешем, инъекция не удалась.

Отличие Statement от PreparedStatement
Statement - вы должны заботиться о кавычках в запросе и ставить их там где они нужны.
PreparedStatement - вставляет значения в запрос и за счет методов setString setInt и прочих. Он сам понимает где нужны кавычки, а где нет. Соответственно все входные данных оборачивает ими.

понедельник, 1 апреля 2019 г.

Защита сайта от взлома [закрыт]

Создал, наконец, свой первый проект. Начитался кучу информации по защите сайта (sql-инъекции и т.д., и т.п.). Но я понимаю, что в сети не рассматриваются все нюансы по защите, поэтому я создал шуточный клон сайта, залил на сервер и пытаюсь найти дыры в безопасности. Пока безуспешно - стоит строгая проверка на url-адрес, админка скрыта в таинственной папке)))
Вопрос очевиден - я хочу понять, есть ли возможность взломать сайт, открыть структуру папок и попасть в админку.
Уважаемые программисты, помогите, пожалуйста! Я прописал в .htaccess вывод любых предупреждений и ошибок, поэтому буду премного благодарен за взлом собственного сайта! Фишка в том, что весь проект написан собственноручно в Коделобстере без использования готовых шаблонов.
P.S. Для админ-части аутентификация написана собственноручно, пожалуйста, взломайте и ее. Попыток входа - 3. Сейчас я увеличу до 20.
Псевдосайт - http://mars.fh38095o.bget.ru/
P.P.S. Кто действительно заинтересуется и покажет, что невозможно найти ссылку на админку, я скину url в личку.


Ответ

Я не знаю, что такое "коделобстер" но, судя по всему, 'то какая-то платформа для создания уязвимых сайтов. Выкладывать на проверку код со столь хрестоматийной SQL инъекцией должно быть стыдно.
Ну и просить протестировать на безопасность игрушечный сайт, в котором всего 5 таблиц (block_pages,category,feedback,simple_pages,temple) и нет практически никакой информации - это как бы смешно. Ты бы еще выложил программу Hello world и попросил ее взломать. Что там ломать? Зачем? Кому нужен этот сайт? Если бы, скажем, сайт хранил номера кредиток, то они бы уже уплыли, как эти глубокомысленные вопросы из таблицы feedback:
alert('hello'); hello ddddddd dddd ddddddddddd dddddddddddddddd dd dddd ddddddddd ddd dddddddd dd dddddd dddddddddddd ddddddddd ddddddddddd dd dddd dddddddddddd dd ddd ddddddd dddddddddd dddddddddddd ddd dd dddd dddddddd dddd ddddd dddd ddddddd d dddd dddddddd ра дывал п у дм жчсдялыпыа adfasdf asadf adf asdf asdf adf asdfasdfasfdasdfagfsdghsdbcxasdf
Опять же, интерфейса к этой таблице на сайте нет. То есть, в последний момент автор забоялся, и прикрутил вместо нее к сайту Disqus. Очень, очень остроумный способ тестирования.
Судя по тому, что в комментариях автор путает XSS с SQL инъекцией (собираясь защищаться от первой с помощью "stmt"), а "скрытая админка" почитается им как верх хитроумия в защите сайта, то ему просто рано выклыдывать что-либо на тестирование.
По поводу намека на дыры. Дело в том, что никакие намеки здесь не нужны. Все, что тебе нужно знать - это то, что была SQL инъекция. Ну так об этом было сказано безо всяких намеков, открытым текстом. А вот какая конкретно - для защиты знать не нужно, от слова "совсем". Фишка в том, что для обеспечения защиты про дыры знать не нужно. Защита от атак иррелевантна самим атакам. Про то что нужно для защиты - написано было во всех учебниках (ну, кроме устаревших): надо, чтобы любые переменные попадали в запрос не напрямую, а через плейсхолдеры. Вот это и надо было делать. Ты об этом знал (как следует из твоих комментариев, когда ты собрался XSS лечить через "stmt"), но не придавал значения. Вот теперь, после того как ты закрыл эту дыру, надеюсь, будешь придавать.
И опять же, сам видишь - чтобы закрыть дыру тебе не понадобилось узнавать, как был произведен взлом. Чтобы строить защиту, надо уметь строить, а не ломать. "Намекать на дыры" бесполезно. Здесь смысл не в том, чтобы залатать одну конкретную, а в том, чтобы все запросы без исключения выполнялись по единому безопасному принципу. И тогда хакер пусть хоть в лепешку разобьётся, но ни одной дыры не найдёт.
По поводу XSS. Ситуация довольно забавная. Действительно, где-то в недрах сайта режется, почему-то, прямой слеш. Но, разумеется, это не может помещать, и XSS можно внедрить и без слеша. Но тут уже, увы, на твою защиту встают браузеры, которые научились отфильтровывать такие явные XSS-через-запрос за горе-программистов.
Но опять же, надеяться на браузер - это значит гарантированно налететь на инъекцию. Такие вещи надо делать самому. Причем делать, опять же, не "как бог на душу положит", а сис-те-ма-ти-чес-ки!
Вот ты вырезал теги в фидбеке и успокоился. А в идентификаторе страницы - нет. А здесь должна быть та же система, что и с SQL инъекциями - не надо сидеть и думать, где знадо защищаться, а где не надо. Защищаться надо везде!
В защите от XSS аналогом плейсхолдеров в SQL может являться шаблонизатор с автоискейпингом. То есть, мы можем считать себя в безопасности, если
Любой, абсолютно любой вывод производится только через шаблонизатор. По умолчанию шаблонизатор форматирует ВСЕ выводимые переменные. И только те, для которых указано явно, что они должны выводиться как есть - выводятся как есть.
Таким шаблонизатором, в частности, является Twig
Да, и еще одно замечание. Код
or die("Ошибка в запросе $zapros");
это просто подарок взломщику. Скажем, если бы его не было, то я бы просто не взялся ломать - это потребовало бы куда больше времени, а время - деньги.
Судя по всему, ты прямо в коде обращаешься к функциям mysqli. Этого делать тоже не надо, но к безопасности это отношения не имеет, а для практики сойдёт. Но эти свои чудовищные or die() постирай все до единого. Вместо этого перед коннектом к mysql напиши одну строчку,
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

вторник, 2 октября 2018 г.

Грамотная защита от SQL-Injection

Уже давно меня мучает вопрос: Как защитить свой сайт от SQL-Injection. В былые времена я использовал mysql_real string_escape для экранирования кавычек. Но прошло некое время, и я узнал про PDO, говорится, мол, эта тема неплохо поможет в защите сайта. Скажем, я не великий хакер и никак это не могу проверить, я знаю, что при mysql_real string_escape все кавычки и прочая ерунда экранировались - и вуаля, вы уже в домике.
Сейчас я работаю над своим проектом и решил проверить сайт на защиту проникновения. В результате, что я делал:
Я просто брал и вводил данные с кавычками, например, Infor`mat'ion. Как я уже говорил, я не великий хакер, но точно знаю, что вся эта дрянь должна экранироваться, но она так и заносилась в базу данных, что натолкнуло меня на мысль, что никакой защиты у меня, по сути, и нету. А данные я заносил вот таким вот образом:
$sql = "INSERT INTO chat (time, login, private, text) values(?, ?, ?, ?)"; $db->prepare($sql)->execute(array($time, $login, $private, $text));`
Этот метод я вычитал на хабре в "Почему стоит пользоваться PDO для работы с базой данных", подключение такое же, как и указано в статье.
Как можно защититься от этих инъекций?


Ответ

При использовании подготовленных выражений (prepared statement) параметры внешнего происхождения отправляются на сервер отдельно от самого запроса либо автоматически экранируются клиентской библиотекой. Создаём таблицу drop_table. Далее $name = "Evil'); DROP TABLE drop_table;--";
$sql = "INSERT INTO `test` (`name`) values('{$name}');"; $statement = $db->prepare($sql); $statement->execute(); Таблица drop_table успешно удалена. :) Теперь тот же запрос, но только через prepared statement: $sql = "INSERT INTO `test` (`name`) values(?);"; $statement = $db->prepare($sql); $statement->execute([$name]); В таблицу test добавилась запись Evil'); DROP TABLE drop_table;-- UPDATE Таким образом, мы обезопасили себя от SQL injection, но не защитили от XSS В последнем случае требуется: валидация. К примеру, если необходимо оповестить пользователя о недопустимости введёных им данных либо для логирования; санитизация. Приведение данных к соответствующему типу (int)$age. В PHP существует набор фильтров, созданных именно для таких случаев; использования облегченного языка разметки. Markdown - именно им Вы и пользуетесь на хэшкоде, либо можно задействовать старичка bbCode. В идеале заставить не только рядового пользователя, но и администрацию сайта пользоваться им. В целях оптимизации, (чтобы каждый раз не парсить), можно воспользоваться key-value storage (memcached, redis,...) для хранения html версии. Если по каким-то причинам требуется хранить в базе html от источника, который не вызывает доверие, то можно воспользоваться HTML Purifier Немного рекламы: :) для валидации: Rock Validate для санитизации: Rock Sanitize