41

GitHub - momosecurity/rhizobia_J: JAVA安全SDK及编码规范

 5 years ago
source link: https://github.com/momosecurity/rhizobia_J
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

README.md

项目简介

本项目包含两部分: java安全编码规范和JAVA安全SDK,SDK介绍详见下述。

项目结构

├── LICENSE
├── README.md
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── immomo
    │   │           └── rhizobia
    │   │               └── rhizobia_J
    │   │                   ├── base
    │   │                   │   ├── SqliSanitiser.java
    │   │                   │   └── WhiteChecker.java
    │   │                   ├── crypto
    │   │                   │   ├── AESUtils.java
    │   │                   │   └── RSAUtils.java
    │   │                   ├── csrf
    │   │                   │   └── CSRFTokenUtils.java
    │   │                   ├── deserialization
    │   │                   │   └── SecureObjectInputStream.java
    │   │                   ├── extra
    │   │                   │   ├── LICENSE
    │   │                   │   ├── LICENSE-CONTENT
    │   │                   │   ├── LICENSE-README
    │   │                   │   ├── codecs
    │   │                   │   │   ├── AbstractCharacterCodec.java
    │   │                   │   │   ├── AbstractCodec.java
    │   │                   │   │   ├── AbstractIntegerCodec.java
    │   │                   │   │   ├── AbstractPushbackSequence.java
    │   │                   │   │   ├── Codec.java
    │   │                   │   │   ├── DB2Codec.java
    │   │                   │   │   ├── HTMLEntityCodec.java
    │   │                   │   │   ├── HashTrie.java
    │   │                   │   │   ├── JavaScriptCodec.java
    │   │                   │   │   ├── MySQLCodec.java
    │   │                   │   │   ├── OracleCodec.java
    │   │                   │   │   ├── PushBackSequenceImpl.java
    │   │                   │   │   ├── PushbackSequence.java
    │   │                   │   │   ├── PushbackString.java
    │   │                   │   │   └── Trie.java
    │   │                   │   └── commons
    │   │                   │       ├── CollectionsUtil.java
    │   │                   │       ├── EncoderConstants.java
    │   │                   │       ├── NullSafe.java
    │   │                   │       ├── RandomCreater.java
    │   │                   │       └── StringUtilities.java
    │   │                   ├── sqli
    │   │                   │   ├── DB2Sanitiser.java
    │   │                   │   ├── MysqlSanitiser.java
    │   │                   │   └── OracleSanitiser.java
    │   │                   ├── ssrf
    │   │                   │   └── SSRFWhiteChecker.java
    │   │                   ├── urlredirection
    │   │                   │   └── UrlRedirectionWhiteChecher.java
    │   │                   ├── xss
    │   │                   │   └── XssSanitiser.java
    │   │                   └── xxe
    │   │                       └── XmlUtils.java
    │   └── resources
    │       └── log4j.properties
    └── test
        └── java
            └── com
                └── immomo
                    └── rhizobia
                        └── rhizobia_J
                            ├── AESUtilsTest.java
                            ├── CSRFTokenUtilsTest.java
                            ├── DB2SanitiserTest.java
                            ├── MysqlSanitiserTest.java
                            ├── OracleSanitiserTest.java
                            ├── SSRFWhiteCheckerTest.java
                            ├── SafeClass.java
                            ├── SecureObjectInputStreamTest.java
                            ├── TestBean.java
                            ├── UnsafeClass.java
                            ├── UrlRedirectionWhiteChecherTest.java
                            ├── XmlUtilsTest.java
                            └── XssEncoderTest.java

目录

1、引用java security library

环境需求

  • Java 8
  • Maven 3

a、编译jar包:

    mvn -Dmaven.test.skip=true clean install

b、引入java security library:

在target目录中找到target/rhizobia_J-1.0.jar,导入工程中

需要在自己的maven工程pom.xml中加入如下依赖

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.11</version>
    </dependency>

2、SQL注入防护

