предисловие
Я не знаю, ребята, видели ли вы, ребята, остросюжетный фильм-рассуждение режиссера барона Бо Одала - "Кто я: Нет абсолютно безопасной системы", главный герой фильма и его друзья создали хакерскую организацию CLAY (Clawn Laghing At You означает шутка клоуна) и последовательно вторгся в международную систему безопасности, международную финансовую систему, международную систему финансовой оценки, Службу безопасности Германии и Службу разведки Германии. Как программист, когда я увидел, как главный герой фильма использует уязвимости системы для вторжения, я не мог не подумать, что системное программное обеспечение, в разработке которого я участвовал, также должно иметь более или менее лазейки (по оценкам, это программное обеспечение поддерживается другими .Но три минуты 😂). Мы не можем гарантировать, что в системе вообще нет лазеек, и мы не можем гарантировать, что система абсолютно безопасна, мы можем только найти и исправить как можно больше лазеек, чтобы сделать нашу систему максимально безопасной. Итак, сегодня я подытожу вместе с вами распространенные уязвимости и соответствующие решения 💪.
P.S. Хоть это всего лишь фильм, но сюжет будет вымышленным, но после просмотра фильма я все же сказал такую фразу: Эта группа людей слишком 🐂 критикуется (другими словами это уже не описать)! Это действительно стоящий фильм
Общие уязвимости безопасности и решения
SQL-инъекция
Что такое SQL-инъекция
SQL-инъекция является одной из наиболее распространенных сетевых атак, это не использование операционной системы BUG для достижения атаки, а защита от небрежного написания программистами, вторжение через вход в учетную запись без операторов SQL или даже подделка базы данных.
Возьмем пример: например, теперь есть функция входа в систему, мы объединяем параметры (учетная запись, пароль и т. д.), представленные пользователем в коде, и, наконец, объединяем их в полный оператор SQL для проверки личности пользователя👇
String sql = "select * from user where username=' "+userName+" ' and password=' "+password+" '";
Если кто-то в это время введет недопустимые параметры для отправки, это может привести к тому, что приведенное выше утверждение станет таким👇
select * from user where username='xxx' or 1 = 1 - - and password=' '
Давайте проанализируем это выражение SQL: username='xxx' или 1 = 1 после того, как условие where означает "имя пользователя равно xxx или 1=1", тогда это условие становится постоянным состоянием; Давайте оглянемся назад, и есть два небольших горизонтальных строки перед ключевым словом и. Эта горизонтальная линия должна быть вам знакома, верно? В операторе SQL две маленькие горизонтальные линии представляют собой комментарии, которые делают недействительной проверку пароля после , и, другими словами, приведенный выше оператор является оператором, который может быть успешно выполнен, таким образом достигая цели SQL-инъекции. Это по-прежнему относительно мягкая инъекция SQL, что, если оператор SQL станет таким? 👇
select * from user where username='xxx' ; DROP DATABASE (DB Name); - - and password=' '
Если предложение действительно SQL успешно выполнено в базе данных, то последствия будут катастрофическими...
Решения для SQL-инъекций
Любая программа с уязвимостью SQL-инъекций связана с тем, что программе необходимо принять ввод переменной от пользователя клиента или параметр, переданный URL-адресом, и эта переменная или параметр является частью инструкции SQL.Для содержимого, введенного пользователем или параметром прошло, мы с Вами всегда должны быть начеку,Мы должны строго соблюдать принцип ненадежных внешних данных, Среди различных методов атак в области веб-безопасности, большинство из них вызваны разработчиками, нарушающими этот принцип, поэтому естественно думать, начиная с обнаружения, фильтрации и проверки параметров, чтобы гарантировать, что параметры являются Разработчики Ожидаемые параметры безопасности.
①Проверьте тип и формат переменных данных: Если ваш SQL заявление аналогично , где ID = {$ идентификатор} в виде базы данных всех чисел идентификатор, то он должен быть перед оператор SQL выполняется, тип проверки принятого параметра ID не является ИНТ; Если номер телефона принимается параметры, они должны проверить и обеспечить строгие параметры должны быть формат номер телефона, другие типы параметров также является причиной. В коротком предложении: до тех пор, как есть параметры фиксированного формата (номер, номер телефона, адрес электронной почты и т.д.), оператор SQL перед выполнением, должно быть строго в соответствии с фиксированным форматом, который будет рассмотрен, чтобы убедиться, что переменная формат мы ожидали, так что вы можете в очень предотвратить SQL - инъекций в значительной степени. Мы начали приводит пример проверку имени пользователя и пароль, если у нас есть регулярное имя пользователя в стадии разработки продукта, такие как положения длины имени пользователя должен быть 5-15 символов, а имя пользователя только самым размер букв записи, цифр и некоторых символов безопасности, не содержит специальных символов. Поэтому на этот раз мы должны написать check_username (проверьте имя пользователя) метод, имя пользователя из параметров методы получило единый экзамен. Но в статье публикации системы, комментарий система должна позволять пользователям представлять любую строку сцены, проверьте выше образом неуместно, то вам нужно использовать другие программы, такие как фильтры.
P.S. Для проверки параметров фиксированного формата вы можете использовать библиотеку классов инструментов Hutool, чтобы сделать ваш код «приятным» ~ Друзья, которые не прикасались к Hutool, могут обратиться кBig Clever научит вас изучать Java | Hutool — набор инструментов, которые делают Java приятной
②фильтровать специальные символы: Это предложение легко понять.Для некоторых параметров, которые не имеют фиксированного формата, нам нужно отфильтровать их по спецсимволам, чтобы запретить пользователям передавать небезопасные спецсимволы.
③Использование предложенных утверждений: Так называемый подготовленный оператор предназначен для замены значения переменной в операторе SQL заполнителем.Можно сказать, что оператор SQL является шаблонным или параметризованным.Как правило, этот тип оператора называется подготовленным оператором или параметризованным оператором. Использование предварительно скомпилированных операторов для связываемых переменных — лучший способ предотвратить внедрение SQL. Семантика использования предварительно скомпилированных операторов SQL не изменится. В операторах SQL переменные представлены вопросительными знаками? Символ комментария SQL (то есть две упомянутые горизонтальные линии) выше) передается, что не может изменить структуру всего оператора SQL.
④Если используется MyBatis, попробуйте использовать формат # {xxx} при написании оператора сопоставления: В MyBatis,Формат параметра как {xxx}. Следовательно, такие параметры необходимо обрабатывать вручную в коде, чтобы предотвратить SQL-инъекцию.
Уязвимость межсайтового скриптинга
Что такое уязвимость межсайтового скриптинга
Межсайтовый скриптинг (Межсайтовый скриптинг, обычно называемый XSS) происходит на стороне клиента и может использоваться для таких атак, как кража конфиденциальности, фишинг, кража паролей и распространение вредоносного кода. В XSS-атаках используются в основном HTML и Javascript, а также VBScript и ActionScript. Хотя XSS-атака не наносит прямого вреда веб-серверу, она распространяется через веб-сайт, так что пользователи веб-сайта подвергаются атаке, а учетная запись пользователя веб-сайта украдена, что наносит серьезный вред веб-сайту. Проще говоря, XSS-атака заключается во вставке вредоносного кода сценария на веб-страницу.Когда пользователь просматривает страницу, вредоносный код сценария, встроенный в страницу, будет выполняться, тем самым достигая цели злонамеренной атаки на пользователя. Атаки XSS делятся на следующие три категории👇
- Светоотражающие XSS
: атакующий заранее, чтобы сделать хорошую ссылку на атаку, вам нужно обмануть пользователей, чтобы нажать на ссылку, чтобы вызвать код XSS (серверу нет таких страниц и контент), обычно подверженным продовольствию к странице поиска или появиться в корпусе короткого сообщения. (Нажмите на неизвестные ссылки не произвольно О, очень опасно 🈲) - Тип хранения XSS
: Это код, сохраненный атакой XSS на сервер, такой как хакеры, изменяют или публикуют личную информацию, такую как место для загрузки вредоносного кода, в настоящее время, если нет фильтра или фильтра LAX, то Код будет храниться на сервере, всякий раз, когда пользователь посещает страницу, будет выполнять выполнение кода, эта атака XSS очень опасна, вероятно, приведет к тому, что может привести к большому количеству червей. - XSS типа DOM: это межсайтовая уязвимость, которая возникает в DOM на стороне клиента (объектная модель документа), что в значительной степени связано с проблемами безопасности, вызванными логикой обработки сценариев на стороне клиента.
Решения для уязвимостей межсайтового скриптинга
Как и советы по защите от SQL-инъекций, мы по-прежнемуСтрого следуйте принципу, что внешним данным нельзя доверятьВсе слова, такие как скрипт, Iframe и т. Д. На входе, должны быть строго проверены. Вход здесь - не только интерфейс ввода, с которым пользователь может напрямую взаимодействовать, но и переменные в cookie в HTTP-запросе, переменные в заголовке HTTP-запроса и т. Д. Здесь рекомендуется использовать фильтры для проверки параметров. В конце концов, есть так много мест для проверки и проверки, поэтому вы не можете написать их один за другим ~ Перейти непосредственно к коду
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class HTMLFilter {
/** regex flag union representing /si modifiers in php **/
private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
private static final Pattern P_END_ARROW = Pattern.compile("^>");
private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
private static final Pattern P_AMP = Pattern.compile("&");
private static final Pattern P_QUOTE = Pattern.compile("<");
private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
// @xxx could grow large... maybe use sesat's ReferenceMap
private static final ConcurrentMap<String,Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<String, Pattern>();
private static final ConcurrentMap<String,Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<String, Pattern>();
/** set of allowed html elements, along with allowed attributes for each element **/
private final Map<String, List<String>> vAllowed;
/** counts of open tags for each (allowable) html element **/
private final Map<String, Integer> vTagCounts = new HashMap<String, Integer>();
/** html elements which must always be self-closing (e.g. "<img />") **/
private final String[] vSelfClosingTags;
/** html elements which must always have separate opening and closing tags (e.g. "<b></b>") **/
private final String[] vNeedClosingTags;
/** set of disallowed html elements **/
private final String[] vDisallowed;
/** attributes which should be checked for valid protocols **/
private final String[] vProtocolAtts;
/** allowed protocols **/
private final String[] vAllowedProtocols;
/** tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />") **/
private final String[] vRemoveBlanks;
/** entities allowed within html markup **/
private final String[] vAllowedEntities;
/** flag determining whether comments are allowed in input String. */
private final boolean stripComment;
private final boolean encodeQuotes;
private boolean vDebug = false;
/**
* flag determining whether to try to make tags when presented with "unbalanced"
* angle brackets (e.g. "<b text </b>" becomes "<b> text </b>"). If set to false,
* unbalanced angle brackets will be html escaped.
*/
private final boolean alwaysMakeTags;
/** Default constructor.
*
*/
public HTMLFilter() {
vAllowed = new HashMap<>();
final ArrayList<String> a_atts = new ArrayList<String>();
a_atts.add("href");
a_atts.add("target");
vAllowed.put("a", a_atts);
final ArrayList<String> img_atts = new ArrayList<String>();
img_atts.add("src");
img_atts.add("width");
img_atts.add("height");
img_atts.add("alt");
vAllowed.put("img", img_atts);
final ArrayList<String> no_atts = new ArrayList<String>();
vAllowed.put("b", no_atts);
vAllowed.put("strong", no_atts);
vAllowed.put("i", no_atts);
vAllowed.put("em", no_atts);
vSelfClosingTags = new String[]{"img"};
vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
vDisallowed = new String[]{};
vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
vProtocolAtts = new String[]{"src", "href"};
vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
stripComment = true;
encodeQuotes = true;
alwaysMakeTags = true;
}
/** Set debug flag to true. Otherwise use default settings. See the default constructor.
*
* @param debug turn debug on with a true argument
*/
public HTMLFilter(final boolean debug) {
this();
vDebug = debug;
}
/** Map-parameter configurable constructor.
*
* @param conf map containing configuration. keys match field names.
*/
public HTMLFilter(final Map<String,Object> conf) {
assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
vDisallowed = (String[]) conf.get("vDisallowed");
vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
vProtocolAtts = (String[]) conf.get("vProtocolAtts");
vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
vAllowedEntities = (String[]) conf.get("vAllowedEntities");
stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
}
private void reset() {
vTagCounts.clear();
}
private void debug(final String msg) {
if (vDebug) {
Logger.getAnonymousLogger().info(msg);
}
}
//---------------------------------------------------------------
// my versions of some PHP library functions
public static String chr(final int decimal) {
return String.valueOf((char) decimal);
}
public static String htmlSpecialChars(final String s) {
String result = s;
result = regexReplace(P_AMP, "&", result);
result = regexReplace(P_QUOTE, """, result);
result = regexReplace(P_LEFT_ARROW, "<", result);
result = regexReplace(P_RIGHT_ARROW, ">", result);
return result;
}
//---------------------------------------------------------------
/**
* given a user submitted input String, filter out any invalid or restricted
* html.
*
* @param input text (i.e. submitted by a user) than may contain html
* @return "clean" version of input, with only valid, whitelisted html elements allowed
*/
public String filter(final String input) {
reset();
String s = input;
debug("************************************************");
debug(" INPUT: " + input);
s = escapeComments(s);
debug(" escapeComments: " + s);
s = balanceHTML(s);
debug(" balanceHTML: " + s);
s = checkTags(s);
debug(" checkTags: " + s);
s = processRemoveBlanks(s);
debug("processRemoveBlanks: " + s);
s = validateEntities(s);
debug(" validateEntites: " + s);
debug("************************************************\n\n");
return s;
}
public boolean isAlwaysMakeTags(){
return alwaysMakeTags;
}
public boolean isStripComments(){
return stripComment;
}
private String escapeComments(final String s) {
final Matcher m = P_COMMENTS.matcher(s);
final StringBuffer buf = new StringBuffer();
if (m.find()) {
final String match = m.group(1); //(.*?)
m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
}
m.appendTail(buf);
return buf.toString();
}
private String balanceHTML(String s) {
if (alwaysMakeTags) {
//
// try and form html
//
s = regexReplace(P_END_ARROW, "", s);
s = regexReplace(P_BODY_TO_END, "<$1>", s);
s = regexReplace(P_XML_CONTENT, "$1<$2", s);
} else {
//
// escape stray brackets
//
s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);
//
// the last regexp causes '<>' entities to appear
// (we need to do a lookahead assertion so that the last bracket can
// be used in the next pass of the regexp)
//
s = regexReplace(P_BOTH_ARROWS, "", s);
}
return s;
}
private String checkTags(String s) {
Matcher m = P_TAGS.matcher(s);
final StringBuffer buf = new StringBuffer();
while (m.find()) {
String replaceStr = m.group(1);
replaceStr = processTag(replaceStr);
m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
}
m.appendTail(buf);
s = buf.toString();
// these get tallied in processTag
// (remember to reset before subsequent calls to filter method)
for (String key : vTagCounts.keySet()) {
for (int ii = 0; ii < vTagCounts.get(key); ii++) {
s += "</" + key + ">";
}
}
return s;
}
private String processRemoveBlanks(final String s) {
String result = s;
for (String tag : vRemoveBlanks) {
if(!P_REMOVE_PAIR_BLANKS.containsKey(tag)){
P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
}
result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
if(!P_REMOVE_SELF_BLANKS.containsKey(tag)){
P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
}
result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
}
return result;
}
private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {
Matcher m = regex_pattern.matcher(s);
return m.replaceAll(replacement);
}
private String processTag(final String s) {
// ending tags
Matcher m = P_END_TAG.matcher(s);
if (m.find()) {
final String name = m.group(1).toLowerCase();
if (allowed(name)) {
if (!inArray(name, vSelfClosingTags)) {
if (vTagCounts.containsKey(name)) {
vTagCounts.put(name, vTagCounts.get(name) - 1);
return "</" + name + ">";
}
}
}
}
// starting tags
m = P_START_TAG.matcher(s);
if (m.find()) {
final String name = m.group(1).toLowerCase();
final String body = m.group(2);
String ending = m.group(3);
if (allowed(name)) {
String params = "";
final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
final List<String> paramNames = new ArrayList<String>();
final List<String> paramValues = new ArrayList<String>();
while (m2.find()) {
paramNames.add(m2.group(1)); //([a-z0-9]+)
paramValues.add(m2.group(3)); //(.*?)
}
while (m3.find()) {
paramNames.add(m3.group(1)); //([a-z0-9]+)
paramValues.add(m3.group(3)); //([^\"\\s']+)
}
String paramName, paramValue;
for (int ii = 0; ii < paramNames.size(); ii++) {
paramName = paramNames.get(ii).toLowerCase();
paramValue = paramValues.get(ii);
if (allowedAttribute(name, paramName)) {
if (inArray(paramName, vProtocolAtts)) {
paramValue = processParamProtocol(paramValue);
}
params += " " + paramName + "=\"" + paramValue + "\"";
}
}
if (inArray(name, vSelfClosingTags)) {
ending = " /";
}
if (inArray(name, vNeedClosingTags)) {
ending = "";
}
if (ending == null || ending.length() < 1) {
if (vTagCounts.containsKey(name)) {
vTagCounts.put(name, vTagCounts.get(name) + 1);
} else {
vTagCounts.put(name, 1);
}
} else {
ending = " /";
}
return "<" + name + params + ending + ">";
} else {
return "";
}
}
// comments
m = P_COMMENT.matcher(s);
if (!stripComment && m.find()) {
return "<" + m.group() + ">";
}
return "";
}
private String processParamProtocol(String s) {
s = decodeEntities(s);
final Matcher m = P_PROTOCOL.matcher(s);
if (m.find()) {
final String protocol = m.group(1);
if (!inArray(protocol, vAllowedProtocols)) {
// bad protocol, turn into local anchor link instead
s = "#" + s.substring(protocol.length() + 1, s.length());
if (s.startsWith("#//")) {
s = "#" + s.substring(3, s.length());
}
}
}
return s;
}
private String decodeEntities(String s) {
StringBuffer buf = new StringBuffer();
Matcher m = P_ENTITY.matcher(s);
while (m.find()) {
final String match = m.group(1);
final int decimal = Integer.decode(match).intValue();
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
buf = new StringBuffer();
m = P_ENTITY_UNICODE.matcher(s);
while (m.find()) {
final String match = m.group(1);
final int decimal = Integer.valueOf(match, 16).intValue();
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
buf = new StringBuffer();
m = P_ENCODE.matcher(s);
while (m.find()) {
final String match = m.group(1);
final int decimal = Integer.valueOf(match, 16).intValue();
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
s = validateEntities(s);
return s;
}
private String validateEntities(final String s) {
StringBuffer buf = new StringBuffer();
// validate entities throughout the string
Matcher m = P_VALID_ENTITIES.matcher(s);
while (m.find()) {
final String one = m.group(1); //([^&;]*)
final String two = m.group(2); //(?=(;|&|$))
m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
}
m.appendTail(buf);
return encodeQuotes(buf.toString());
}
private String encodeQuotes(final String s){
if(encodeQuotes){
StringBuffer buf = new StringBuffer();
Matcher m = P_VALID_QUOTES.matcher(s);
while (m.find()) {
final String one = m.group(1); //(>|^)
final String two = m.group(2); //([^<]+?)
final String three = m.group(3); //(<|$)
m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, """, two) + three));
}
m.appendTail(buf);
return buf.toString();
}else{
return s;
}
}
private String checkEntity(final String preamble, final String term) {
return ";".equals(term) && isValidEntity(preamble)
? '&' + preamble
: "&" + preamble;
}
private boolean isValidEntity(final String entity) {
return inArray(entity, vAllowedEntities);
}
private static boolean inArray(final String s, final String[] array) {
for (String item : array) {
if (item != null && item.equals(s)) {
return true;
}
}
return false;
}
private boolean allowed(final String name) {
return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
}
private boolean allowedAttribute(final String name, final String paramName) {
return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
}
}
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* XSS过滤
*
*/
public class XssFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
@Override
public void destroy() {
}
}
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
/**
* XSS过滤处理
*
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
private static final List<String> EXCLUSIVE_FIELDS = Arrays.asList("matter,remark".split(","));
//没被包装过的HttpServletRequest(特殊场景,需要自己过滤)
HttpServletRequest orgRequest;
//html过滤
private final static HTMLFilter htmlFilter = new HTMLFilter();
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
orgRequest = request;
}
@Override
public ServletInputStream getInputStream() throws IOException {
//非json类型,直接返回
if(!super.getHeader("Content-Type").equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)){
return super.getInputStream();
}
//为空,直接返回
String json = IOUtils.toString(super.getInputStream(), "utf-8");
if (StringUtils.isBlank(json)) {
return super.getInputStream();
}
//xss过滤
json = xssEncode(json);
final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes());
return new ServletInputStream() {
@Override
public boolean isFinished() {
return true;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bis.read();
}
};
}
@Override
public String getParameter(String name) {
String value = super.getParameter(xssEncode(name));
if (StringUtils.isNotBlank(value)) {
value = xssEncode(value);
}
return value;
}
@Override
public String[] getParameterValues(String name) {
String[] parameters = super.getParameterValues(name);
if (parameters == null || parameters.length == 0) {
return null;
}
/**
* 添加不过滤的字段值
*/
if (EXCLUSIVE_FIELDS.contains(name)) {
return parameters;
}
for (int i = 0; i < parameters.length; i++) {
parameters[i] = xssEncode(parameters[i]);
}
return parameters;
}
@Override
public Map<String,String[]> getParameterMap() {
Map<String,String[]> map = new LinkedHashMap<>();
Map<String,String[]> parameters = super.getParameterMap();
for (String key : parameters.keySet()) {
String[] values = parameters.get(key);
for (int i = 0; i < values.length; i++) {
values[i] = xssEncode(values[i]);
}
map.put(key, values);
}
return map;
}
@Override
public String getHeader(String name) {
String value = super.getHeader(xssEncode(name));
if (StringUtils.isNotBlank(value)) {
value = xssEncode(value);
}
return value;
}
private String xssEncode(String input) {
return htmlFilter.filter(input);
}
/**
* 获取最原始的request
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
/**
* 获取最原始的request
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
if (request instanceof XssHttpServletRequestWrapper) {
return ((XssHttpServletRequestWrapper) request).getOrgRequest();
}
return request;
}
}
P.S. Все три файла .java необходимы, а код можно использовать напрямую, гарантируя, что вам больше не придется беспокоиться об уязвимостях XSS✌
Другие уязвимости и их решения
SQL-инъекция и XSS-уязвимости, упомянутые выше, являются двумя наиболее распространенными и наиболее часто встречающимися уязвимостями, поэтому я расскажу о них подробнее, а затем кратко расскажу о других уязвимостях и их решениях~
LDAP-инъекция
Внедрение LDAP — это метод атаки, который использует уязвимость в проверке ввода для внедрения исполняемых запросов. LDAP — это упрощенный протокол доступа к каталогам, открытый межплатформенный протокол для аутентификации службы каталогов. LDAP — это язык общения, который приложения могут использовать для доступа к серверам каталогов. Эти серверы каталогов обычно хранят имена пользователей, пароли, сведения об учетной записи и другую информацию, которая может использоваться совместно с другими объектами в сети. Внедрение LDAP происходит, когда приложение вставляет необработанные входные данные непосредственно в оператор LDAP. В этом случае злоумышленник может использовать синтаксис фильтра LDAP, который позволяет серверу выполнять дополнительные запросы и операторы LDAP.
Самый простой способ предотвратить внедрение LDAP — обеспечить экранирование или отклонение специальных символов LDAP ( ) ! | & * во время аутентификации.
Уязвимость при загрузке файлов
Уязвимости загрузки файлов обычно вызваны плохой фильтрацией переменных пути загрузки файлов в коде веб-страницы.Если код реализации функции загрузки файлов не ограничивает строго суффикс файла или тип файла, загружаемого пользователями, злоумышленники могут загружать произвольные файлы через каталог, к которому обращается пользователь. в Интернете, включая файл бэкдора веб-сайта (веб-оболочку), а затем удаленно управлять сервером веб-сайта.
Поэтому в процессе разработки веб-сайтов и приложений необходимо строго ограничивать и проверять загружаемые файлы, а также запрещается загружать файлы с вредоносным кодом. В то же время разрешение на выполнение связанных каталогов ограничено для предотвращения атак через веб-шелл.
Незашифрованный запрос на вход
Из-за небезопасной веб-конфигурации запросы на вход в систему передают конфиденциальные поля, такие как имена пользователей и пароли, в незашифрованном виде, и злоумышленники могут прослушивать сеть, чтобы украсть эту конфиденциальную информацию. Следовательно, эти конфиденциальные параметры должны быть зашифрованы перед передачей.
DDoS-атака
Все должны быть знакомы с атакой DOS. Его полное имя - это отказ в обслуживании, который просто означает создание общественного сайта недоступного. DDOS Attack (распределенный отказ в обслуживании) - это обновленная версия DOS. Разница между ними не велика. Атака DOS заключается в том, что злоумышленник непрерывно делает запросы на обслуживание, чтобы законный запрос пользователя нельзя обрабатывать во времени, что делает веб-сайт недоступным; DDOS Attack - это то, что злоумышленник использует несколько компьютеров или компьютер кластеры для выполнения атаки DOS.
Наиболее прямым способом предотвращения атак DDOS является ограничение потока трафика (включая IP и т. Д.) одного пользователя.
XXE-уязвимости (редко)
Полное название уязвимости XXE — уязвимость XML External Entity.Когда приложение анализирует ввод XML, если загрузка внешних объектов не запрещена, могут быть загружены вредоносные внешние файлы и коды, что приведет к произвольному чтению файла, выполнению команды, порту интрасети. сканирование, атаки на внутренние веб-сайты и другие атаки. Эта уязвимость возникает только в интерфейсах, которые могут принимать параметры в формате XML. Решение также относительно простое — отключить внешние объекты.
P.S.Лазейки XXE относительно редки, и я мало что о них знаю, поэтому упомяну решение этой лазейки за один раз, прошу меня простить 😂 Если есть друзья, которые знают об этой лазейке, пожалуйста, оставьте сообщение в область комментариев.
Уязвимость слабого пароля
Эта уязвимость очень проста, потому что другие пароли легко догадаются другими (люди, которые хорошо вас хорошо знают и т. Д.) Или грубые усилия, растрескивающиеся путем взлома инструментов, поэтому легко поставить системное программное обеспечение в чрезвычайно нестабильное состояние. Безопасный государство. Решение этой уязвимости также очень простое, которое заключается в том, чтобы сделать ваш пароль достаточно сложным и скрытым, и в то же время ограничить количество попыток пароля.
Уязвимость отслеживания заголовков HTTP
Спецификация HTTP/1.1 (RFC2616) определяет метод HTTP TRACE, который в основном используется клиентом для проверки или получения диагностической информации путем отправки запроса TRACE на веб-сервер. Когда веб-сервер включает TRACE, отправленный заголовок запроса будет полностью возвращен в содержимом (теле) ответа сервера, где заголовок HTTP, вероятно, будет включать маркер сеанса, файлы cookie или другую информацию аутентификации, и злоумышленники могут использовать эту уязвимость для обманывать законных пользователей и получать их личную информацию. Нам просто нужно отключить метод HTTP TRACE для устранения этой уязвимости.
Бизнес-уязвимости, связанные с системой
Из-за недостатков в дизайне системы в некоторых бизнес-модулях системы будут лазейки, такие как повторные атаки (замаскированные платежи), контроль разрешений (операции с превышением охвата) и так далее. Единого способа устранения таких лазеек не существует, и мы можем только усердно работать над тем, чтобы проверять его по крупицам.
резюме
В итоге можно резюмировать одним предложением: абсолютно безопасной системы в мире не существует, люди будут ошибаться, а те, кого одобрят, еще и писать проблемные коды, так что нельзя игнорировать вопросы безопасности системы, и небольшой лазейка может привести к огромной катастрофе.
Мой опыт ограничен, и некоторые места могут быть не особо к месту.Если у вас возникнут какие-либо вопросы во время чтения, пожалуйста, оставьте сообщение в области комментариев, и мы обсудим их по одному в будущем🙇
Я надеюсь, что вы можете использовать свои милые маленькие руки, чтобы любить + следуйте (✿◡‿◡), чтобы больше друзей могли видеть эту статью ~ краб-краб YO (● '◡' ●)
Если в статье есть какая-либо ошибка, пожалуйста, оставьте сообщение, чтобы исправить ее; если у вас есть лучшее и более уникальное понимание, вы можете оставить свои ценные идеи в области сообщений.
Люби то, что любишь, делай то, что делаешь, слушай свое сердце, ничего не проси