ESAPI结合Top10安全开发实战

ESAPI(Enterprise Security API)是一个免费开源的Web应用程序API,目的帮助开发者开发出更加安全的代码,并且它本身就很方便调用。

根据下面的图,我将会介绍OWASP上10种类型的漏洞所对应的API使用方法,大概有十多个接口。

相关API介绍可以查看官方文档:https://www.javadoc.io/doc/org.owasp.esapi/esapi/2.1.0

对应使用到的API方法(使用思维导图分类):

0x01. 安装ESAPI(Java语言)

安装ESAPI的过程

1.下载esapi (jar)

2.https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API#tab=Downloads

3.将jar包和源码Download下来

4.下载Log4j (log4j-1.2.17.zip)(一定要导入这个jar包,没有会报错的!)

http://logging.apache.org/log4j/1.2/download.html

5.我使用的编辑器是IDEA:

5-1.)通过项目 Project Structure 导入esapi、log4j;

5-2.)创建Resources目录;

在Resources目录里面导入ESAPI.properties 、 validation.properties 两个配置文件。

i.

ii.注意Idea通过如下方式创建Resources目录:

5-3.)ESAPI.properties 、validation.properties 在哪?

i. 注意这两个配置文件就在下载的ESAPI for Java(source)里面。路径如下:

写测试代码,校验是否成功部署

a).Code:

import org.owasp.esapi.ESAPI;

public class Test {
    public static void main(String[]args) {
        String safe = ESAPI.encoder().encodeForHTML("<script>alert('xss')</script>");
        System.out.println(safe);
    }
}

b). 右键运行看到的效果:

如果是JavaEE项目

1.请将esapi 和 log4j jar包放置到WEB-INF下的lib目录里面:

0x02. 从XSS类漏洞开始

防治Cross Site Scripting类型的漏洞,ESAPI提供了两个相关接口 Encode、Validator。

Encode接口

Encode(编码器接口)包含了许多解码输入和编码输出的方法,这样处理过的字符对于各种解释器都是安全的。

ESAPI根据XSS问题的特征和产生的原因,提供了不同的接口,下面我们介绍各个编码器的使用和原理。

Encode接口针对XSS的预发是在输出编码上,根据你要输出到不同地方转换成不同的编码。不同地方有HTML、Javascript、URL、CSS等。

(需了解3种编码格式:URL编码、HTML编码、JavaScript编码)其思想是对特殊意义的字符必须进行编码,编码后不影响原来结构体。

列举ESAPI针对XSS漏洞的7个预防方法:

1.调用HTML编码器

Code:

String safe1 =ESAPI.encoder().encodeForHTML("data1");
System.out.println(safe1);
String safe2 = ESAPI.encoder().encodeForHTML("<script>alert('xss')</script>");
System.out.println(safe2);

Out:

个人总结:

这个编码器使用的是HTMLEntityCodec,编译原理是,如果是空格、字母或者数字就不进行编码;如果有特殊字符在HTML中有匹配的替代字符,就使用替代字符。

应用场景:

有需要输出用户输入的字符串、表单提交后的数据需要展示的场景都可以运用。下面用留言板作为案例。

Code:

从数据库中得到数据,解析展示到用户浏览器界面。

<body>
 <table border=1>
     <tr>
         <th>留言人姓名</th>
         <th>留言时间</th>
         <th>留言标题</th>
         <th>留言内容</th>
     </tr>
     <%
         ArrayList<Message> al = new ArrayList();
         al = (ArrayList)  session.getAttribute("al");
         if (al != null) {
             Iterator it = al.iterator();
             while (it.hasNext()) {
                 Message ms = (Message)  it.next();
     %>
     <tr>
         <td><%= new LoginDao().getName(ms.getId())  %></td>
         <td><%= ms.getTime().toString()  %></td>
         <td><%= ms.getTitle() %></td>
         <td> <%=ESAPI.encoder().encodeForHTML(  ms.getMessage() ) %></td>
     </tr>
     <%
             }
         }
     %>
 </table>
 </body>

输出效果:

这样即使用户输入了这样标签,在返回到浏览器的时候也得不到执行。因为已经转换成URL编码了。

2.调用HTML属性编码器

Code:

String safe1 =ESAPI.encoder().encodeForHTMLAttribute("data 1 2");
System.out.println(safe1);
String safe2 = ESAPI.encoder().encodeForHTMLAttribute("<script>alert('xss')</script>");
System.out.println(safe2);

Out:

个人总结:

HTML属性编码和HTML编码在实现原理上是一样的,唯一的不同点就是HTML属性编码不需要对空格进行编码。所以也就是免疫了一个空格而已。

3.调用JavaScript编码器

Code:

<script>
    var searchValue = <%=ESAPI.encoder().encoderForJavaScript(searchValue)%>;
    // ......
</script>

个人总结:

JavaScript编码器首先判断是不是免疫的字符,如果是免疫字符,就直接返回;如果是数字、字母也直接返回;如果是小于256的字符就使用\xHH的编码方式;如果是大于256的字符,就使用\uHHHHH的方式编码。

4.调用CSS编码器

Code:

String ESAPI.encoder().encoderForCSS(String);

个人总结:

ESAPI的CSS编码是通过反斜杠(\)加上十六进制数进行的编码。

5.调用URL编码器

Code:

String url = ESAPI.encoder().encodeForURL("/?callback=<script>alert('xss')</script>");
System.out.println(url);
String url2 = ESAPI.encoder().encodeForURL("/a/title");
System.out.println(url2);

Out:

%2F%3Fcallback%3D%3Cscript%3Ealert%28%27xss%27%29
%3C%2Fscript%3E
%2Fa%2Ftitle

个人总结:

URL编码器先将字符串转换为UTF-8,然后对转换的字符串用%加上十六进制数的方式编码。

6.调用VBScript编码器

Code:

String vb = ESAPI.encoder().encodeForVBScript("add(1)");
System.out.println(vb);

Out:

add"&chrw(40)&"1"&chrw(41)

个人总结:

将用户数据直接放入脚本中是非常危险的。VBScript编码器对免疫字符加上字符的整型值表示。比如>的整形值是62。

7.调用复合(嵌套)编码器

Code:

<script>
    var vDiv= document.createElement('div');
    vDiv.innerHTML ="<%=ESAPI.encoder.encodeForJS(ESAPI.encoder.encodeForHTML(divVALUE))%>";
    document.body.appendChild(vDiv);
</script>

个人总结:

上面那几种都只能处理一种编码的情况,对于存在多种编码的情况,可以使用复合(嵌套)编码,它根据由内到外的调用顺序来执行ESAPI的编码方法。

8.小结

由于Web页面上输出内容的地方很多,输出的背景环境也不相同,甚至有同一个输入可能输出到同一个页面上的不同地方,每一个地方的编码也可能不同,所以想彻底预防XSS是很困难的。从安全的角度考虑,建议使用HTTPOnly标志。

Validator接口

Validator接口定义了一组用于规范化和验证不可信输入的方法。开发人员可以扩展这个接口以适应自己的数据格式。与抛出异常相比,这个接口返回布尔结果,因为并非所有的验证问题都是安全问题。布尔返回允许开发人员更清晰地处理有效、无效的结果。

开发人员使用这个方法的时候必须采用“白名单”方法来验证特定的模式或字符集匹配。因为“黑名单”有可能被绕过。

1.调用getValidInput( )

Code:

// 1
getValidInput(java.lang.String context, java.lang.String input, java.lang.String type, int maxLength,boolean allowNull, ValidationErrorList errors);

// 2
isValidInput(java.lang.String context, java.lang.String input, java.lang.String type, int maxLength,boolean allowNull);

String validatedFirstName =ESAPI.validator().getValidInput("FirstName", myForm.getFirstName(), "FirstNameRegex", 255, false, errorList);

// 3
boolean isValidFirstName = ESAPI.validator().isValidInput("FirstName", myForm.getFirstName(), "FirstNameRegex", 255, false);

应用场景:

在有数据提交的地方,防止跨站攻击。 解决方案就是验证输入,检查每个输入的有效性。

Code:

String input = "<script>alert('xss')</script>";
String type = "SafeString"; //使用validation.properties文件定义的SafeString正则表达式规则
try {
    String data = ESAPI.validator().getValidInput(
            "Swingset Validation Secure Exercise", input, type,
            200, false);
    System.out.println(data);
} catch (ValidationException e) {
    /*当检测到输入字符串不匹配正则表达式时产生该异常,应进行适当处理*/
    input = "Validation attack detected";
    request.setAttribute("userMessage", e.getUserMessage());
    request.setAttribute("logMessage", e.getLogMessage());
} catch (Exception e) {
    input = "exception thrown";
    request.setAttribute("logMessage", e.getMessage());
}

返回规范化和验证过的输入数据。所有无效的输入将生成一个描述性的ValidationException异常。

0x03.注入类漏洞

防治Injection Flaws类型的漏洞有多种类型比如有命令注入、XPath注入、LDAP注入、SQL注入、其它注入,我举例一个SQL注入的防护,而针对SQL注入ESAPI提供了一个相关接口Encode。

Encode接口 1.调用 ESAPI.Encoder().encodeForSQL()

Code:

//生成一个Oracle编码器实例
Codec ORACLE_CODEC = new OracleCodec();

//ESAPI 版本的查询
String query = "SELECT NAME FROM users WHERE id = " +
        ESAPI.encoder().encodeForSQL(ORACLE_CODEC, validatedUserId)
        + "AND date_created >= '"
        + ESAPI.encoder().encodeForSQL(ORACLE_CODEC, validatedStartDate) + "'";

//执行statement 获得结果
Statement myStmt = conn.createStatement(query);

个人总结:

可以看出使用ESAPI防范SQL注入非常容易,只需要创建一个相应的数据库编码器,然后在调用ESAPI.encoder().encodeForSQL时,作为第一个参数传入即可。 ESAPI也是封装好了过滤规则。

应用场景:

比如在搜索、查询场景下,需要用户输入的字符串插入SQL命令的地方,就可以运用。

0x04.恶意文件执行类漏洞

防治Malicious File Execution类型的漏洞,ESAPI提供了一个相关接口 Validator。

Validator接口

使用ESAPI防治恶意文件执行漏洞有2个方法,1是使用ESAPI验证上传文件名。2是检查文件大小。

1.调用isValidFileName()

Code:

if(!ESAPI.validator().isValidFileName("upload",filename,
        allowedExtensions,false)){
    throw new Validation UploadException("Upload only simple filenames with
            thefollowingextensions"+allowedExtensions,"Upload failed
            isValidFileName check");
}

2.使用ESAPI检查上传文件大小

Code:

ServletFileUpload upload=newServletFileUpload(factory);
upload.setSizeMax(maxBytes);

0x05. 不安全的直接对象引用类漏洞

防治Insecure Direct Object Reference类型的漏洞,ESAPI提供了两个相关类AccessReferenceMap、AccessController。

AccessReferenceMap类

AccessReferenceMap是ESAPI提供的用户实现非直接引用ID和直接引用ID之间匹配的类。

1.实例化RandomAccessReferenceMap类

Code:

//使用随机访问的引用map
AccessReferenceMapmap = new RandomAccessReferenceMap();

String userID =arm.getDirectReference(indirectRef);

应用场景:

在需要传递传递的访问中,比如用户访问信息页,通过UserID的操作。

AccessController接口

角色权限验证,检查当前用户对应的角色是否有权限访问url,权限的定义在esapi\fbac-policies\URLAccessRules.txt文件中。

1.调用assertAuthorizedForURL()

Code:

ESAPI.accessController().assertAuthorizedForURL();

0x06. CSRF类漏洞

防治Cross Site Request Forgery类型的漏洞,最常用的解决方案就是使用Token,在任何需要保护的表单,增加一个隐藏的字段来存放这个Token。对于需要保护的URL,需要增加一个参数来存放这个Token。ESAPI提供了一个相关接口 User。

User接口

管理身份验证和身份管理的规则接口。

1.调用resetCSRFToken()

使用流程:

a.新建CSRF令牌添加进用户每次登陆以及存储在http session里,这种令牌至少对每用户会话来说应该是唯一的,或者是对每个请求是唯一的。

//产生一个新的CSRF Token,在用户第一次登录的时候保存在用户的session中。
private String csrfToken = resetCSRFToken();
public String resetCSRFToken() {
    csrfToken = ESAPI.randomizer().getRandomString(8, DefaultEncoder.CHAR_ALPHANUMERICS);
    return csrfToken;
}

b.令牌同样可以包含在URL中或作为一个URL参数标记/隐藏字段。

c.在服务器端检查提交令牌与用户会话对象令牌是否匹 配。

d.在注销和会话超时,删除用户对象会话和会话销毁。

个人总结:

用ESAPI防止CSRF攻击的关键点就是生成Token,而且不能被攻击者知道生成方式,如果攻击者能伪造你的Token说明也是不安全的。还有CSRF安全的前提是你的站点没有XSS,不然攻击者利用XSS漏洞一样可以绕过CSRF检测。

应用场景:

在登录用户进行的操作里,都可以加上Token,比如用户提交修改密码操作。

0x07. 安全配置错误类漏洞

防治Leakage and Improper Error Handling类型的漏洞,需要具体项目具体分析。

Enterprise Security Exception

据不完全统计,国内个人信息泄露数达55.3亿条左右,平均每人就有4条相关的个人信息泄露,这些信息最终的命运,是在黑市中反复倒手,直至被榨干价值。其中,80%的数据泄露自企业内鬼,黑客仅占20%。

上面的信息说明(统计没有数据来源支持,不可全信),不过这类问题基本是内部自身的问题,得从内部开始查找、解决。

0x08. 失效的身份认证和会话管理类漏洞

0x09. 不安全的加密存储类漏洞

防治Insecure Cryptographic Storage类型的漏洞,ESAPI提供了一个相关接口 Encryptor。

Encryptor接口

Encryptor接口提供了一组用于执行通用加密、随机数和散列操作的方法。在配置文件ESAPI.properties中提供了关于加密的配置项,可以设置使用的加密算法、秘钥长度、初始化向量、哈希和签名算法。

1.调用encrypt()

Code:

//加密
String ciphertext =ESAPI.encryptor().encrypt("123456");

2.调用decrypt ()

Code:

//解密
String decrypted =ESAPI.encryptor().decrypt(ciphertext);

个人总结:

解密所提供的密文,使用来自它的信息和由属性Encryptor指定的主加密密钥。

0x10. 传输层保护不足类漏洞

防治Insecure Communications类型的漏洞,预防措施有三种:

一、秘钥交换,二、对称加密与非对称加密结合,三、SSL、TLS。

0x11. 未限制URL访问类漏洞

防治Failure to Resstrict URL Access类型的漏洞,ESAPI提供了一个相关接口 AccessController。

AccessController接口

AccessController接口定义了一组方法,这些方法可以在各种应用程序中使用,以加强访问控制。如果这个URL不是公开的,那么必须限制能够访问他的授权用户,加强基于用户或角色的访问控制,完全禁止访问未被授权的页面类型(如配置文件、日志文件、源文件等)

1.调用assertAuthorizedForURL(Stringurl)

Codemode:

ESAPI.accessController().assertAuthorizedForURL(request.getRequestURI().toString());

个人总结:

该方法检查当前用户是否被授权访问引用的URL。通常该方法在应用程序的控制器或过滤器中调用。

发表评论

电子邮件地址不会被公开。 必填项已用*标注