All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.org.atool.fluent.mybatis.utility.SqlInject Maven / Gradle / Ivy

package cn.org.atool.fluent.mybatis.utility;

import java.util.*;
import java.util.stream.Stream;

/**
 * 简单判断有没有sql注入风险
 *
 * @author darui.wu 2019/10/10 10:17 上午
 */
@SuppressWarnings({"UnusedReturnValue"})
public class SqlInject {
    private static final String[] KEYWORDS = {"and", "or", "not", "use", "insert", "delete", "update", "select", "count",
        "group", "union", "create", "drop", "truncate", "alter", "grant", "execute", "exec",
        "xp_cmdshell", "call", "declare", "source", "sql"};
    /**
     * 可能产生注入的危险字符串
     */
    private static final List DANGER_CHAR = Arrays.asList(";", "--", "*", "%", "\\", "'", "#", "/");

    private static final Map> KEYWORD_MAP = initMap(KEYWORDS);

    private static final Map> DANGER_MAP = initMap(DANGER_CHAR.toArray(new String[0]));

    private static final String KEYWORD_STR = "[" + String.join(",", KEYWORDS) + "]";

    private static final String DANGER_STR = "[" + String.join(",", DANGER_CHAR) + "]";

    private static final int TO_UPPER = 'A' - 'a';

    private static Map> initMap(String... words) {
        Map> map = new HashMap<>(256);
        Stream.of(words).forEach(word -> {
            char[] chars = word.toCharArray();
            char first = Character.toLowerCase(chars[0]);
            if (!map.containsKey(first)) {
                map.put(first, new ArrayList<>());
                map.put(Character.toUpperCase(first), map.get(first));
            }
            map.get(first).add(Arrays.copyOfRange(chars, 1, chars.length));
        });
        return map;
    }

    /**
     * 预先判断字段 propertyName的值 propertyValue 不包含sql注入字符
     *
     * @param propertyName  属性名称
     * @param propertyValue 具体属性值
     * @return 返回无注入风险的 propertyValue
     */
    public static String assertNoInject(String propertyName, String propertyValue) {
        if (probablySqlInject(propertyValue)) {
            throw new RuntimeException(propertyName + " can't contain following KeyWord: " + KEYWORD_STR + " or following danger characters: " + DANGER_STR);
        }
        return propertyValue;
    }

    /**
     * 是否含有sql注入,返回true表示含有
     *
     * @param str sql
     * @return ignore
     */
    public static boolean probablySqlInject(String str) {
        if (str == null) {
            return false;
        }
        boolean isWordStart = true;
        int length = str.length();
        int index = 0;
        for (char ch : str.toCharArray()) {
            index++;
            if (isBlank(ch)) {
                isWordStart = true;
                continue;
            }
            if (isWordStart && isKeyWord(str, length, KEYWORD_MAP.get(ch), index, false)) {
                return true;
            }
            if (isKeyWord(str, length, DANGER_MAP.get(ch), index, true)) {
                return true;
            }
            isWordStart = false;
        }
        return false;
    }

    private static boolean isKeyWord(String str, int length, List charsList, int index, boolean isDanger) {
        if (charsList == null) {
            return false;
        }
        for (char[] chars : charsList) {
            if (isKeyWord(str, length, chars, index, isDanger)) {
                return true;
            }
        }
        return false;
    }

    private static boolean isKeyWord(String str, int length, char[] chars, int index, boolean isDanger) {
        int start = index;
        for (char ch : chars) {
            if (start >= length) {
                return false;
            }
            char charAt = str.charAt(start);
            if (ch != charAt && ch + TO_UPPER != charAt) {
                return false;
            }
            start++;
        }
        return isDanger || start == length || isBlank(str.charAt(start));
    }

    /**
     * 是否是空格符
     *
     * @param ch char
     * @return ignore
     */
    private static boolean isBlank(char ch) {
        return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == 0;
    }

    /**
     * 简单判断是否可能存在SQL注入危险
     * 不允许可能产生注入的字符串
     *
     * @param str sql
     * @return ignore
     */
    public static boolean hasSimpleInject(String str) {
        if (str == null) {
            return false;
        }
        int length = str.length();
        int index = 0;
        for (char ch : str.toCharArray()) {
            index++;
            if (isKeyWord(str, length, DANGER_MAP.get(ch), index, true)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 简单判断 propertyName的值 propertyValue 不包含sql危险注入字符
     * ";", "--", "*", "%", "\\", "'", "#", "/"
     *
     * @param propertyName  属性名称
     * @param propertyValue 具体属性值
     * @return 返回无注入风险的 propertyValue
     */
    public static String assertSimpleNoInject(String propertyName, String propertyValue) {
        if (hasSimpleInject(propertyValue)) {
            throw new RuntimeException(propertyName + " can't contain following string: " + DANGER_STR);
        }
        return propertyValue;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy