资讯专栏INFORMATION COLUMN

Web安全 - 预防前端渗透的编码技巧

hufeng / 3382人阅读

摘要:注入编码技巧前端的转义是必不可少的,为了防止抓包修改参数值,我们重点放在后端。为了契合前后分离的说法。所以请不要认为可以预防,这是一种错误的理解目前存储的方式有以下几种存取优点不易遭受可设置弊端易遭受。

前端攻击主要包括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);//处理中文乱码
                    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("<[
| | ]*script[
| | ]*>(.*?)", 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[
| | ]*=[
| | ]*["|"](.*?)["|"]", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Remove any lonesome  tag  
            scriptPattern = Pattern.compile("", Pattern.CASE_INSENSITIVE);  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Remove any lonesome ,如果Chrome没有显示弹出,请设置:

上图的设置会取消浏览器的xss-auditor启动(一种浏览器内建的xss防御模块,可阻止大多数反射型xss)。如果不想使用上图的方式启动,可以将服务端代码改为:

@app.route("/hello-injection")
def hello_inject():
    person = {"name": "asd", "secret": "aaaaaaaaaaa"}
    if request.args.get("name"):
        person["name"] = request.args.get("name")
        template = """

Hello %s

""" % person["name"] response = make_response(person["name"]) response.headers["X-XSS-Protection"] = "0" # xss auditor关闭 return response

将xss-auditor关闭可以弹出。如果开启呢?还能弹出吗?修改成以下代码:

@app.route("/hello-injection")
def hello_inject():
    person = {"name": "asd", "secret": "aaaaaaaaaaa"}
    if request.args.get("name"):
        person["name"] = request.args.get("name")
        template = """

Hello %s

""" % person["name"] response = make_response(person["name"].replace(">", ">;")) response.headers["X-XSS-Protection"] = "1" # xss auditor开启 return response

竟然可以弹出。。。这里介绍下auditor的:

通常情况下,我们都会对用户提交的数据进行一些处理,如果这些处理导致和提交的内容不一样了,但是仍然可以执行,比如像本例一样。那么xss auditor 就无能为力了。不过xss auditor本身的智能度也挺高,像字符编码,大小写变化这种变化依然躲不过xss auditor。
二、CSRF 1、Referer

在过滤器或拦截器中判读请求头的Referer值,如果同域名就可以继续访问,否则请求被拦截。

String referer = request.getHeader("Referer");
if(referer.startsWith("http://www.xxx.com")){
    chain.doFilter(request, response);
}else{
    return ;
}

弊端:有些网站或用户会停用Referer,所以上面这种方式会导致正常用户也不能访问系统。

2、Token

这种方式用的最多,是将Token作为每次请求的参数来验证请求是否有效。

弊端:黑客可通过发送用户链接,盗取Token值。

3、JWT

为何用JWT?

白话意思是用户的信息可放在客户端保存,代替之前服务器session的保存方式。为了契合前后分离的说法。所以请不要认为JWT可以预防CSRF,这是一种错误的理解!

目前存储JWT的方式有以下几种:
1、Cookie存取
优点:不易遭受XSS(可设置HttpOnly)
弊端:易遭受CSRF。
2、LocalStorage
优点:不产生CSRF、存储量大
缺点:易遭受XSS、难清除(Android机很难清除)
3、

YOU stick the (JWT) token in the Authorization HTTP header of a request.

这是http://stackoverflow.com/描述的一种方法。

总结起来就是各有优缺点。个人觉得CSRF较难防御,看个人轻重程度了。

下面是代码:

其中有些是自己项目的逻辑,请需修改。

/**
 * 
 * @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;
        AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
        try {
            String jwt = null;
            Cookie[] cookies = request.getCookies();
            for(Cookie cookie:cookies){
                if(cookie.getName().equals("jwt")){
                    jwt = cookie.getValue();
                    break;
                }
            }
            String referer = request.getHeader("Referer");
            String userName = (String) principal.getName();
            if(referer!=null&&referer.indexOf("/XXX/login")!=-1&&jwt==null){//登录时设置JWT及判断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";//des密钥
    
    /**
     * 创建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);//可用其它加密
            
            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!=null&&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;
    }
    
}

先这样吧,不太会写文章,希望大家海涵。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/88063.html

相关文章

  • Web安全 - 预防前端渗透编码技巧

    摘要:注入编码技巧前端的转义是必不可少的,为了防止抓包修改参数值,我们重点放在后端。为了契合前后分离的说法。所以请不要认为可以预防,这是一种错误的理解目前存储的方式有以下几种存取优点不易遭受可设置弊端易遭受。 前端攻击主要包括XSS(跨站脚本攻击)、CSRF(跨站请求伪造)、SQL注入。Noodles的技术周刊 中有详细解释。 一、XSS&SQL注入 它们的发生是在用户恶意输入和抓包修改情...

    lijinke666 评论0 收藏0
  • Web安全 - 预防前端渗透编码技巧

    摘要:注入编码技巧前端的转义是必不可少的,为了防止抓包修改参数值,我们重点放在后端。为了契合前后分离的说法。所以请不要认为可以预防,这是一种错误的理解目前存储的方式有以下几种存取优点不易遭受可设置弊端易遭受。 前端攻击主要包括XSS(跨站脚本攻击)、CSRF(跨站请求伪造)、SQL注入。Noodles的技术周刊 中有详细解释。 一、XSS&SQL注入 它们的发生是在用户恶意输入和抓包修改情...

    raledong 评论0 收藏0
  • Web安全 - 预防前端渗透编码技巧

    摘要:注入编码技巧前端的转义是必不可少的,为了防止抓包修改参数值,我们重点放在后端。为了契合前后分离的说法。所以请不要认为可以预防,这是一种错误的理解目前存储的方式有以下几种存取优点不易遭受可设置弊端易遭受。 前端攻击主要包括XSS(跨站脚本攻击)、CSRF(跨站请求伪造)、SQL注入。Noodles的技术周刊 中有详细解释。 一、XSS&SQL注入 它们的发生是在用户恶意输入和抓包修改情...

    Render 评论0 收藏0
  • [面试专题]前端需要知道web安全知识

    摘要:攻击方式端口扫描攻击洪水攻击洪水攻击跳转攻击防范手段保证服务器系统的安全确保服务器软件没有任何漏洞,防止攻击者入侵。 前端需要知道的web安全知识 标签(空格分隔): 未分类 安全 [Doc] Crypto (加密) [Doc] TLS/SSL [Doc] HTTPS [Point] XSS [Point] CSRF [Point] 中间人攻击 [Point] Sql/Nosql ...

    ivydom 评论0 收藏0
  • [面试专题]前端需要知道web安全知识

    摘要:攻击方式端口扫描攻击洪水攻击洪水攻击跳转攻击防范手段保证服务器系统的安全确保服务器软件没有任何漏洞,防止攻击者入侵。 前端需要知道的web安全知识 标签(空格分隔): 未分类 安全 [Doc] Crypto (加密) [Doc] TLS/SSL [Doc] HTTPS [Point] XSS [Point] CSRF [Point] 中间人攻击 [Point] Sql/Nosql ...

    王晗 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<