ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 28.04.2024
Просмотров: 91
Скачиваний: 0
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Например, если у нас сайт www.bankname.ru, хакер может создать свой сайт www.fakebank.ru и подключить ресурсы с нашего сайта. С помощью Cross-origin Resource Sharing мы можем сказать, можно ли отдавать свои ресурсы сайта www.bankname.ru, если пользователь обращается реально к домену
www.fakebank.ru.
Если не сделать защиты, то есть шанс, что хакер сможет обратиться к ресурсам банка и, возможно, подсмотреть какую-то информацию.
Например, пользователь загружает сайт www.fakebank.ru с поддельной формой для изменения пароля, но на реальном сайте банка есть защита в виде какого-то секрета, который отображается на странице. Страница www.fakebank.ru может после загрузки отправить Ajax-запрос к реальному банку, чтобы в фоне загрузить реальную страницу и найти на ней секрет.
Может ли банк защититься от такого запроса? Блокировать по Referer не получится, потому что пользователь может переходить на сайт банка с другого сайта легитимно. Например, по ссылке в поиске Google или с какого-то другого сайта. Это нормально, что пользователь переходит на ваш сайт с других. Плохо, когда он отправляет форму на сайт.
Банк может сообщить браузерам, при обращении к каким доменам разрешено отгружать его ресурсы с помощью заголовка Access-Control-Allow-Origin, в котором могут быть домены. На PHP это делается с помощью метода header:
header("Access-Control-Allow-Origin: www.bankname.ru ");
?>
В .NET чуть больше шагов, но тоже не так сложно:
using System.Web.Http.Cors;
namespace Controllers {
[EnableCors(origins: "www.bankname.ru", headers: "*", methods: "*")] public class MyController : ApiController {
// Код метода
}
}
За реализацию защиты Cross-origin Resource Sharing отвечают браузеры, потому что сервер не может знать — пользователь загружает сайт сам или загрузка инициирована сайтом с помощью AJAX-запроса.
А вот браузеры знают, с чем именно они работают и могут сделать проверку, соответствует запрос требованиям сайта (банка) или есть нарушения. Но сейчас все браузеры уже реализуют требования к защите от межсайтовых запросов.
Есть два параметра заголовков:
-
Access-Control-Allow-Origin — определяет, какие домены могут обращаться к ресурсам сайта; -
Access-Control-Allow-Methods — показывает, какие типы запросов можно отправлять GET, POST, PUT и т. д. серверу для доступа к его ресурсам. Чаще всего необходим GET, поэтому стоит подумать над тем, чтобы добавить только его в список разрешенных. POST-запросы между сайтами тоже иногда бывают необходимы, но это встречается немного реже.
Надеюсь, мне удалось вас убедить, что межсайтовое взаимодействие опасно и может привести к серьезным проблемам. К этому вопросу нужно подходить с полной ответственностью.
ГЛАВА 8
DoS-атака на web-сайт
Когда найти ошибку в сценариях web-сайта не удается, хакеры начинают прибегать к другим методам, в частности к DoS-атаке. Мы уже говорили о ней ранее (см.разд. 1.7), а сейчас рассмотрим более подробно: увидим, как с ее помощью можно сделать web-сервер недоступным или максимально затормозить его работу. В этом случае добросовестным пользователям не остается ресурсов сервера, и они не получают ответа.
-
Поиск медленных страниц
Когда хакер ищет цель для организации DoS-атаки, он выбирает те web-страницы, которые загружаются очень долго, что говорит об их плохой оптимизации, то есть чтобы web-сервер мог отобразить необходимые данные, ему приходится осуществить множество операций, сильно загружающих процессор.
Во время работы над сайтом, который можно было отнести к электронной коммерции Sony в США, мне приходилось несколько раз сталкиваться с ситуацией, когда проблемы в производительности становились причиной значительного замедления серверов. Хакеры регулярно искали страницы, которые генерировались на сервере медленнее всего, и пытались атаковать их, поэтому приходилось быть очень внимательным к каждой странице.
Из личного опыта могу сказать, что мне чаще всего приходилось сталкиваться с тремя ошибками, которые приводили к проблемам с производительностью страниц на сайте:
-
доступ к базе данных — чаще всего в web самым слабым местом являются SQL- запросы, которые выполняются очень долго; -
переполнение ресурсов; -
неосвобождение ресурсов.
В этой главе мы поговорим о тех проблемах, с которыми мне приходилось сталкиваться на практике, и как я в таких случаях оптимизировал код.
Для тестирования производительности страниц я предпочитаю использовать JMeter — очень простая программа от Apache https://jmeter.apache.org/ — простая программа с открытым исходным кодом на Java.
-
Оптимизация работы с СУБД
На своем YouTube-канале я как-то наглядно показал влияние индексов на производительность запросов: https://www.youtube.com/watch?v=fAHtiulkQnA. Один правильный индекс способен значительно снизить нагрузку на сервер и позволит обрабатывать больше пользователей.
С другой стороны, отсутствие индекса или плохой запрос способны стать причиной того, что сервер будет занят обработкой только SQL.
Оптимизация SQL-запросов позволяет сэкономить мощности web-сервера и повысить его производительность, что уменьшит возможность положительного исхода проводимой против него DoS-атаки. Для доступа к данным используется язык запросов SQL, который стандартизирован много лет назад, но не потерял своей актуальности и по сей день. Стандарт будет использоваться еще долгое время, а вот возможности, которые он предоставляет, уже не могут обеспечить современных потребностей.
В своей практике я использовал различные СУБД и не раз обжигался на том, что они по-разному могут обрабатывать даже SQL-запросы. Вроде бы все выполняется верно, но с небольшими отклонениями: например, СУБД может и не поддерживать чтение данных, которые записаны в базу данных, но еще не подтверждены. Это свойство связано с поддержкой различных уровней изоляции и настроек.
Поэтому вы должны с самого начала писать сценарий именно под ту СУБД, с которой будет происходить работа. Нельзя писать код под MS SQL Server или MySQL, а потом просто перенести его под Oracle. Это совершенно разные системы, которые работают по-разному, поэтому вследствие такого переноса могут возникнуть проблемы не только с производительностью, но и верного выполнения запроса. У MySQL и SQL Server по-разному реализованы функции ограничения выборки, что часто используется при написании страничных запросов.
При оптимизации приложений, работающих с СУБД, нужно действовать с двух сторон: оптимизировать саму СУБД и средства доступа к данным (SQL-запросы). Причем работать нужно сразу над обеими составляющими, потому что они взаимосвязаны: повышение производительности СУБД может негативно сказаться на производительности SQL-запроса.
Данная книга не является руководством по СУБД, поэтому оптимизацию мы рассмотрим только в общих чертах. За более подробной информацией обращайтесь к специализированной литературе.
-
Оптимизация SQL-запросов
Некоторые программисты считают, что запросы работают одинаково в любой СУБД. Это большая ошибка. Действительно, существует стандарт SQL, и запросы, написанные на нем, будут восприняты в большинстве систем одинаково. Но только "восприняты", а их обработка может происходить совершенно по-разному.
Максимальные проблемы во время переноса приложения могут принести расширения языка SQL. Так, например, в MS SQL Server используется Transact-SQL, а в Oracle — PL/SQL, и их операторы совершенно несовместимы. Вы должны заранее определиться с используемой СУБД, чтобы не столкнуться с возможными проблемами в будущем.
Но даже если вы переведете синтаксис запросов с одного языка на другой, проблем по-прежнему останется очень много. Это связано с различными архитектурами оптимизаторов, разницей в блокировках и т. д. Если код программы при смене СУБД требует незначительных изменений, то запросы могут потребовать работы.
Несмотря на большие различия между СУБД разных производителей, есть и общие стороны: например, большинство из них выполняет запросы следующим образом:
-
разбор запроса; -
оптимизация; -
генерация плана выполнения; -
выполнение запроса.
Это всего лишь общий план выполнения, а в каждой конкретной СУБД количество шагов может меняться. Но главное состоит в том, что перед выполнением происходит несколько шагов по подготовке, которые могут отнимать много времени.
Для очень простого запроса с одним select разбор и генерация плана может занять миллисекунды, но для большого запроса с десятками SELECT и большим количеством join подготовительные этапы могут занимать секунду и даже более.
После выполнения запроса использованный план будет сохранен в специальном буфере. При следующем запросе эти данные будут получены из буфера, и сразу же начнется его выполнение без лишних затрат на подготовку.
Сервер сможет повторно использовать план выполнения, но только при определенных условиях. Посмотрим на два SQL-запроса:
SELECT *
FROM TableName WHERE ColumnName=10
и
SELECT *
FROM TableName WHERE ColumnName=20
Оба выбирают все данные из одной и той же таблицы. Только первый покажет строки, в которых колонка ColumnName содержит значение 10, а второй покажет строки, где эта же колонка содержит значение 20. На первый взгляд они очень похожи и должны выполняться по одному и тому же плану. Но это заметно только человеку, оптимизатор этого может не увидеть и, несмотря на их схожесть, будет производить все подготовительные шаги для каждого.
Чтобы этого не было, нужно использовать переменные. Переменные в SQL схожи по назначению с переменными в PHP, но в зависимости от СУБД и драйвера могут оформляться разным способом. Поэтому я не буду делать никаких оформлений, чтобы не сбить вас с толку, а просто буду называть переменные именем paramx, где X — это любое число:
SELECT *
FROM TableName
WHERE ColumnName=param1
Теперь достаточно только передать значение переменной param1. В этом случае SQL-запросы будут восприниматься оптимизатором как одинаковые и лишних затрат на проведение подготовительных этапов не будет.
Получается, что параметры позволяют защититься не только от SQL-инъекции, но и повысить скорость выполнения сервером запросов. Используйте этот очень простой и эффективный метод.
Буфер для хранения планов выполнения не бесконечен, поэтому в нем хранятся данные только о последних SQL-запросах (количество зависит от размера буфера). Если какой-то SQL-запрос выполняется достаточно часто, то в нем обязательно нужно использовать переменные, потому что это значительно повысит производительность. Попробуйте дважды выполнить один и тот же SQL-запрос и посмотреть на скорость выполнения. Вторичное выполнение будет намного быстрее, что может быть заметным даже на глаз.
Если вы работаете с MS SQL Server, то в нем я рекомендую выполнить команду:
SET STATISTICS TIME ON
И после этого выполните SQL-запрос. В SQL Management Studio помимо результата запроса вы должны на вкладке Messages (рис. 8.1) увидеть еще и статистику выполнения, например такую:
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 1 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 1 ms.
(0 row(s) affected)
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
Рис. 8.1. Служебная информация выполнения запроса