防止XSS注入的方法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了防止XSS注入的方法相关的知识,希望对你有一定的参考价值。
参考技术A 关于防止XSS注入:1.尽量使用框架。通过对自己的网页进行xss保留型攻击的测试,尽管自己没有对某些输入框进入转义,但还是无法进行xss注入,因为框架会自动将某些特殊字符转义。(我使用的spring+struts+hibernate框架)。
2.如果不使用框架,一定要进行字符转义。对< 、 > 、" 、 等字符进行转义成为: < 、 > 、 " 等。
3.对输入长度进行限制。因为一般在进行xss攻击的时候,注入的字符串长度会非常长,例如 <script>alert("hello");</script> 编译成十六进制时,会变成 \3C\73\63\72\69\70\74\3E\61\6C\65\72\74\28\22\68\65\6C\6C\6F\22\29\3B\3C\2F\73\63\72\69\70\74\3E ,可见,尽管是一个非常短的alert语句,长度也会很长,所以对输入长度进行限制会非常有效地过滤掉大部分的xss攻击。
springboot安全组件总结
目录
防SQL注入组件
什么是sql注入
sql注入解释:是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中(例如,为了转储数据库内容给攻击者)攻击者在界面的表单信息或URL上输入一些奇怪的SQL片段(例如“or ‘1’=’1’”这样的语句),有可能入侵参数检验不足的应用程序。所以,在应用中需要做一些工作,来防备这样的攻击方式。在一些安全性要求很高的应用中(比如征信一代查询),经常使用将SQL语句全部替换为存储过程这样的方式,来防止SQL注入。这当然是一种很安全的方式,但在平时开发中,可能不需要这种死板的方式。
PreparedStatement防止SQL注入
PreparedStatement对象防止sql注入的方式是把用户非法输入的单引号转化为双单引号,从而达到了防止sql注入的目的。
以测试表financialec表为例,如下图:
Mysql5.0以上版本使用com.mysql.cj.jdbc.Driver驱动类创建连接,不再使用com.mysql.jdbc.Driver驱动类创建连接。如下图创建测试类:
最终执行的sql语句打印出来是:
select * from financialec where queryorgname='123'' or ''8''=''8' |
如下图所示:
由此可见,prepareStatement对象防止sql注入的方式是把用户非法输入的单引号转化为双单引号,从而达到了防止sql注入的目的。
mybatis防止SQL注入
MyBatis框架作为一款半自动化的持久层框架,其SQL语句有时需要手动编写,这个时候需要防止SQL注入。其实,MyBatis的SQL是一个具有“输入+输出”的功能,类似于函数的结构。
首先看一下下面两个sql语句的区别:
例1: <delete id="deleteFinancialecById" parameterType="String"> delete from financialec where id = #id </delete> |
例2: <delete id="deleteFinancialecById" parameterType="String"> delete from financialec where id = $id </delete> |
其中,parameterType表示了输入的参数类型。
如果我们想防止SQL注入,理所当然地要在输入参数上下功夫。上面代码中分别使用#、$即输入参数在SQL中拼接的部分,传入参数后,打印出执行的SQL语句,会看到SQL是这样的:
delete from financialec where id = ? |
delete from financialec where id = 111 |
通过测试,不管输入什么参数,使用#时打印出的SQL都是一样的。这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题。
mybatis中的#和$的区别:
- #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号
如:where id=#id,如果传入的值是111,那么解析成sql时的值为where id="111"
- $将传入的数据直接显示生成在sql中
如:where id=$id,如果传入的值是111,那么解析成sql时的值为where id=111;
如果传入的值是;drop table financialec,则解析成的sql为:delete from financialec where id=;drop table financialec;
- #方式能够很大程度防止sql注入,$方式无法防止Sql注入
- $方式一般用于传入数据库对象,例如传入表名
- 一般能用#的就别用$,若不得不使用“$xxx”这样的参数,要手工地做好过滤工作,来防止sql注入攻击
- 在MyBatis中,“$xxx”这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“$xxx”这样的参数格式。所以,这样的参数需要在代码中手工进行处理来防止注入
- #是经过预编译的,相当于JDBC中的PreparedStatement,是安全的;$是未
经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入的风险。
综合上述分析,在编写MyBatis的映射语句时,尽量采用“#xxx”这样的格式。若不得不使用“$xxx”这样的参数,要手工地做好过滤工作,来防止SQL注入攻击。
防XSS攻击组件
XSS过滤处理
public class XssHttpServletRequestWrapper extends
HttpServletRequestWrapper
/**
* @param request
*/
public XssHttpServletRequestWrapper(HttpServletRequest request)
super(request);
@Override
public String[] getParameterValues(String name)
String[] values = super.getParameterValues(name);
if (values != null)
int length = values.length;
String[] escapseValues = new String[length];
for (int i = 0; i < length; i++)
// 防xss攻击和过滤前后空格
escapseValues[i] = EscapeUtil.clean(values[i]).trim();
return escapseValues;
return super.getParameterValues(name);
@Override
public ServletInputStream getInputStream() throws IOException
// 非json类型,直接返回
if (!isJsonRequest())
return super.getInputStream();
// 为空,直接返回
String json = IOUtils.toString(super.getInputStream(), "utf-8");
if (StringUtils.isEmpty(json))
return super.getInputStream();
// xss过滤
json = EscapeUtil.clean(json).trim();
final ByteArrayInputStream bis = new ByteArrayInputStream(
json.getBytes("utf-8"));
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();
;
/**
* 是否是Json请求
*
* @param request
*/
public boolean isJsonRequest()
String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
return MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(header)
|| MediaType.APPLICATION_JSON_UTF8_VALUE
.equalsIgnoreCase(header);
防止XSS攻击的过滤器
public class XssFilter implements Filter
/**
* 排除链接
*/
public List<String> excludes = new ArrayList<>();
/**
* xss过滤开关
*/
public boolean enabled = false;
@Override
public void init(FilterConfig filterConfig) throws ServletException
String tempExcludes = filterConfig.getInitParameter("excludes");
String tempEnabled = filterConfig.getInitParameter("enabled");
if (StringUtils.isNotEmpty(tempExcludes))
String[] url = tempExcludes.split(",");
for (int i = 0; url != null && i < url.length; i++)
excludes.add(url[i]);
if (StringUtils.isNotEmpty(tempEnabled))
enabled = Boolean.valueOf(tempEnabled);
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (handleExcludeURL(req, resp))
chain.doFilter(request, response);
return;
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
(HttpServletRequest) request);
chain.doFilter(xssRequest, response);
private boolean handleExcludeURL(HttpServletRequest request,
HttpServletResponse response)
if (!enabled)
return true;
if (excludes == null || excludes.isEmpty())
return false;
String url = request.getServletPath();
for (String pattern : excludes)
Pattern p = Pattern.compile("^" + pattern);
Matcher m = p.matcher(url);
if (m.find())
return true;
return false;
@Override
public void destroy()
过滤器配置
@Configuration
public class FilterConfig
@Value("$xss.enabled")
private String enabled;
@Value("$xss.excludes")
private String excludes;
@Value("$xss.urlPatterns")
private String urlPatterns;
@SuppressWarnings( "rawtypes", "unchecked" )
@Bean
public FilterRegistrationBean xssFilterRegistration()
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter());
registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
registration.setName("xssFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
Map<String, String> initParameters = new HashMap<String, String>();
// excludes用于配置不需要参数过滤的请求url
initParameters.put("excludes", excludes);
initParameters.put("enabled", enabled);
registration.setInitParameters(initParameters);
return registration;
@SuppressWarnings( "rawtypes", "unchecked" )
@Bean
public FilterRegistrationBean someFilterRegistration()
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new RepeatableFilter());
registration.addUrlPatterns("/*");
registration.setName("repeatableFilter");
registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
return registration;
防重复提交组件
自定义注解防止表单重复提交
package org.jeecg.common.RepeatSubmit;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解防止表单重复提交
*/
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit
/**
* 间隔时间(ms),小于此时间视为重复提交
*/
public int interval() default 5000;
/**
* 提示消息
*/
public String message() default "不允许重复提交,请稍候再试";
注解参数说明:
参数 | 类型 | 默认值 | 描述 |
interval | int | 5000 | 间隔时间(ms),小于此时间视为重复提交 |
message | String | 不允许重复提交,请稍后再试 | 提示消息 |
自定义防止重复提交拦截器
package org.jeecg.common.RepeatSubmit.interceptor;
import com.alibaba.fastjson.JSONObject;
import org.jeecg.common.RepeatSubmit.RepeatSubmit;
import org.jeecg.common.api.vo.Result;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* @version V1.0
* @Description:防止重复提交拦截器
* @author: qixiongfei
* @date: 2022/2/28 10:19
*/
@Component
public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception
if (handler instanceof HandlerMethod)
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
RepeatSubmit annotation =
method.getAnnotation(RepeatSubmit.class);
if (annotation != null)
if (this.isRepeatSubmit(request, annotation))
Result result = Result.OK(annotation.message());
renderString(response,
JSONObject.toJSONString(result));
return false;
return true;
else
return true;
/**
* 验证是否重复提交由子类实现具体的防重复提交的规则
*
* @param request
* @return
* @throws Exception
*/
public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);
/**
* 将字符串渲染到客户端
*
* @param response 渲染对象
* @param string 待渲染的字符串
*/
public static void renderString(HttpServletResponse response, String
string)
try
response.setStatus(200);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().print(string);
catch (IOException e)
e.printStackTrace();
对防重复提交业务,拦截器具体逻辑实现
package org.jeecg.common.RepeatSubmit.interceptor.impl;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.RepeatSubmit.RepeatSubmit;
import org.jeecg.common.RepeatSubmit.filter.RepeatedlyRequestWrapper;
import org.jeecg.common.RepeatSubmit.http.HttpHelper;
import org.jeecg.common.RepeatSubmit.interceptor.RepeatSubmitInterceptor;
import org.jeecg.common.RepeatSubmit.redis.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Description: 判断请求url和数据是否和上一次相同,如果和上次相同,则是重复提交表单。 有效时间为10秒内。
* @author: qixiongfei
* @date: 2022/2/28 11:41
* @version V1.0
*/
@Component
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
public final String REPEAT_PARAMS = "repeatParams";
public final String REPEAT_TIME = "repeatTime";
public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
// 令牌自定义标识
@Value("$token.header")
private String header;
@Autowired
private RedisCache redisCache;
@SuppressWarnings("unchecked")
@Override
public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation)
String nowParams = "";
if (request instanceof RepeatedlyRequestWrapper)
RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
nowParams = HttpHelper.getBodyString(repeatedlyRequest);
// body参数为空,获取Parameter的数据
if (StringUtils.isEmpty(nowParams))
nowParams = JSONObject.toJSONString(request.getParameterMap());
Map<String, Object> nowDataMap = new HashMap<String, Object>();
nowDataMap.put(REPEAT_PARAMS, nowParams);
nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
// 请求地址(作为存放cache的key值)
String url = request.getRequestURI();
System.out.println(url);
// 唯一值(没有消息头则使用请求地址)
String submitKey = StringUtils.trimToEmpty(request.getHeader(header));
// 唯一标识(指定key + url + 消息头)
String cacheRepeatKey = REPEAT_SUBMIT_KEY + url + submitKey;
Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
//1、第1秒点击时走=null方法,五秒之内走!=null方法 五秒之外后的第1秒点击时redis存储的信息过期,重新开始走=null方法
//1、第1秒点击时走=null方法,五秒之内走!=null方法 五秒之外后的第1秒点击时redis存储的信息过期,重新开始走=null方法
if (sessionObj != null)
Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
if (sessionMap.containsKey(url))
Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval()))
return true;
Map<String, Object> cacheMap = new HashMap<String, Object>();
cacheMap.put(url, nowDataMap);
redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS);
return false;
/**
* 判断参数是否相同
*/
private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
String nowParams = (String) nowMap.get(REPEAT_PARAMS);
String preParams = (String) preMap.get(REPEAT_PARAMS);
return nowParams.equals(preParams);
/**
* 判断两次间隔时间
*/
private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval)
long time1 = (Long) nowMap.get(REPEAT_TIME);
long time2 = (Long) preMap.get(REPEAT_TIME);
if ((time1 - time2) < interval)
return true;
return false;
使用方法一:采用默认参数
@AutoLog(value = "知识库-点赞")
@ApiOperation(value = "知识库-点赞", notes = "知识库-点赞")
@PutMapping(value = "/like")
@RepeatSubmit
public Result<?> like(
@RequestParam(name = "condition", required = true) String condition)
......
使用方法二:指定防重复时间和错误消息
@AutoLog(value = "知识库-点赞")
@ApiOperation(value = "知识库-点赞", notes = "知识库-点赞")
@PutMapping(value = "/like")
@RepeatSubmit(interval = 5000, message = "点赞不允许重复提交,请稍后再试!")
public Result<?> like(
@RequestParam(name = "condition", required = true) String condition)
......
前端通过js控制
// 禁用按钮
$.modal.disable();
// 启用按钮
$.modal.enable();
以上是关于防止XSS注入的方法的主要内容,如果未能解决你的问题,请参考以下文章