a、确认是连接的是哪种数据库,选择对应的数据库编码,目前支持数据库包括:MySQL Oracle DB2:

    import com.immomo.rhizobia.rhizobia_J.sqli.MysqlSanitiser;
    //import com.immomo.rhizobia.rhizobia_J.sqli.OracleSanitiser;
    //import com.immomo.rhizobia.rhizobia_J.sqli.DB2Sanitiser;
    
    MysqlSanitiser sqlTool = MysqlSanitiser.getInstance();

b、对sql语句中拼接的参数进行转义:

    String id = "1' or '1'='1' #";
    String idEncode = sqlTool.mysqlSanitise(id);
    String query = "SELECT NAME FROM users WHERE id = '" + idEncode + "'";

使用order by、group by等需要转换列名时,需使用带boolean参数

    //保证列名中的下划线不被转义
    String columnName = "user_name";
    String columnNameEncode = sqlTool.mysqlSanitise(columnName, true);
    query = "SELECT NAME FROM users order by " + columnNameEncode ;

c、转义前后对比:

    转义前:SELECT NAME FROM users WHERE id = '1' or '1'='1' #'
    转义后:SELECT NAME FROM users WHERE id = '1\' or \'1\'\=\'1\' \#'

d、表名列名转义前后对比:

    转义前:SELECT NAME FROM users order by user_name
    转义后:SELECT NAME FROM users order by user_name
    转义前:SELECT NAME FROM users order by user-name
    转义后:SELECT NAME FROM users order by user\-name

3、xss防护

a、调用XssSanitiser单例:

    import com.immomo.rhizobia.rhizobia_J.xss.XssSanitiser;
    
    XssSanitiser xssFilter = XssSanitiser.getInstance();

b、如果输出到html body:

    String ret = xssFilter.encodeForHTML(oriString);

过滤前后对比:

    过滤前:<script> alert('xss') </script>
    过滤后:&lt;script&gt;alert&#x28;&#x27;xss&#x27;&#x29;&lt;&#x2f;script&gt;

c、如果输出到html标签的属性(多了对空字符的过滤):

    String ret = xssFilter.encodeForHTMLAttribute(oriString);

过滤前后对比:

    过滤前:<script> alert('xss') </script>
    过滤后:&lt;script&gt;&#x20;alert&#x28;&#x27;xss&#x27;&#x29;&#x20;&lt;&#x2f;script&gt;

d、如果输出到JavaScript代码块中:

    String ret = xssFilter.encodeForJavaScript(oriString);

过滤前后对比:

    过滤前:alert('xss');
    过滤后:alert\x28\x27xss\x27\x29\x3B

4、url重定向防护

a、调用UrlRedirectionWhiteChecher单例:

    import com.immomo.rhizobia.rhizobia_J.urlredirection.UrlRedirectionWhiteChecher;
    
    UrlRedirectionWhiteChecher urlChecker = UrlRedirectionWhiteChecher.getInstance();

b、自定义白名单:

    List<String> whitelist = new ArrayList<String>();
    String white1=".trust1.com";
    String white2=".trust2.com";
    
    //setWhiteList会先清空原有白名单列表
    //在原有基础上新增白名单,使用addWhiteList(whitelist)
    urlChecker.setWhiteList(whitelist);

c、校验url:

    try{
        boolean isWhite = urlChecker.verifyURL(url.trim());
    } catch (Exception e) {
        ...
    }

5、SSRF防护

a、调用SSRFWhiteChecker单例,与前面url重定向类似:

    import com.immomo.rhizobia.rhizobia_J.ssrf.SSRFWhiteChecker;
    
    SSRFWhiteChecker ssrfChecker = SSRFWhiteChecker.getInstance();

b、自定义白名单:

    List<String> whitelist = new ArrayList<String>();
    String white1=".trust1.com";
    String white2=".trust2.com";
    
    //setWhiteList会先清空原有白名单列表
    //在原有基础上新增白名单,使用addWhiteList(whitelist)
    ssrfChecker.setWhiteList(whitelist);

c、校验url:

    try{
        boolean isWhite = ssrfChecker.verifyURL(url.trim());
    } catch (Exception e) {
        ...
    }

6、CSRF防护

a、随机算出csrf token,并且每次生成随机值都不一样(实测结果连续生成1000亿次无重复):

    import com.immomo.rhizobia.rhizobia_J.csrf.CSRFTokenUtils;

    CSRFTokenUtils csrfInstance = CSRFTokenUtils.getInstance();
    String token = csrfInstance.resetCsrfToken(32);

b、后端保存生成的token,以待校验(可以采用数据库、分布存储等任意存储手段)

c、前端页面加上hidden字段

form中加入csrf token的hidden字段:

    <input name="${(_csrf.parameterName)!}" value="${(_csrf.token)!}" type="hidden">

ajax中加入csrf头

    xhr.setRequestHeader("${_csrf.headerName}", "${_csrf.token}");

d、当前端向后端发送请求时,请求header中携带token,后端收到后与之前存储的token进行校验

7、readObject反序列化漏洞防护

a、选择适当的构造函数初始化,自定义白名单:

使用SecureObjectInputStream中适当的构造函数,增加自定义的白名单

    import com.immomo.rhizobia.rhizobia_J.deserialization.SecureObjectInputStream;

    SecureObjectInputStream(InputStream in, String[] classlist)
    SecureObjectInputStream(InputStream in, List<String> classlist)

b、使用安全的类SecureObjectInputStream,恢复非白名单中类的对象时会抛出异常:

    List<String> classlist = new ArrayList<String>();
    classlist.add(SafeClass.class.toString());
    
    try{   
        //考虑如果白名单为空时会影响正常判断逻辑,所以此处会抛出异常
        SecureObjectInputStream ois = new SecureObjectInputStream(fis, classlist);
        
        //使用安全的SecureObjectInputStream恢复对象时会抛出exception
        UnsafeClass objectFromDisk = (UnsafeClass)ois.readObject();
    } catch (Exception e) {
        ...
    }

8、xxe防护

8.1、解析xml内容为Document

a、初始化时注意xml编码格式:

    import com.immomo.rhizobia.rhizobia_J.xxe.XmlUtils;
    //如果xml格式包含外部实体,会抛异常
    try{
        Document doc =  XmlUtils.getInstance().newDocument(xmlFile, "utf-8");
    } catch (Exception e) {
        ...
    }

b、使用生成的Document对象:

    Node notifyNode = doc.getFirstChild();
    NodeList list = notifyNode.getChildNodes();
    for (int i = 0, length = list.getLength(); i < length; i++) {
        Node n = list.item(i);
        String nodeName = n.getNodeName();
        String nodeContent = n.getTextContent();
        System.out.println(nodeName.toString() + "    " + nodeContent.toString());
    }

8.2、解析xml内容为Bean

a、自定义TestBean,然后调用converyToJavaBean解析:

    import com.immomo.rhizobia.rhizobia_J.xxe.XmlUtils;

    //如果xml格式包含外部实体,会抛异常
    XmlUtils xmlParser =  XmlUtils.getInstance();
    try {
        TestBean testbean = (TestBean)xmlParser.converyToJavaBean(xmlFile, TestBean.class);
    } catch (Exception e) {
        ...
    }

b、使用生成的bean对象:

    testbean.getTo()
    testbean.getFrom()
    testbean.getHeading()
    testbean.getBody()

9、AES加解密

知识点1:oracle官方已经在如下版本去除了aes-256的限制,6u181,7u171,8u161,9 b148,openjdk7u
                 https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8170157
 
知识点2:之所以AES、RSA没有封装base64或16进制编码处理,是因为在使用base64编码后的内容中,可能存在'+'字符,
              '+'字符返回给前端后再返回给后端时,如果不经过处理,会变为' '空格字符,
               所以请用户自行选择base64编码或16进制编码
               并在对加密内容进行base64编码时,请注意'+'字符

a、调用AESUtils:

    import com.immomo.rhizobia.rhizobia_J.crypto.AESUtils;
    
    AESUtils aesInstance = AESUtils.getInstance(String aesKey, String secretKey, String aesMode);
    /**
    参数说明:
        aesKey:     用于生成密钥的原始字符串,推荐使用session/id,具有唯一性
        secretKey:  加解密双方约定的secret
        aesMode:    值为null时,默认采用"AES/CBC/PKCS5Padding"
    */
    AESUtils aesInstance = AESUtils.getInstance("843739488","TcmEqGzSpH5S2VgoUix7HJ9cwqCofoUD",null);

