preface

I wonder if you’ve seen Barron Beau O ‘Dahl’s whodunnit who AM I? There is no absolutely safe system. The hero of the movie and his friends formed the hacker organization CLAY (Clawn Laghing At You means clown’s ridicule), and invaded the international security system, international financial system, international Financial assessment system, German security Agency, German intelligence Agency successively. As a programmer, WHEN I saw the protagonist in the movie using system loopholes to invade, I could not help thinking that the system software I participated in the development must have more or less loopholes (it is estimated that these software can not last three minutes in the hands of others ๐Ÿ˜‚). We can’t guarantee that the system has no bugs, nor can we guarantee that the system is absolutely safe. What we can do is to find and repair bugs as much as possible, so that our system can be as safe as possible. So today we summarize the common vulnerabilities and corresponding solutions ๐Ÿ’ช.

P.S. Although it’s just a movie and the plot will be made up, I still said this after watching the movie: these people are ๐Ÿ‚ (there’s no other way to describe it)! This is really a movie worth seeing twice

Common security vulnerabilities and solutions

SQL injection

What is SQL injection

SQL injection is one of the most common network attacks. It is not an attack based on the bugs of the operating system, but an attack based on the carelessness of programmers when they write SQL statements to invade, log in without an account, or even tamper with the database.

Let’s take an example: for example, now there is a login function, we in the code of the user submitted in the parameters (account, password, etc.) for splicing, finally splicing into a complete SQL statement for user identity verification ๐Ÿ‘‡

String sql = “select * from user where username=’ “+userName+” ‘ and password=’ “+password+” ‘”;

If someone enters an invalid parameter to commit, the above statement might look like ๐Ÿ‘‡

select * from user where username=’xxx’ or 1 = 1 – – and password=’ ‘

Select * from user where username=’ XXX ‘or 1=1 where username=’ XXX’ or 1=1 Let’s look back, there are two small lines in front of the keyword and, this line must be familiar. In the SQL statement, the two small lines represent the comment, which invalidates the password verification after and. This means that the above statement is a guaranteed success statement, which accomplishes the purpose of SQL injection. This is still a gentle SQL injection, but what if the SQL statement looks like this? ๐Ÿ‘‡

select * from user where username=’xxx’ ; DROP DATABASE (DB Name); – – and password=’ ‘

If this SQL is executed successfully in the database, the consequences will be unimaginable…

SQL injection solution

Whoever has SQL injection vulnerabilities, all because of the program to accept from the client user input variables, or URL parameter, and the variable or parameter is part of the SQL statement, or transfer the contents of this user input parameters, we should be vigilant, we will strictly follow the principle of external data not to be trusted, Looking at the various attacks in the field of Web security, most of them are caused by developers violating this principle, so it is natural to start from the detection, filtering, verification of parameters, to ensure that the parameters are the security parameters that the developer intended.

Select * from SQL where id={$id}; select * from SQL where id={$id}; If you accept a mobile phone number parameter, you should check and make sure that the parameter is in the format of a mobile phone number, as well as any other type of parameter. As long as there are fixed format parameters (number, mobile phone number, email, etc.), before SQL statement execution, we should strictly check the fixed format to ensure that the variable is the format we expect, which can largely prevent SQL injection attacks. We started with an example of verifying user names and passwords. If we had a user name rule in the product design phase, for example, it stipulated that the length of the user name must be 5-15 characters, and the user name can only be composed of upper and lower case letters, numbers, and some security symbols, and no special characters. In this case, we should write a check_username method, which is used to uniformly check the received username parameters. However, in the case that article publishing system and comment system must allow users to submit arbitrary strings, the above verification mode is not appropriate, and other schemes such as filters are needed.

P.S. validation of fixed format parameters can be done using the Hutool library, Make your code “sweet” ~ never contact Hutool friend can teach you A Java reference great wisdom | Hutool – A set of tools that keep Java sweet

โ‘ก Filter special symbols: this sentence is very well understood, for some parameters without a fixed format, we need to filter its special characters, forbid users to pass unsafe special symbols.

The so-called precompiled statement is to replace variable values in SQL Statements with placeholders, which can be said to template or parameterize SQL Statements. Such Statements are generally called Prepared Statements or Parameterized Statements. Using pre-compiled statements for bound variables is the best way to prevent SQL injection. The semantics of using pre-compiled SQL statements do not change. No matter how skilled a hacker is at this point, passing in SQL comment symbols (i.e., the two horizontal lines mentioned above) cannot change the structure of the entire SQL statement.

(4) If you use Mybatis, then in writing the mapping statement, try to use #{XXX} format: in Mybatis, XXX format parameters will directly participate in SQL compilation, so that can not avoid injection attacks. However, when it comes to dynamic table names and column names, only parameters in the format {XXX} will directly participate in SQL compilation, thus avoiding injection attacks. However, when it comes to dynamic table and column names, only parameters in the format XXX will directly participate in SQL compilation, thus avoiding injection attacks. However, when it comes to dynamic table and column names, only {XXX} can be used. Therefore, such parameters need to be handled manually in the code to prevent SQL injection.

Cross-site scripting vulnerability

What is cross-site scripting vulnerability

Cross-site scripting (commonly referred to as XSS) occurs on the client side and can be used for privacy theft, phishing, password theft, spreading malicious code, and more. XSS attacks mainly use HTML and Javascript, but also VBScript and ActionScript. Although XSS attack has no direct harm to the WEB server, it spreads through the website, so that the users of the website are attacked, leading to the theft of the user account, and thus causing serious harm to the website. To put it plainly, AN XSS attack is to insert malicious script code into a Web page. When the user browses the page, the embedded malicious script code will be executed, so as to attack the user. XSS attacks fall into the following three categories ๐Ÿ‘‡

  1. Reflective XSS< non-persistent > : Attackers make attack links in advance and need to trick users into clicking the links to trigger XSS code (there is no such page and content in the server), which is easy to appear in search pages or spam messages. (Unknown links do not randomly click oh, very dangerous ๐Ÿˆฒ)
  2. Stored XSS< persistence > : The XSS attacks is the code stored in the server, such as hackers in places such as modify personal information or post upload malicious code, at this time if there is no filter or lax, then the code will be stored in the server, every time a user access the page when will trigger code execution, this kind of XSS attacks is very dangerous, Easy to cause worms or a lot of Cookie theft.
  3. DOM XSS: it is a cross-site vulnerability that occurs in the client DOM (Document Object Model). The main reason is the security problem caused by the client script processing logic.

Solution to cross-site scripting vulnerability

As with the advice of SQL injection protection, we still need to strictly follow the principle that external data cannot be trusted. We must strictly check script, iframe and other words in all input. The input here is not only an input interface that users can interact with directly, but also includes variables in cookies in HTTP requests, variables in HTTP request headers, and so on. Here it is recommended to use the filter to verify the parameters, after all, there are so many places to check the verification, the total can not write a ~ directly on the code ๐Ÿ‘‡

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. "" becomes " text "). If set to false, * unbalanced angle brackets will be html escaped. */
    private final boolean alwaysMakeTags;
 
    /** Default constructor. * */
    public HTMLFilter(a) {
        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(a); 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(a) {
        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, "&amp;", result);
        result = regexReplace(P_QUOTE, "&quot;", result);
        result = regexReplace(P_LEFT_ARROW, "&lt;", result);
        result = regexReplace(P_RIGHT_ARROW, "&gt;", 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(a){
        return alwaysMakeTags;
    }
 
    public boolean isStripComments(a){
        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, "< The $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, "&quot;", two) + three));
            }
            m.appendTail(buf);
            return buf.toString();
        }else{
            returns; }}private String checkEntity(final String preamble, final String term) {
 
        return ";".equals(term) && isValidEntity(preamble)
                ? '&' + preamble
                : "&amp;" + 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) {
        returnallowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); }}Copy the code
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
 
/** * XSS filter ** /
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(a) {}}Copy the code
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 filter ** /
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
	
	private static final List<String> EXCLUSIVE_FIELDS = Arrays.asList("matter,remark".split(","));
	
    // Unwrapped HttpServletRequest (special case, need to filter yourself)
    HttpServletRequest orgRequest;
    / / HTML filter
    private final static HTMLFilter htmlFilter = new HTMLFilter();
 
    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        orgRequest = request;
    }
 
    @Override
    public ServletInputStream getInputStream(a) throws IOException {
        // It is a non-JSON type
        if(!super.getHeader("Content-Type").equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)){
            return super.getInputStream();
        }
 
        // Is empty and returns directly
        String json = IOUtils.toString(super.getInputStream(), "utf-8");
        if (StringUtils.isBlank(json)) {
            return super.getInputStream();
        }
 
        / / XSS filtering
        json = xssEncode(json);
        final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes());
        return new ServletInputStream() {
            @Override
            public boolean isFinished(a) {
                return true;
            }
 
            @Override
            public boolean isReady(a) {
                return true;
            }
 
            @Override
            public void setReadListener(ReadListener readListener) {}@Override
            public int read(a) throws IOException {
                returnbis.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;
        }
        
        /** * Adds an unfiltered field value */
        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);
    }
 
    /** * get the original request */
    public HttpServletRequest getOrgRequest(a) {
        return orgRequest;
    }
 
    /** * get the original request */
    public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
        if (request instanceof XssHttpServletRequestWrapper) {
            return ((XssHttpServletRequestWrapper) request).getOrgRequest();
        }
 
        returnrequest; }}Copy the code

P.S. three.java files indispensable, code can be used directly, guaranteed to avoid โœŒ with XSS bugs

Other vulnerabilities and their solutions

SQL injection and XSS vulnerabilities mentioned above are two of the most common and most encountered vulnerabilities, so there are more to say. Next, I will briefly talk to you about other vulnerabilities and their solutions

LDAP injection

LDAP injection is an attack that exploits vulnerabilities in input validation to inject executable queries. LDAP is a lightweight directory access protocol that is open and cross-platform for directory service authentication. LDAP is a communication language that applications can use to access directory servers. These directory servers typically store usernames, passwords, account details, and other information that can be shared with other entities on the network. LDAP injection occurs when an application inserts raw input directly into an LDAP statement. In this case, an attacker can use the LDAP filter syntax, which enables the server to execute additional queries and LDAP statements.

The easiest way to prevent LDAP injection is to ensure that the LDAP special character ()! | & * escaped or reject in the validation.

File upload vulnerability

File upload holes usually because the file upload path variable filter is lax in Web page code, if the file upload function implementation code not strictly limit users to upload file suffix or file type, the attacker can be accessed through a Web directory to upload any file, including website back door file (webshell), and remote control of the Web server.

Therefore, strictly restrict and verify uploaded files during website and application development, and do not upload files containing malicious codes. In addition, the execution permissions of related directories are restricted to defend against WebShell attacks.

Unencrypted login request

Because the Web configuration is not secure, login requests transmit sensitive fields, such as user names and passwords, unencrypted. Attackers can eavesdrop on the network to rob sensitive information. Therefore, it is necessary to encrypt these sensitive parameters before transmission.

DDoS attack

Denial of Service, or DoS attack, is a familiar form of Denial of Service that simply blocks access to a public website, Distributed Denial of Service (DDoS) attacks are an upgrade of DoS. The difference between the two is not big, DoS attack is the attacker constantly put forward service requests, so that legitimate users can not timely processing requests, so that the website can not access; In a DDoS attack, an attacker uses multiple computers or a cluster of computers to carry out DoS attacks.

The most direct way to prevent DDoS attacks is to limit the traffic (including IP addresses) of a single user.

XXE Vulnerability (less common)

The full name of XXE vulnerability is XML External Entity vulnerability. When an application parses XML input, malicious External files and codes can be loaded if the loading of External entities is not prohibited, resulting in arbitrary file reading, command execution, Intranet port scanning, and attacks on Intranet websites. This vulnerability only occurs on interfaces that can accept parameters in XML format. The solution is straightforward: disable external entities.

P.S. XXE vulnerability is relatively rare, and I am not very familiar with it, so the solution to this vulnerability will be briefly mentioned, you forgive me, ha ๐Ÿ˜‚ if you know about this vulnerability, feel free to leave a comment in the comments section.

Weak cryptographic vulnerability

Weak passwords are easy to guess by others (people who know you well, etc.) or brute force by a decryption tool, leaving your system software in an extremely insecure state. The solution to this vulnerability is simple: make your password complex and cryptic enough, and limit the number of attempts.

HTTP headers trace vulnerabilities

The HTTP/1.1 (RFC2616) specification defines the HTTP TRACE method, which is mainly used by clients to submit TRACE requests to the Web server for testing or obtaining diagnostic information. When TRACE is enabled on the Web server, the submitted request header is returned in its entirety in the Body of the server response, where the HTTP header is likely to contain Session tokens, Cookies, or other authentication information. An attacker could then use the flaw to trick legitimate users and gain access to their private information. We can resolve this vulnerability simply by disabling the HTTP TRACE method.

System-related service vulnerabilities

Due to the flaws in the design of the system, some business modules in the system are vulnerable, such as replay attack (disguised payment), permission control (unauthorized operation) and so on. There is no unified way to solve this kind of loophole, can only be a little bit of hard small partners investigation ๐Ÿ˜‚.

summary

Finally summed up is a word: there is no absolutely safe system in the world, people will make mistakes, and then ๐Ÿ‚ batch of people will write the code there are problems, so the system security problem can not be ignored, a small vulnerability may cause a huge disaster.

My experience is limited, some places may not be particularly in place, if you think of any questions when reading, welcome to leave a message in the comments section, we will discuss one by one ๐Ÿ™‡

Please take a thumbs up and a follow (โœฟโ—กโ€ฟโ—ก) for this article ~ a crab (โ—’โ—ก’โ—)

If there are mistakes in the article, welcome to comment correction; If you have a better, more unique understanding, you are welcome to leave your valuable ideas in the comments area.

Love what you love, do what you do, listen to your heart and ask nothing