防御前端渗透的编码技巧
前端攻击主要包括XSS(跨站脚本攻击)、CSRF(跨站请求伪造)、SQL注入。网上有很多,“Noodles”的技术周刊 中就有很多详细的解释。
一、 XSS&SQL注入
它们的共同之处是在用户输入或抓包修改的情况下,由于前后端没有有效过滤,导致恶意代码的执行。
1、XSS&SQL注入编码技巧:
前端的转义是必不可少的,但是抓包就除外了,所以我们重点放在后端。网上有个XSSProject,地址为:http://yunjiechao-163-com.ite...。
也可以直接用下面的代码:
- /**
- *
- * XSS过滤
- * @author Alex
- *
- */
- public class XSSFilter implements Filter{
-
- public void doFilter(ServletRequest arg0, ServletResponse arg1,
- FilterChain arg2) throws IOException, ServletException {
- // TODO Auto-generated method stub
- HttpServletRequest request = (HttpServletRequest) arg0;
- HttpServletResponse response = (HttpServletResponse) arg1;
- XssAndSqlHttpServletRequestWrapper xssRequest = new XssAndSqlHttpServletRequestWrapper(request);//采用包装器过滤掉恶意输入
- arg2.doFilter(xssRequest, response);
- }
-
- }
复制代码- /**
- *
- * XSS包装器
- * @author Alex
- *
- */
- public class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {
-
- private Logger log = Logger.getLogger(getClass());
- private HttpServletRequest orgRequest = null;
-
- public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) {
- super(request);
- orgRequest = request;
- }
-
- @Override
- public String getParameter(String name) {
- String value = null;
- try {
- //不过滤菜单
- if(!name.equals("menuHtml")){//是我自己的菜单,大家无视掉!
- value = super.getParameter(xssEncode(name));
- if (value != null) {
- value = URLDecoder.decode(value, Constant.UTF);//或用BASE处理中文乱码
- value = xssEncode(value);
- }
- }else{
- value = super.getParameter(name);
- }
- } catch (UnsupportedEncodingException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- log.error(e.getMessage());
- }
- return value;
- }
-
- @Override
- public String getHeader(String name) {//请求头也可能插入
- String value = null;
- try {
- value = super.getHeader(xssEncode(name));
- if (value != null) {
- value = xssEncode(value);
- }
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- log.error(e.getMessage());
- }
- return value;
- }
-
- private static String xssEncode(String s) {//替换成中文字符
- if (s == null || s.isEmpty()) {
- return s;
- }else{
- s = stripXSSAndSql(s);
- }
- StringBuilder sb = new StringBuilder(s.length() + 16);
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- switch (c) {
- case '>':
- sb.append(">");// 转义大于号
- break;
- case '<':
- sb.append("<");// 转义小于号
- break;
- case ''':
- sb.append("'");// 转义单引号
- break;
- case '"':
- sb.append(""");// 转义双引号
- break;
- case '&':
- sb.append("&");// 转义&
- break;
- case '#':
- sb.append("#");// 转义#
- break;
- default:
- sb.append(c);
- break;
- }
- }
- return sb.toString();
- }
-
- public HttpServletRequest getOrgRequest() {
- return orgRequest;
- }
-
- public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
- if (req instanceof XssAndSqlHttpServletRequestWrapper) {
- return ((XssAndSqlHttpServletRequestWrapper) req).getOrgRequest();
- }
- return req;
- }
-
- public static String stripXSSAndSql(String value) {
- if (value != null) {
- // Avoid anything between script tags
- Pattern scriptPattern = Pattern.compile("<[rn| | ]*script[rn| | ]*>(.*?)</[rn| | ]*script[rn| | ]*>", Pattern.CASE_INSENSITIVE);
- value = scriptPattern.matcher(value).replaceAll("");
- // Avoid anything in a src="http://www.yihaomen.com/article/java/..." type of e-xpression
- scriptPattern = Pattern.compile("src[rn| | ]*=[rn| | ]*[\"|\'](.*?)[\"|\']", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
- value = scriptPattern.matcher(value).replaceAll("");
- // Remove any lonesome </script> tag
- scriptPattern = Pattern.compile("</[rn| | ]*script[rn| | ]*>", Pattern.CASE_INSENSITIVE);
- value = scriptPattern.matcher(value).replaceAll("");
- // Remove any lonesome <script ...> tag
- scriptPattern = Pattern.compile("<[rn| | ]*script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
- value = scriptPattern.matcher(value).replaceAll("");
- // Avoid eval(...) expressions
- scriptPattern = Pattern.compile("eval\((.*?)\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
- value = scriptPattern.matcher(value).replaceAll("");
- // Avoid e-xpression(...) expressions
- scriptPattern = Pattern.compile("e-xpression\((.*?)\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
- value = scriptPattern.matcher(value).replaceAll("");
- // Avoid javascript:... expressions
- scriptPattern = Pattern.compile("javascript[rn| | ]*:[rn| | ]*", Pattern.CASE_INSENSITIVE);
- value = scriptPattern.matcher(value).replaceAll("");
- // Avoid vbscript:... expressions
- scriptPattern = Pattern.compile("vbscript[rn| | ]*:[rn| | ]*", Pattern.CASE_INSENSITIVE);
- value = scriptPattern.matcher(value).replaceAll("");
- // Avoid onload= expressions
- scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
- value = scriptPattern.matcher(value).replaceAll("");
- }
- return value;
- }
-
- }
复制代码
web.xml中添加
- <filter>
- <filter-name>XSSFilter</filter-name>
- <filter-class>com.xxx.filter.XSSFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>XSSFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
复制代码
二、CSRF
1、Referer的方式
在过滤器中判读请求头中Referer的值,如同域名就可以访问。
- String referer = request.getHeader("Referer");
- if(referer.startsWith("http://www.xxx.com")){
- chain.doFilter(request, response);
- }
复制代码
弊端:用户过于关心自己的浏览记录,可能会屏蔽这个值,导致正常用户也不能访问系统。
2、采用Token的方式
弊端:黑客发送给用户一个链接,盗取Token,这种方式叫CXRF。
3、JWT的方式
今天闲来无事就试了一下这种方法,感觉还不错,跟大家分享一下。开源社区有源码,我没用那个就自己随便写了点。
下面是代码:
- /**
- *
- * @author Alex
- *
- */
- public class JWTFilter implements Filter{
-
- private Logger log = Logger.getLogger(getClass());
-
- public void destroy() {
- // TODO Auto-generated method stub
-
- }
-
- public void doFilter(ServletRequest arg0, ServletResponse arg1,
- FilterChain arg2) throws IOException, ServletException {
- // TODO Auto-generated method stub
- HttpServletRequest request = (HttpServletRequest) arg0;
- HttpServletResponse response = (HttpServletResponse) arg1;
- try {
- String jwt = null;
- Cookie[] cookies = request.getCookies();
- for(Cookie cookie:cookies){
- if(cookie.getName().equals("jwt")){
- jwt = cookie.getValue();
- break;
- }
- }
- String userName = (String) request.getSession().getAttribute("userName");
- if(jwt==null){//Cookie中无JWT
- jwt = JWTWrapper.createJWT(userName);//userName设置为接收方
- response.addHeader("Set-Cookie", "jwt="+jwt+";Path=/;HttpOnly");//防止JS获取Cookie
- }else{
- int judge = JWTWrapper.judgeJWT(jwt, userName);//判断JWT是否过期
- if(judge==-1){//被篡改
- return ;
- }else if(judge==0){//过期或将要过期
- String jwt_new = JWTWrapper.createJWT(userName);
- response.addHeader("Set-Cookie", "jwt="+jwt_new+";Path=/;HttpOnly");
- }
- }
- arg2.doFilter(request, response);
- } catch (Exception e) {
- // TODO: handle exception
- e.printStackTrace();
- log.error(e.getMessage());
- }
- }
-
- public void init(FilterConfig arg0) throws ServletException {
- // TODO Auto-generated method stub
-
- }
-
- }
复制代码- /**
- *
- * JWT包装器
- * @author Alex
- *
- */
- public class JWTWrapper {
-
- private static String iss = "XXX";//签发者
- private static Long exp_add = Long.valueOf(30*60*1000);//过期时间,默认半小时
- private static String des_key = "XXXXXXX";
-
- /**
- * 创建JWT
- * @param aud 接收方
- */
- public static String createJWT(String aud){
- String jwt = null;
- try {
- Long iat = System.currentTimeMillis();//签发时间,应该用秒,用毫秒省事了
-
- String header = "{"typ":"JWT","alg":"DES"}";//头部
- String payload = "{"iss":""+iss+"","aud":""+aud+"","iat":"+iat+","exp":"+(iat+exp_add)+"}";//载荷
- String signature = null;//签名
-
- header = Base64.encodeBase64URLSafeString(header.getBytes(Constant.UTF));
- payload = Base64.encodeBase64URLSafeString(payload.getBytes(Constant.UTF));
-
- signature = DesUtil.encrypt(header+"."+payload, des_key);//des加密速度快些,可以用其它加密
-
- jwt = header+"."+payload+"."+signature;
- } catch (Exception e) {
- // TODO: handle exception
- e.printStackTrace();
- System.out.println(e.getMessage());
- }
- return jwt;
- }
-
- /**
- * 验证JWT
- * @param jwt
- * @param aud 接收方
- * @return
- */
- public static int judgeJWT(String jwt,String aud){
- int judge = -1;//-1:篡改的JWT
- try {
- if(jwt.indexOf(".")!=-1){//分割JWT
- String[] strs = jwt.split("\.");
- String signature_new = strs[0]+"."+strs[1]+"."+DesUtil.encrypt(strs[0]+"."+strs[1], des_key);//签名
-
- if(signature_new.startsWith(jwt)){//未被篡改
- String payload = new String(Base64.decodeBase64(strs[1]), Constant.UTF);//载荷
- JSONObject JO = JSONObject.fromObject(payload);
- if(JO.getString("iss").equals(iss)&&JO.getString("aud").equals(aud)){
- Long exp = JO.getLong("exp");
-
- Long iat = System.currentTimeMillis();//签发时间
- if(exp>iat&&(exp-iat)>1*60*1000){//过期时间>1分钟
- judge = 1;//正常,不需更新JWT
- }else{
- judge = 0;//JWT过期或将要过期
- }
- }
- }
- }
- } catch (Exception e) {
- // TODO: handle exception
- e.printStackTrace();
- System.out.println(e.getMessage());
- }
- return judge;
- }
-
- }
复制代码
大家有要用的,就动手试试吧!
先这样吧,有时间会继续追加。不太会写文章,本人工作经验不多,希望大家海涵,哈哈。 |