b、加密

    String orginText = "10000";
    
    byte[] ciphertext = aesInstance.Encrypt(orginText);
    //由于返回是byte流,所以如果需要base64编码或转换成Hex,需另做处理
    String encryptRet = new BASE64Encoder().encode(ciphertext);

c、解密

    //同样,如果加密内容用base64编码或转换成Hex,解密时需另做处理
    byte[] encrypted = new BASE64Decoder().decodeBuffer(encryptRet);
    String DeString = aesInstance.Decrypt(encrypted);

10、RSA加解密 加签验签

感谢LeadroyaL的issue

同前一节知识点2

10.1、加解密

a、创建RSAUtils单例

  1. 如密钥在文件中:
    import com.immomo.rhizobia.rhizobia_J.crypto.RSAUtils;
    
    RSAUtils rsaInstance = RSAUtils.getInstance(priKeyPath, pubKeyPath);
    /**
    参数说明:目前证书支持 PEM 格式
        priKeyPath:  openssl生成的私钥地址
        pubKeyPath:  openssl生成的公钥地址
    */
    String priKeyPath = "/tmp/pri.key";
    String pubKeyPath = "/tmp/pub.key";
    RSAUtils rsaInstance = RSAUtils.getInstance(priKeyPath, pubKeyPath);
  1. 由于文件头尾格式有多种,根据需要调用set方法修改头尾后生成公私钥
   RSAUtils rsaInstance = RSAUtils.getInstance();
   rsaInstance.setPemPriHead("-----BEGIN PRIVATE KEY-----\n");
   rsaInstance.setPemPriEnd("-----END PRIVATE KEY-----");
   rsaInstance.setPemPubHead("-----BEGIN PUBLIC KEY-----\n");
   rsaInstance.setPemPubEnd("-----END PUBLIC KEY-----");
   rsaInstance.setPrivateKey(rsaInstance.getPrivateKey(priKeyPath));
   rsaInstance.setPublicKey(rsaInstance.getPublicKey(pubKeyPath));
  1. 如果已有公私钥,也可直接用已有密钥生成单例
    //如下为生成公私钥的例子,用户可任意选用其他方法生成公私钥
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(512);
    KeyPair keyPair = keyPairGenerator.generateKeyPair();
    PublicKey rsaPublicKey = (PublicKey) keyPair.getPublic();
    PrivateKey rsaPrivateKey = (PrivateKey) keyPair.getPrivate();
    
    //生成单例
    RSAUtils rsaInstance = RSAUtils.getInstance(rsaPrivateKey, rsaPublicKey);

b、加密

    String plaintext = "123";

    byte[] ciphertext = rsaInstance.encrypt(plaintext);
    //与aes一样返回是byte流,所以如果需要base64编码或转换成Hex,需另做处理
    String encryptRet = new BASE64Encoder().encode(ciphertext);

c、解密

    //同样,如果加密内容用base64编码或转换成Hex,解密时需另做处理
    byte[] encrypted = new BASE64Decoder().decodeBuffer(encryptRet);
    String plaintext = rsaInstance.decrypt(ciphertext);

10.2、加签验签

a、加签

    //原文
    String plaintext = "123";

    //有时需要生成摘要,可自己选用摘要的算法
    plaintext = DigestUtils.sha256Hex(plaintext);

    byte[] sigintext = rsaInstance.sign(plaintext);
    //与aes一样返回是byte流,所以如果需要base64编码或转换成Hex,需另做处理
    String signtRet = new BASE64Encoder().encode(sigintext);

b、验签

    //同样,如果加密内容用base64编码或转换成Hex,解密时需另做处理
    byte[] verified = new BASE64Decoder().decodeBuffer(signtRet);
    boolean ifPass = rsaInstance.verify(verified, plaintext);

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK