当前位置:   article > 正文

防止后台SQL注入并解析各种json类型_.net json 防注入

.net json 防注入

前两天做了一个关于后台的防止SQL注入的操作。因为项目数据层全部编码完成,现在再来大动干戈修改数据层,有点繁琐耗时。所以就添加了一个过滤器来拦截前台传递到后台的参数信息,在数据进入控制层之前先拦截信息进行检查,如果含有SQL注入的关键字,则直接返回前台。所以需要针对前台传递的各种json字符串和json数组进行解析。
因为在过滤器里面先获取了request里面的payload的信息,而在request里面的payload信息是以流的形式存储的,所以读取了,后面控制层将无法获取,那么就需要在进行json解析之前,先处理一下request里面的信息。
将request读取之后,再将流读取位置置为开始,那么其他的request就能继续读取了。具体操作如下:

public class RequestWrapper extends HttpServletRequestWrapper {
	private final String body;

	public RequestWrapper(HttpServletRequest request) {
		super(request);
		StringBuilder stringBuilder = new StringBuilder();
		BufferedReader bufferedReader = null;
		InputStream inputStream = null;
		try {
			inputStream = request.getInputStream();
			if (inputStream != null) {
				bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
				char[] charBuffer = new char[128];
				int bytesRead = -1;
				while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
					stringBuilder.append(charBuffer, 0, bytesRead);
				}
			} else {
				stringBuilder.append("");
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (bufferedReader != null) {
				try {
					bufferedReader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		body = stringBuilder.toString();
	}

	@Override
	public ServletInputStream getInputStream() throws IOException {
		final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
		return new ServletInputStream() {
			public boolean isFinished() {
				return false;
			}

			public boolean isReady() {
				return false;
			}

			public void setReadListener(ReadListener readListener) {
			}

			@Override
			public int read() throws IOException {
				return bais.read();
			}
		};
	}

	@Override
	public BufferedReader getReader() throws IOException {
		return new BufferedReader(new InputStreamReader(this.getInputStream()));
	}

	public String getBody() {
		return this.body;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

下面继续解决SQL注入的问题,这里选择了两种json解析方式.

1.使用net.sf.json

我开始解析json用了net.sf.json的包。导入依赖如下。

		<dependency>
			<groupId>net.sf.json-lib</groupId>
			<artifactId>json-lib</artifactId>
			<version>2.4</version>
			<classifier>jdk15</classifier>
		</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

下面首先是获取前台请求的URL中采用get方式传递过来的是否含有SQL注入的关键字。然后再判断request的payload中的参数值是否含有关键字。解析body里面的json,采用了递归的方式,判断body是否实例化了jsonObject或jsonArray来区分json字符串、json数组、数组等。具体如下代码:

import net.sf.json.JSONArray;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import net.sf.json.util.JSONTokener;

@Component
public class SQLValidateFilter implements Filter {

	private static final Logger logger = LoggerFactory.getLogger(SQLValidateFilter.class);
	@Value("${spring.profiles.active}")
	private String activeProfile;

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		List<String> listValues = new ArrayList<String>();
		RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest) request);
		HttpServletResponse resp = (HttpServletResponse) response;
		listValues = getParametersBehindUrl(requestWrapper, listValues);
		String body = requestWrapper.getBody();
		if (body != null && !StringUtils.EMPTY.equals(body)) {
			listValues = getBodyValue(listValues, body);
		}
		getValidate(resp, chain, requestWrapper, listValues);

	}

	/**
	 * 验证request里面是否含有非法字符
	 * @param response
	 * @param filterChain
	 * @param requestWrapper
	 * @param listValues
	 * @throws IOException
	 * @throws ServletException
	 */
	private void getValidate(HttpServletResponse response, FilterChain filterChain, RequestWrapper requestWrapper,
			List<String> listValues) throws IOException, ServletException {
		if (sqlValidate(listValues)) {
			response.setStatus(417);
			if ("test".equals(activeProfile)) {
				response.setContentType("text/html;charset=GBK");
			} else {
				response.setContentType("text/html;charset=UTF-8");
			}
			response.getOutputStream().write("您发送请求中的参数中含有非法字符".getBytes());
			return;
		} else {
			filterChain.doFilter(requestWrapper, response);
		}
	}
	
	
	/**
	 * 获取URL后面的值
	 * @param req
	 * @param listValues
	 * @return
	 */
	private List<String> getParametersBehindUrl(ServletRequest req, List<String> listValues) {
		Enumeration<?> params = req.getParameterNames();
		while (params.hasMoreElements()) {
			String name = params.nextElement().toString();
			String[] value = req.getParameterValues(name);
			for (int i = 0; i < value.length; i++) {
				listValues.add(value[i]);
			}
		}

		return listValues;
	}

	/**
	 * 获取body里面的参数值
	 * @param listValues
	 * @param body
	 * @return
	 */
	private List<String> getBodyValue(List<String> listValues, String body) {
		JSONTokener token = new JSONTokener(body);
		try {
			while (token.more()) {
				Object value = token.nextValue();
				if (value instanceof JSONObject) {
					listValues.addAll(parseJsonObject((JSONObject) value));
				} else if (value instanceof JSONArray) {
					listValues.addAll(parseJsonArray((JSONArray) value));
				} else {
					listValues.add(value.toString());
				}
			}
		} catch (JSONException e) {
			logger.error(e.getMessage(), e);
		}
		return listValues;
	}

	/**
	 * 解析 Json 对象,获取值列表
	 * 
	 * @param object
	 * @return
	 * @throws JSONException
	 */
	private List<String> parseJsonObject(JSONObject object) throws JSONException {
		List<String> results = new ArrayList<String>();
		Iterator<?> keys = object.keys();
		while (keys.hasNext()) {
			String key = (String) keys.next();
			Object value = object.get(key);
			if (value instanceof JSONObject) {
				results.addAll(parseJsonObject((JSONObject) value));
			} else if (value instanceof JSONArray) {
				results.addAll(parseJsonArray((JSONArray) value));
			} else {
				results.add(value.toString());
			}
		}
		return results;
	}

	/**
	 * 解析 Json 数组,获取值列表
	 * 
	 * @param array
	 * @return
	 * @throws JSONException
	 */
	private List<String> parseJsonArray(JSONArray array) throws JSONException {
		List<String> results = new ArrayList<String>();
		for (int index = 0; index < array.size(); index++) {
			Object value = array.get(index);
			if (value instanceof JSONObject) {
				results.addAll(parseJsonObject((JSONObject) value));
			} else if (value instanceof JSONArray) {
				results.addAll(parseJsonArray((JSONArray) value));
			} else {
				results.add(value.toString());
			}
		}
		return results;
	}
	
	// 校验
		protected static boolean sqlValidate(List<String> listValues) {
			String dataValues = listValues.toString().toLowerCase(Locale.ENGLISH);// 统一转为小写
			String badStr = "'|and|exec|execute|insert|select|delete|count|drop|*|chr|mid|master|truncate|"
					+ "char|declare|net user|xp_cmdshell|;|+|like'|and|exec|execute|insert|create"
					+ "table|from|grant|use|group_concat|column_name|"
					+ "information_schema.columns|table_schema|union|where|order|by|*|//|--|#|";
			String[] badStrs = badStr.split("\\|");
			for (int i = 0; i < badStrs.length; i++) {
				if (dataValues.contains(badStrs[i])) {
					logger.info("dataValues=" + dataValues + " marchValue=" + badStrs[i]);
					return true;
				}
			}
			return false;
		}
	
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169

2.直接使用jackson

因为后台与前台的数据交互,封装的json本身就是采用jackson的,所以后面对上的代码进行了优化。直接使用jackson实现对json数据的处理。添加依赖如下:

	<dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
 		<artifactId>jackson-databind</artifactId>
 		<version>2.8.10</version>
 	</dependency>
 	<dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
  		<artifactId>jackson-core</artifactId>
  		<version>2.8.10</version>
  	<dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

下面直接使用jackson的属性来获取


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class SQLValidateFilter implements Filter {

	private static final Logger logger = LoggerFactory.getLogger(SQLValidateFilter.class);
	@Value("${spring.profiles.active}")
	private String activeProfile;

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		RequestWrapper wrapper = new RequestWrapper((HttpServletRequest) request);
		HttpServletResponse resp = (HttpServletResponse) response;
		if (existsInvalidSqlTokenInRequest(wrapper)) {
			resp.setStatus(417);
			String contentType = "test".equals(activeProfile) ? "text/html;charset=GBK" : "text/html;charset=UTF-8";
			response.setContentType(contentType);
			response.getOutputStream().write("您发送请求中的参数中含有非法字符".getBytes());
			return;
		}
		
		chain.doFilter(wrapper, resp); 

	}
	/**
	 * 判断请求中是否有非法 SQL 关键字
	 * 
	 * @param request
	 * @return
	 * @throws IOException 
	 * @throws JsonProcessingException 
	 */
	private boolean existsInvalidSqlTokenInRequest(RequestWrapper request) throws JsonProcessingException, IOException {
		for (String value : getParameterValuesBehindUrl(request)) {
			if (findInvalidSqlToken(value)) {
				return true;
			}
		}
		for (String value : getParameterValuesInBody(request)) {
			if (findInvalidSqlToken(value)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 从 URL 中提取参数值
	 * 
	 * @param request
	 * @return
	 */
	private List<String> getParameterValuesBehindUrl(RequestWrapper request) {
		List<String> results = new ArrayList<String>();
		Enumeration<String> params = request.getParameterNames();
		while (params.hasMoreElements()) {
			String name = params.nextElement().toString();
			String[] values = request.getParameterValues(name);
			for (String value : values) {
				results.add(value);
			}
		}
		return results;
	}

	/**
	 * 从报文体中提取参数值
	 * 
	 * @param request
	 * @return
	 * @throws JsonProcessingException
	 * @throws IOException
	 */
	private List<String> getParameterValuesInBody(RequestWrapper request)
			throws JsonProcessingException, IOException {
		List<String> results = new ArrayList<String>();
		String body = request.getBody();
		if (StringUtils.isNotBlank(body)) {
			ObjectMapper mapper = new ObjectMapper();
			JsonNode node = mapper.readTree(body);
			results.addAll(parseJsonNode(node));
		}
		return results;
	}
	
	/**
	 * 从 JSON 节点中提取参数值
	 * 
	 * @param node
	 * @return
	 */
	private List<String> parseJsonNode(JsonNode node) {
		List<String> results = new ArrayList<String>();
		switch (node.getNodeType()) {
		case ARRAY:
			for (int index = 0; index < node.size(); index++) {
				results.addAll(parseJsonNode(node.get(index)));
			}
			break;
		case OBJECT:
			Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
			while (fields.hasNext()) {
				results.addAll(parseJsonNode(fields.next().getValue()));
			}
			break;
		default:
			results.add(node.toString());
			break;
		}
		return results;
	}

	/**
	 * 从字符串中查找 SQL 关键字
	 * 
	 * @param value
	 * @return
	 */
	private boolean findInvalidSqlToken(String value) {
		String lowerCaseValue = value.toLowerCase(Locale.ENGLISH);
		String sqlTokens = "'|and|exec|execute|insert|select|delete|count|drop|*|chr|mid|master|truncate|"
				+ "char|declare|net user|xp_cmdshell|;|+|like'|and|exec|execute|insert|create"
				+ "table|from|grant|use|group_concat|column_name|"
				+ "information_schema.columns|table_schema|union|where|order|by|*|//|--|#|";
		for (String token : sqlTokens.split("\\|")) {
			if (lowerCaseValue.contains(token)) {
				logger.info("dataValue=" + lowerCaseValue + ", marchValue=" + token);
				return true;
			}
		}
		return false;
	}

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		
	}
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149

到此,项目的SQL注入问题解决。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/269644
推荐阅读