本文用到的技术

AOP

ESAPI

关于AOP技术

AOP(Aspect-Oriented Programming)面向切面编程。切面是什么?切面表示从业务逻辑分离出来的横切逻辑,比如性能监控、日志记录、权限控制,这些功能可从核心逻辑代码中抽离出去。也就是说AOP可以解决代码耦合问题,让职责更加单一。

这里要讲的是利用AOP技术解决代码安全问题。把安全代码从业务逻辑中分离出来,让其单一的解决安全问题。

优势:不影响业务代码,修复安全漏洞的时候,可以对原代码很好的继承,不需修改原代码(基于配置定义切点的时候)。

将AOP技术应用于安全技术的案例很少,网上很难找到较多示例代码。

关于ESAPI

OWASP Enterprise Security API (ESAPI)

ESAPI (OWASP企业安全应用程序接口)是一个免费、开源的、网页应用程序安全控件库,它使程序员能够更容易写出更低风险的程序。ESAPI接口库被设计来使程序员能够更容易的在现有的程序中引入安全因素。ESAPI库也可以成为作为新程序开发的基础。

通俗点说ESAPI是OWASP提供的一个安全开发API库。

优势:对比自己开发的安全处理代码更成熟稳定。

ESAPI具体的使用实际也是比较少,网上也很难找到较多示例代码。开发过程比较困难点

AOP 技术做 XSS 防御

OWASP提供了几种XSS防御的方法,包括阻止非信任的数据插入,escaping HTML输入、escaping attribute、escaping JavaScript、以及escaping几种其他类型。ESAPI提供了多种encoding库。另外也可以采用白名单validate方法(比如字母数字),也可以自定义正则表达式。

AOP技术实现ESAPI提供的encoding和validation库,来做XSS攻击的防御工作。

ESAPI提供的几种encoding方法:

  • 将用户数据输出到html body某处时,须经过html转义。ESAPI.encoder().encodeForHTML( request.getParameter( “input” ) )
  • 将用户数据输出到html标签的属性时,须经过标签属性的转义。ESAPI.encoder().encodeForHTMLAttribute( request.getParameter( “input” ) )
  • 将用户数据输出到JavaScript数据域时,须经过JavaScript转义。ESAPI.encoder().encodeForJavaScript( request.getParameter( “input” ) )
  • 将用户数据输出到URL的参数时,须经过URL转义。ESAPI.encoder().encodeForURL( request.getParameter( “input” ) )

方案逻辑

实现

定义切点的注解

在需要进行XSS防御的方法前使用相应的注解。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface XSSAllTag {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface XSSBeanTag {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface XSSStringTag {
}

定义切面类

拦截指定方法,遍历String类型参数,和数据模型(JAVABean)中的String属性,并对其进行XSS validate。

@Aspect
@Component
public class SecXSSAspect {
@SuppressWarnings("unchecked")
@Around(value = "@annotation(com.tony.security.aspectbox.XSSAllTag)")
public Object xssAllAdvice(ProceedingJoinPoint pjp) throws Throwable {
// 获取切入方法的参数
Object[] args = pjp.getArgs();
// 获取切入方法
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
// 获取参数的类型
Class<?>[] paramTypes = method.getParameterTypes();
// 获取参数名
ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
for (int i = 0; i < paramTypes.length; i++) {
System.out.println("parameterName" + i + ": " + parameterNames[i]);
if (paramTypes[i].equals(String.class)) {
if (args[i] != null && args[i] != "" && !parameterNames[i].equalsIgnoreCase("password")) {
args[i] = XSSValidation.escapeHTML((String) args[i]);
}
} else if (!GeneralHelper.isSimpleType(paramTypes[i]) && !GeneralHelper.isSpringType(paramTypes[i])) {
if (args[i] != null) {
Class<?> class1 = args[i].getClass();
Method[] ms = class1.getDeclaredMethods();
for (int j = 0; j < ms.length; j++) {
// 遍历所有返回为String类型的get方法(不包含password字段),获取并处理get返回值,调用其set方法重写安全编码的值
if (ms[j].getName().startsWith("get") && ms[j].getReturnType().equals(String.class)) {
if (!ms[j].getName().toLowerCase().contains("password")) {
String result = XSSValidation.escapeHTML((String) ms[j].invoke(args[i]));
String methodName = ms[j].getName().replace("get", "set");
Method setM = class1.getDeclaredMethod(methodName, String.class);
setM.invoke(args[i], result);
}
}
}
}
} else {
continue;
}
}
return pjp.proceed();
}
...
}

使用ESAPI库处理XSS攻击input

针对XSS攻击不同location与类别,提供相应的encoding方法。

public abstract class XSSValidation {
public static String REGEX_ALPHANUMERIC = "AlphaNumberic";
public static String REGEX_ALPHA = "Alpha";
public static String REGEX_NUMERIC = "Numeric";
public static String REGEX_EMAIL = "Email";
public static String REGEX_ZIP_CODE = "ZipCode";
public static String REGEX_IP_ADDRESS = "IPAddress";
public static String REGEX_SSN = "SSN";
public XSSValidation() {
}
public static String escapeCustomString(String s, String regex,
int maxLength) {
Pattern p = ESAPI.securityConfiguration().getValidationPattern(regex);
if (p == null)
p = Pattern.compile(regex);
// Sending pattern directly.
try {
return ESAPI.validator().getValidInput("CUSTOM_STRING_ESCAPE", s,
regex, maxLength, false, false);
} catch (Exception e) {
e.printStackTrace();
String resultString = "";
Matcher m = p.matcher(s);
while (m.find()) {
resultString += m.group(0);
}
return resultString;
}
}
public static String escapeJavaScript(String s) {
return ESAPI.encoder().encodeForJavaScript(s);
}
public static String escapeCSS(String s) {
return ESAPI.encoder().encodeForCSS(s);
}
public static String escapeHTML(String s) {
return ESAPI.encoder().encodeForHTML(s);
}
public static String escapeHTMLAttribute(String s) {
return ESAPI.encoder().encodeForHTMLAttribute(s);
}
public static String escapeURL(String s) throws EncodingException {
return ESAPI.encoder().encodeForURL(s);
}
@SuppressWarnings("finally")
public static String validateCreditCard(String s) {
try {
s = ESAPI.validator().getValidCreditCard("CREDIT_CARD", s, false);
} catch (ValidationException e) {
s = "VALIDATION FAILED " + s;
e.printStackTrace();
} catch (Exception e) {
s = "VALIDATION FAILED " + s;
e.printStackTrace();
} finally {
return s;
}
}
}

判定是否简单数据类型

public class GeneralHelper {
/** 简单数据类型集合 */
public static final Set<Class<?>> SMIPLE_CLASS_SET = new HashSet<Class<?>>(18);
/** Spring Controller常用数据类型集合 */
public static final Set<Class<?>> SPRING_CLASS_SET = new HashSet<Class<?>>(18);
static
{
SMIPLE_CLASS_SET.add(int.class);
SMIPLE_CLASS_SET.add(long.class);
SMIPLE_CLASS_SET.add(float.class);
SMIPLE_CLASS_SET.add(double.class);
SMIPLE_CLASS_SET.add(byte.class);
SMIPLE_CLASS_SET.add(char.class);
SMIPLE_CLASS_SET.add(short.class);
SMIPLE_CLASS_SET.add(boolean.class);
SMIPLE_CLASS_SET.add(Integer.class);
SMIPLE_CLASS_SET.add(Long.class);
SMIPLE_CLASS_SET.add(Float.class);
SMIPLE_CLASS_SET.add(Double.class);
SMIPLE_CLASS_SET.add(Byte.class);
SMIPLE_CLASS_SET.add(Character.class);
SMIPLE_CLASS_SET.add(Short.class);
SMIPLE_CLASS_SET.add(Boolean.class);
SMIPLE_CLASS_SET.add(String.class);
SMIPLE_CLASS_SET.add(Date.class);
}
/** 检查 clazz 是否为简单数据类型 */
public final static boolean isSimpleType(Class<?> clazz)
{
return SMIPLE_CLASS_SET.contains(clazz);
} static
{
SPRING_CLASS_SET.add(Model.class);
SPRING_CLASS_SET.add(ModelAndView.class);
SPRING_CLASS_SET.add(HttpSession.class); } /** 检查 clazz 是否为Spring Controller常用数据类型 */
public final static boolean isSpringType(Class<?> clazz)
{
return SPRING_CLASS_SET.contains(clazz);
}
}

Controller中使用

在Contoller层使用前面定义的注解,进行XSS攻击防护。

@XSSAllTag
@RequestMapping(value="/user/addUser")
public ModelAndView addUser(String flag,@ModelAttribute User user,ModelAndView mv){
if(flag.equals("1")){
mv.setViewName("user/showAddUser"); }else{
hrmService.addUser(user);
mv.setViewName("redirect:/user/selectUser");
}
return mv;
}

AOP技术做FileUpload防御

方案逻辑

定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FileSuffixTag {
}

定义切面

@Aspect
@Component
public class FileUploadAspect {
@SuppressWarnings("unchecked")
@Around(value = "@annotation(com.tony.security.aspectbox.FileSuffixTag)")
public Object fileSuffixAdvice(ProceedingJoinPoint pjp) throws Throwable {
boolean result = true;
// 获取切入方法的参数
Object[] args = pjp.getArgs();
// 获取切入方法
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
// 获取参数的类型
Class<?>[] paramTypes = method.getParameterTypes();
// 获取参数名
ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
for (int i = 0; i < paramTypes.length; i++) {
System.out.println("parameterName" + i + ": " + parameterNames[i]);
if (!GeneralHelper.isSimpleType(paramTypes[i]) && !GeneralHelper.isSpringType(paramTypes[i])) {
if (args[i] != null) {
Class<?> class1 = args[i].getClass();
Method[] ms = class1.getDeclaredMethods();
for (int j = 0; j < ms.length; j++) {
// 遍历所有返回为MutipartFile类型的get方法
if (ms[j].getName().startsWith("get") && ms[j].getReturnType().equals(MultipartFile.class)) {
MultipartFile file = (MultipartFile) ms[j].invoke(args[i]);
if (file !=null && !file.isEmpty()) {
result = FileUploadValidation.suffixValid(file);
System.out.println(result);
}
}
}
}
}
}
if (result == true) {
System.out.println("正确上传");
return pjp.proceed();
}else{
System.out.println("非法上传");
ModelAndView mv = new ModelAndView();
mv.addObject("message","非法上传");
mv.setViewName("forward:/loginForm");
return mv;
}
}
}

使用ESAPI中的文件上传校验库

public abstract class FileUploadValidation {
private static final String GIF_IMAGE_EXTENSION = "gif";
private static final String JPEG_IMAGE_EXTENSION = "jpeg";
private static final String JPG_IMAGE_EXTENSION = "jpg";
private static final String PNG_IMAGE_EXTENSION = "png";
public static final String FILE_UPLOAD_CONTEXT = "fileUpload";
public static boolean allowNull = false;
public FileUploadValidation() {
}
@SuppressWarnings("null")
public static boolean suffixValid(MultipartFile file)
throws IntrusionException, ValidationException {
String filename = file.getOriginalFilename(); List<String> allowedExtensions = new ArrayList<String>();
allowedExtensions.add(GIF_IMAGE_EXTENSION);
allowedExtensions.add(JPEG_IMAGE_EXTENSION);
allowedExtensions.add(JPG_IMAGE_EXTENSION);
allowedExtensions.add(PNG_IMAGE_EXTENSION);
return ESAPI.validator().isValidFileName(FILE_UPLOAD_CONTEXT,filename,allowedExtensions,allowNull);
}
}

Controller中使用

@FileSuffixTag
@RequestMapping(value="/document/addDocument")
public ModelAndView addDocument(String flag,@ModelAttribute Document document,ModelAndView mv,HttpSession session) throws Exception{ if(flag.equals("1")){
mv.setViewName("document/showAddDocument"); }else{
String path = session.getServletContext().getRealPath("/upload");
String fileName = document.getFile().getOriginalFilename();
System.out.println("path -->>"+path);
System.out.println("fileName -->>"+fileName);
document.getFile().transferTo(new File(path+File.separator+fileName));
document.setFilename(fileName);
User user = (User) session.getAttribute(HrmConstants.USER_SESSION);
document.setUser(user);
hrmService.addDocument(document);
mv.setViewName("redirect:/document/selectDocument");
}
return mv;
}

AOP技术做SQL注射防御

消除SQL注射漏洞主要有3种方法:参数化查询、存储过程(类似参数化查询,但是query存在在数据库上,供应用调用)、escaping用户输入。另外一种是做白名单验证,阻止查询中输入非法的格式。我这里选择escaping用户输入和白名单validate。其优点是可以不修改原代码。Aspects能去拦截和分析query,然后在执行前escape所有的表达式(expressions,可能包含恶意内容)。

而参数化查询需要重写动态查询代码,存储过程需要开发move所有查询到数据库层。都需要修改原有代码,可能引入bug和未知行为。此外OWASP提供了一个安全的encoding库,可以直接调用。

方案逻辑

SQL Injection validator 逻辑

Aspect拦截到query后,先进行注释移除,然后利用JSqlParser API对query的where进行解析,输出简单的expression List。

遍历上一步获得的simpleExpressions,对其进行语义重复检测(类似1=1),如果检测到有语义重复,就替换成“1=2”。

最后对类型为String的value进行encode。

https://github.com/JSQLParser/JSqlParser/wiki

ESAPI Encode

ESAPI.encoder().encodeForSQL()

编码结果样例:

输入:foo” and 1 = 2 
输出:foo\” and 1 \= 2

SQL Parser逻辑

*本文作者:tony86,转载请注明来自 FreeBuf.COM

AOP技术应用于安全防御与漏洞修复的更多相关文章

  1. 2018-2019-2 网络对抗技术 20165322 Exp6 信息搜集与漏洞扫描

    2018-2019-2 网络对抗技术 20165322 Exp6 信息搜集与漏洞扫描 目录 实验原理 实验内容与步骤 各种搜索技巧的应用 DNS IP注册信息的查询 基本的扫描技术 漏洞扫描 基础问题 ...

  2. 20165214 2018-2019-2 《网络对抗技术》Exp6 信息搜集与漏洞扫描 Week9

    <网络对抗技术>Exp6 信息搜集与漏洞扫描 Week9 一.实验目标与内容 1.实践目标 掌握信息搜集的最基础技能与常用工具的使用方法. 2.实践内容 (1)各种搜索技巧的应用 使用搜索 ...

  3. 面向侧面的程序设计AOP-------《三》.Net平台AOP技术概览

    本文转载自张逸:晴窗笔记 .Net平台与Java平台相比,由于它至今在服务端仍不具备与unix系统的兼容性,也不具备类似于Java平台下J2EE这样的企业级容器,使得.Net平台在大型的企业级应用上, ...

  4. C#.NET利用ContextBoundObject和Attribute实现AOP技术--AOP事务实现例子

    我前两天看见同事用写了用AOP技术实现缓存的方案,于是好奇看了一下这是怎么实现的.原来是用了.NET中的一个类ContextBoundObject和Attribute相关技术.其实个类在.NET Fr ...

  5. AOP技术基础

    1.引言 2.AOP技术基础 3.Java平台AOP技术研究 4..Net平台AOP技术研究 2.1 AOP技术起源 AOP技术的诞生并不算晚,早在1990年开始,来自Xerox Palo Alto ...

  6. 2018-2019-2 20165232 《网络对抗技术》 Exp6 信息搜集与漏洞扫描

    2018-2019-2 20165232 <网络对抗技术> Exp6 信息搜集与漏洞扫描 一.实践目标 掌握信息搜集的最基础技能与常用工具的使用方法. 二.实践内容. 各种搜索技巧的应 D ...

  7. 2018-2019-2 网络对抗技术 20165237 Exp6 信息搜集与漏洞扫描

    2018-2019-2 网络对抗技术 20165237 Exp6 信息搜集与漏洞扫描 实验目标 1 各种搜索技巧的应用: 2 DNS IP注册信息的查询: 3 基本的扫描技术: 主机发现.端口扫描.O ...

  8. 2018-2019-2 20165221 【网络对抗技术】-- Exp6 信息搜集与漏洞扫描

    2018-2019-2 20165221 [网络对抗技术]-- Exp6 信息搜集与漏洞扫描 目录 1. 实践目标 2. 实践内容 3. 各种搜索技巧的应用 a. 搜索网址的目录结构 b.使用IP路由 ...

  9. 2018-2019-2 20165325 《网络对抗技术》 Exp6 信息搜集与漏洞扫描

    2018-2019-2 20165325 <网络对抗技术> Exp6 信息搜集与漏洞扫描 实验内容(概要) 1 各种搜索技巧的应用: 2 DNS IP注册信息的查询: 3 基本的扫描技术 ...

随机推荐

  1. 如何将Linux rm命令删除的文件放入垃圾箱

    因为rm命令删除的文件是不会放入垃圾箱的,所以无法恢复,下面小编就给大家介绍一种方法,通过替换Linux rm命令的方法,从而将rm命令删除的文件放入垃圾箱. 方法: 1. 在/home/userna ...

  2. Selenium-java 中 对于下拉框 对于网页上的下拉框 如何定位

    WebElement e1 = driver.findElement(By.cssSelector("#s_province")); Select se1 = new Select ...

  3. Python基础-week07 Socket网络编程

    一 客户端/服务器架构 1.定义 又称为C/S架构,S 指的是Server(服务端软件),C指的是Client(客户端软件) 本章的中点就是教大写写一个c/s架构的软件,实现服务端软件和客户端软件基于 ...

  4. k8s与CICD--借助scp插件实现非容器项目的部署

    一直没有时间完成drone系列文章.drone-wechat插件实现了一半,由于企业微信token申请比较麻烦,所以也没有进展.今天抽出时间,研究了一下scp插件,主要目的是实现非容器项目的部署.其实 ...

  5. line-height与vertical-align

    css世界读书笔记: 内联元素与流 块级元素负责结构,内联元素接管内容 x元素的下边缘就是我们的基线baseline x-height就是x的高度 vertical-align:middle是x中点位 ...

  6. Ansible实战之Nginx高可用代理LNMP-wordpress

    author:JevonWei 版权声明:原创作品 blog:http://119.23.52.191/ --- 实验环境:前端使用Nginx做代理服务器,静态资源经由缓存服务器,连接后端web集群, ...

  7. 用jquery实现平滑的页面滚动效果

    通过几句jquery代码实现页面平滑滚动到某一锚点的效果.实现代码来源于https://css-tricks.com/snippets/jquery/smooth-scrolling 实现的jquer ...

  8. ICPC World Finals 2018 Problem H Single Cut of Failure

    题目链接 题解视频 题解文档 解法概要: 问题可以转化为 考虑一个长为 $2n$ 的数组 $A$,$1$ 到 $n$ 这 $n$ 个整数每个恰在 $A$ 中出现 $2$ 次.判断是否存在一个长为 $n ...

  9. 洛谷4438 [Hnoi2018]道路 【树形dp】

    题目 题目太长懒得打 题解 HNOI2018惊现普及+/提高? 由最长路径很短,设\(f[i][x][y]\)表示\(i\)号点到根有\(x\)条未修公路,\(y\)条未修铁路,子树所有乡村不便利值的 ...

  10. JS 中如何输出空格

    在写JS代码的时候,大家可以会发现这样现象: document.write(" 1 2 3 "); 结果: 无论在输出的内容中什么位置有多少个空格,显示的结果好像只有一个空格. 这 ...