栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java安全开发注意事项

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java安全开发注意事项

互联网安全架构设计

前言一、如何防御xss攻击?二、抓包如何防止篡改数据?三、对接口实现加密四、相关安全架构设计方案说明


前言

本文是对Java开发做安全的架构设计。


一、如何防御xss攻击?

攻击场景说明:在客户端发起请求时,如果URL的请求参数被篡改为网页脚本的话,而且后台没有对参数做处理的话,在当前页面中会受到恶意攻击。
如何解决:在后台编写一个过滤器,对后台接收到的请求参数做过滤处理。
代码说明:

@Component
@WebFilter
public class XssFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //重写该方法(对接收的参数做处理)
        XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(xssHttpServletRequestWrapper, response);
    }

    @Override
    public void destroy() {

    }
}

```java
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private HttpServletRequest request;

    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        this.request = request;
    }

    @Override
    //重写该方法,对接收到的参数做过滤处理
    public String getParameter(String name) {
        String oldValue = super.getParameter(name);
        if(StringUtils.isEmpty(oldVa[添加链接描述](https://pc.qq.com/detail/10/detail_3330.html)lue)){
            return oldValue;
        }
        String newValue = StringEscapeUtils.escapeHtml4(oldValue);
        return newValue;
    }
    
}
二、抓包如何防止篡改数据?

攻击场景说明:可以使用第三方抓包工具,对请求前后实现代理,可以修改参数请求内容和参数响应内容。
Fiddler4抓包工具下载地址:

https://pc.qq.com/detail/10/detail_3330.html  

使用Fiddler4篡改请求之前:



解决方案:
使用MD5对参数做加密处理,防止参数被篡改
使用MD5可以直接验证签名参数 MD5 属于单向加密,只能够暴力破解。
MD5应用场景:在nacos分布式配置中心中,使用MD5 比对文件内容是否发生改变

MD5在线暴力破解地址:https://www.cmd5.com/

三、对接口实现加密

攻击场景说明:对接口实现对称加密,抓包的时候看不到明文的数据,但是可以被破解,因为客户端与服务器都是采用同一个密钥实现加解密,可以通过反编译客户端代码得出密钥实现破解。
对称加密

    加密和解密都是同一个秘钥优势:算法公开、加密快、效率高。缺点:可以采用反编译破解客户端真实秘钥

常用对称加密框架:
1.DES(Data Encryption Standard)
2.3DES(DES加强版,使用3次DES计算,Triple DES,DESede)
3.AES(Advanced Encryption Standard,3DES加强版)

工具类代码如下:

public class DESUtil {

    // 密钥,是加密解密的凭据,长度为8的倍数
    private static String PASSWORD_CRYPT_KEY = "12345678";
    private final static String DES = "DES";

    
    public static byte[] encrypt(byte[] src, byte[] key) throws Exception {

        // DES算法要求有一个可信任的随机数源
        SecureRandom sr = new SecureRandom();

        // 从原始密匙数据创建DESKeySpec对象
        DESKeySpec dks = new DESKeySpec(key);

        // 创建一个密匙工厂,然后用它把DESKeySpec转换成
        // 一个SecretKey对象
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);

        SecretKey securekey = keyFactory.generateSecret(dks);

        // Cipher对象实际完成加密操作
        Cipher cipher = Cipher.getInstance(DES);

        // 用密匙初始化Cipher对象
        cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);

        // 现在,获取数据并加密
        // 正式执行加密操作
        return cipher.doFinal(src);

    }

    
    public static byte[] decrypt(byte[] src, byte[] key) throws Exception {

        // DES算法要求有一个可信任的随机数源
        SecureRandom sr = new SecureRandom();
        // 从原始密匙数据创建一个DESKeySpec对象
        DESKeySpec dks = new DESKeySpec(key);

        // 创建一个密匙工厂,然后用它把DESKeySpec对象转换成
        // 一个SecretKey对象
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);

        SecretKey securekey = keyFactory.generateSecret(dks);

        // Cipher对象实际完成解密操作
        Cipher cipher = Cipher.getInstance(DES);

        // 用密匙初始化Cipher对象
        cipher.init(Cipher.DECRYPT_MODE, securekey, sr);

        // 现在,获取数据并解密
        // 正式执行解密操作
        return cipher.doFinal(src);

    }

    
    public final static String decrypt(String data) {

        try {
            return new String(decrypt(hex2byte(data.getBytes()), PASSWORD_CRYPT_KEY.getBytes()));
        } catch (Exception e) {

        }
        return null;
    }

    
    public final static String encrypt(String password) {
        try {
            return byte2hex(encrypt(password.getBytes(), PASSWORD_CRYPT_KEY.getBytes()));
        } catch (Exception e) {

        }
        return null;
    }

    
    public static String byte2hex(byte[] b) {

        String hs = "";
        String stmp = "";
        for (int n = 0; n < b.length; n++) {
            stmp = (java.lang.Integer.toHexString(b[n] & 0xFF));
            if (stmp.length() == 1)
                hs = hs + "0" + stmp;
            else
                hs = hs + stmp;
        }
        return hs.toUpperCase();
    }

    public static byte[] hex2byte(byte[] b) {

        if ((b.length % 2) != 0)
            throw new IllegalArgumentException("长度不是偶数");
        byte[] b2 = new byte[b.length / 2];
        for (int n = 0; n < b.length; n += 2) {
            String item = new String(b, n, 2);
            b2[n / 2] = (byte) Integer.parseInt(item, 16);
        }
        return b2;
    }

    // 测试用例,不需要传递任何参数,直接执行即可。
    public static void main(String[] args) {
        String basestr = "mayikt";
        String str1 = encrypt(basestr);
        System.out.println("原始值: " + basestr);
        System.out.println("加密后: " + str1);
        System.out.println("解密后: " + decrypt(str1));
    }

}


非对称加密
公钥和私钥互换,生成一对秘钥对,分别为公钥和私钥。

    公钥加密,必须私钥解密私钥加密,必须公钥解密
    所以在很多支付接口中为了保证安全性,客户端采用公钥加密,服务器端采用私钥解密,就算别人客户端的公钥,也无法解密,因为解密数据必须要私钥,而私钥存放在服务器端。
    最为著名的就是RSA

工具类代码如下:

import org.apache.commons.codec.binary.base64;
import org.apache.tomcat.util.http.fileupload.IOUtils;


import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;


public class RSAUtils {
    public static final String CHARSET = "UTF-8";
    public static final String RSA_ALGORITHM = "RSA"; // ALGORITHM ['ælgərɪð(ə)m] 算法的意思

    public static Map createKeys(int keySize) {
        // 为RSA算法创建一个KeyPairGenerator对象
        KeyPairGenerator kpg;
        try {
            kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
        }

        // 初始化KeyPairGenerator对象,密钥长度
        kpg.initialize(keySize);
        // 生成密匙对
        KeyPair keyPair = kpg.generateKeyPair();
        // 得到公钥
        Key publicKey = keyPair.getPublic();
        String publicKeyStr = base64.encodebase64URLSafeString(publicKey.getEncoded());
        // 得到私钥
        Key privateKey = keyPair.getPrivate();
        String privateKeyStr = base64.encodebase64URLSafeString(privateKey.getEncoded());
        // map装载公钥和私钥
        Map keyPairMap = new HashMap();
        keyPairMap.put("publicKey", publicKeyStr);
        keyPairMap.put("privateKey", privateKeyStr);
        // 返回map
        return keyPairMap;
    }

    
    public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 通过X509编码的Key指令获得公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(base64.decodebase64(publicKey));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        return key;
    }

    
    public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 通过PKCS#8编码的Key指令获得私钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(base64.decodebase64(privateKey));
        RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        return key;
    }

    
    public static String publicEncrypt(String data, RSAPublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return base64.encodebase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    

    public static String privateDecrypt(String data, RSAPrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, base64.decodebase64(data), privateKey.getModulus().bitLength()), CHARSET);
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    

    public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            //每个Cipher初始化方法使用一个模式参数opmod,并用此模式初始化Cipher对象。此外还有其他参数,包括密钥key、包含密钥的证书certificate、算法参数params和随机源random。
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            return base64.encodebase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength()));
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    

    public static String publicDecrypt(String data, RSAPublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, base64.decodebase64(data), publicKey.getModulus().bitLength()), CHARSET);
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    //rsa切割解码  , ENCRYPT_MODE,加密数据   ,DECRYPT_MODE,解密数据
    private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
        int maxBlock = 0;  //最大块
        if (opmode == Cipher.DECRYPT_MODE) {
            maxBlock = keySize / 8;
        } else {
            maxBlock = keySize / 8 - 11;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] buff;
        int i = 0;
        try {
            while (datas.length > offSet) {
                if (datas.length - offSet > maxBlock) {
                    //可以调用以下的doFinal()方法完成加密或解密数据:
                    buff = cipher.doFinal(datas, offSet, maxBlock);
                } else {
                    buff = cipher.doFinal(datas, offSet, datas.length - offSet);
                }
                out.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        } catch (Exception e) {
            throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
        }
        byte[] resultDatas = out.toByteArray();
        IOUtils.closeQuietly(out);
        return resultDatas;
    }


    // 简单测试____________
    public static void main(String[] args) throws Exception {
        Map keyMap = RSAUtils.createKeys(1024);
        String publicKey = keyMap.get("publicKey");
        String privateKey = keyMap.get("privateKey");
        System.out.println("公钥: nr" + publicKey);
        System.out.println("私钥: nr" + privateKey);
        System.out.println("公钥加密——私钥解密");
        String str="mayikt";
        String encodedData = RSAUtils.publicEncrypt(str, RSAUtils.getPublicKey(privateKey));  //传入明文和公钥加密,得到密文
        System.out.println("密文:rn" + encodedData);
        String decodedData = RSAUtils.privateDecrypt(encodedData, RSAUtils.getPrivateKey(publicKey)); //传入密文和私钥,得到明文
        System.out.println("解密后文字: rn" + decodedData);

    }

}


注意:在Http协议通讯过程中,如果传递特殊字符的情况下,比如:+!等特殊字符统一会变为空格。所以注意需要对url参数实现UrlEncode

四、相关安全架构设计方案说明
    使用api网关过滤器对防止xss、sql注入问题
    对我们的接口实现对称加密,抓包的时候看不到明文的数据,但是可以被破解,因为客户端与服务器都是采用同一个密钥实现加解密,可以通过反编译客户端代码得出密钥实现破解。 Des、aes使用非对称加密RSA 公钥和私钥互换机制,客户端使用公钥实现加密,服务器端采用私钥实现解密,就算黑客破解出公钥也无法对数据实现解密。
    一对密钥 两个密钥 公钥 和私
    对称加解密速度比rsa要快,但是不安全。可以采用MD5对我们的参数实现验证签名,但是数据还是传输明文,可以防止
    篡改数据。互联网电商项目都会采用Https协议 ssl+证书 加密传输 默认443 而我们的Http协议采用明文实现对数据传输,效率比较Https要高,但是不安全。对我们的api接口实现黑名单和白名单控制对我们接口实现服务保护、限流、熔断使用图形验证码防止机器模拟请求对我们代码使用专门扫描工具实现检测漏洞在rpc传递参数过程中使用令牌隐藏真实的参数。数据库表权限框架设计
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/770882.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号