ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 28.04.2024
Просмотров: 100
Скачиваний: 0
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Посмотрим на следующий пример:
public ActionResult Index()
{
SqlConnection connection = new SqlConnection(); connection.ConnectionString = "строка соединения"; connection.Open();
return View () ;
}
Внимание, вопрос — когда будет закрыто соединение с базой данных? Да, соединение будет закрыто за нас, и нам не нужно, по идее, заботиться о ресурсах, но вот когда эти ресурсы будут освобождены? А фиг его знает. После выполнения этого кода соединение будет все еще открытым и занятым. Реальное освобождение ресурсов может произойти через минуту, а может через пять. Это значит, что на сервере будет большое количество открытых ресурсов, запрещенных к использованию. Сервер не безграничен в количестве одновременно доступных соединений, тут все зависит от настроек, и когда вы дойдете до максимума, новое соединение открыть будет невозможно и сайт упадет до тех пор, пока сборщик мусора не освободит неиспользуемые соединения.
Внимание, тут я упомянул два очень важных термина — открытые и занятые соединения. Надо понимать, что это разные вещи. Давайте посмотрим на цикл жизни соединения. Когда вы впервые открываете (вызываете метод Open) объект SqlConnection, то .NET делает следующее:
-
выделяет необходимые объекту ресурсы; -
устанавливает соединение с базой данных.
После этого соединение открыто и занято вашим объектом. Когда вы вызываете метод Close или Dispose, то .NET-объект SqlConnection уничтожается, а открытое соединение остается в живых на некоторое время и находится в специальном пуле. Соединение все еще открыто, но оно свободно для использования и может быть привязано к любому другому объекту SqlConnection.
Теперь, если вы попытаетесь открыть новый объект sqlconnection, то .NET проверит пул на наличие уже открытых соединений с такой же строкой подключения. Если они есть, то будет создан новый объект, но новое физическое соединение с сервером устанавливаться не будет, будет использовано существующее из пула. Таким образом экономится время на обмен приветственными сообщениями с базой данных, а объект Sqlconnection практически мгновенно становится доступным к использованию.
Я сопровождал сайт с миллионами пользователей, и у нас даже в час пик количество одновременно открытых соединений к базе данных не превышает 600. А вне часы пик, держится на отметке в 200 соединений. Недавно мы запускали небольшое обновление и вне часа пик количество соединений взлетело до 1500, а в час пик сайт упал из-за того, что не хватило свободного места в пуле. Мы увидели проблему как раз тогда, когда сайт упал из-за недостаточного количества соединений. Сразу после обновления не проверили, сколько открытых соединений.
Проблема была как раз в одном таком коде, когда открывалось соединение, но явно не закрывалось. Из-за того, что вовремя ресурсы не освобождались, мы теряли их без особого смысла. Когда я нашел проблему и исправил, количество соединений упало опять с более чем 1000 до 200.
При разработке web-приложений, если где-то нужно открывать ресурсы, всегда используйте конструкцию using: public ActionResult Index()
{
using (Sqlconnection connection = new SqlConnection())
{
connection.ConnectionString = "строка соединения"; connection.Open();
}
return View();
}
В этом случае .NET знает, что соединение нам нужно только на период, пока мы находимся внутри using-блока. Как только мы выходим за его пределы, платформа сразу видит, что этот ресурс нам не нужен и его можно освобождать. И тут мы точно можем знать (если программисты Microsoft не совершат ошибку в своем коде, что маловероятно), что соединение с базой данных будет закрыто сразу после выхода из блока using. Вам необязательно вызывать явно метод Close, достаточно просто использовать using.
Сборка мусора в .NET работает отлично, главное, правильно ею пользоваться и помогать платформе, указывая на то, когда ресурсы уже больше не нужны. Дальше она все возьмет на себя.
Я рекомендую использовать именно using. Посмотрим на следующий пример:
public ActionResult Index()
{
connection.ConnectionString = "строка соединения"; connection.Open();
connection.Close() ; return View () ;
}
Казалось бы, здесь все тоже отлично, мы открываем и явно закрываем соединение, поэтому утечки не должно быть. И ее не будет, если перед открытием не произойдет ошибки. Любая исключительная ситуация между Open и Close приведет к тому, что код прервет выполнение и соединение останется открытым. Если хакер сможет найти ситуацию, при которой генерируется исключение (например, неверный параметр), то он может реализовать отказ в обслуживании многократным вызовом страницы с такими неверными параметрами, чтобы соединения не освобождались, и в конце концов израсходовать все возможные соединения.
А как насчет такого случая, когда нужно использовать соединение в разных местах программы для выполнения двух разных запросов в двух разных местах? Если using не может объять оба кода (они находятся в разных методах), то не стоит даже пытаться объять необъятное. Создайте два объекта SqlConnection и откройте соединение дважды. Это не проблема для сервера, потому что он может использовать пул соединений Connection Pool. Сразу же после закрытия первого соединения, оно реально не будет закрыто. Будет уничтожен объект, и соединение будет помечено как свободное. В течение некоторого времени (легко конфигурируется) соединение будет оставаться открытым, и при создании следующего объекта SqlConnection, не нужно тратить время на установку соединения с сервером, можно использовать уже открытое из пула.
В этом примере я использовал в качестве иллюстрации соединение с базой данных как наиболее популярный тип ресурса для подобных приложений. То же самое может касаться и таких ресурсов, как файлы, и, наверное, чего угодно, что требует закрытия. В таких случаях классы реализуют интерфейс Idisposable, и если вы работаете с одним из таких классов, то старайтесь использовать его вместе с using.
Вы не обязаны закрывать соединение самостоятельно, хотя все же явное закрытие является хорошим тоном, и помните, что это позволит вам защититься от DoS- атаки.
ГЛАВА 9
Авторизация
Авторизацию мы рассматриваем в этой книге для понимания очень популярной и достаточно опасной атаки — XSS (Cross Site Scripting — межсайтовый скриптинг).
Обновлять информацию на web-сайте через FTP-клиента с помощью закачки новых файлов достаточно неудобно и не всегда возможно например, уже в конце 1990-х мне приходилось работать в сетях, где доступ в интернет осуществлялся только по HTTP. Именно тогда я задумался о системе администрирования web-сайтов посредством web-интерфейса. Сейчас web-интерфейс стал стандартом для управления любым сайтом.
Сценарии администрирования позволяют управлять содержимым web-страниц, поэтому должны быть закрыты от постороннего взгляда. Для этого необходима отдельная защита, которая позволит вам спать спокойно и не даст хакеру выполнить привилегированные операции. В качестве такой защиты можно использовать средства аутентификации.
Зарегистрированными пользователями проще управлять и контролировать их действия и будет легче защититься от накруток. Авторизация нужна не только администраторам, но и пользователям — им можно предоставлять более персонализированные сервисы, чтобы они могли пользоваться определенными привилегиями.
Зарегистрированные пользователи обычно лояльны сайту, но это уже вопрос маркетинга. В любом случае регистрация может быть выгодна и полезна широкому кругу сайтов.
-
Аутентификация на web-сервере
Если какой-либо каталог web-сервера должен иметь особые права доступа, то можно создать в нем файл .htaccess. В этом файле описываются разрешения, действующие на каталог, в котором он расположен. При обращении к любому файлу из данного каталога web-сервер будет требовать аутентификации. Да, аутентификации будет требовать именно web-сервер, поэтому дополнительные сценарии не понадо
бятся. Таким образом, вы получаете в свое распоряжение эффективный и отлаженный годами метод обеспечения безопасности конфиденциальных данных.
Рассмотрим пример содержимого файла .htaccess:
AuthType Basic
AuthName "By Invitation Only"
AuthUserFile /pub/home/flenov/passwd Require valid-user
В первой строке задается тип аутентификации с помощью директивы AuthType. В данном примере используется базовая аутентификация (Basic), в этом случае web-сервер при обращении к каталогу отобразит окно для ввода имени и пароля. Текст, указанный в директиве AuthName, появится в заголовке окна (рис. 9.1).
Рис. 9.1. Окно запроса имени и пароля
Директива AuthUserFile задает имя файла, в котором находится база имен и паролей пользователей web-сайта. О том, как создавать такой файл и работать с ним, мы поговорим позже. Последняя директива Require в качестве параметра использует значение valid-user. Это значит, что файлы в текущем каталоге смогут открыть только те пользователи, которые прошли аутентификацию.
Вот таким простым способом можно запретить неавторизованный доступ к каталогу, содержащему секретные данные или сценарии администратора. Более подробную информацию об этом методе авторизации можно узнать из моих книг "Linux глазами хакера" или "PHP глазами хакера".
Такой подход к авторизации неплохо подходит для защиты страницы администрирования. В случае с авторизацией пользователей на сайте такой подход неудобен и неэффективен, поэтому в наше время его мало кто использует.
Когда пользователь вводит имя и пароль в такое окно, то браузер запоминает эти данные, кодирует с помощью кодировки base64 и начинает отправлять на сервер с абсолютно каждым запросом в виде параметра authorization (рис. 9.2). Просмот
реть это значение можно, если открыть утилиты разработчика в браузере, загрузить страницу и кликнуть на абсолютно любом запросе, который направляется к вашему серверу с базовой защитой.
Михаил Флёнов
Рис. 9.2. Параметр заголовка с данными авторизации
Как видно на скриншоте, у меня в заголовке параметр authorization равен Basic bWZsZW5vdjpwYXNzd29yZA==
После слова basic идет код в Base64, который легко превращается в чистый текст. В Google вбиваем Base64 decode, и вы найдете много сайтов, которые умеют декодировать такой код. Воспользовавшись одним из них, вы легко узнаете, что bWZsZW5vdjpwYXNzd29yZA== на самом деле соответствует mflenov:password. До двоеточия идет имя, а после идет пароль. Да, я на самом деле для данного примера задал пароль в виде слова password. Это только для примера, в реальности, конечно, у меня пароли намного сложнее.