文章目录
一、网络安全
1、网络安全概述
(1)通常的网络安全有SQL注入,XSS脚本攻击,CRSF伪脚本攻击,DDOS拒绝服务攻击。
(2)SQL注入
所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
预防方式:
- 使用预编译
- 使用ORM框架
- 执行sql语句是使用addslashes进行sql语句转换
- sql语句书写尽量不要省略双引号和单引号
- 过滤掉sql语句里的关键词 :update insert delete select *
- PHP配置文件中设置register_global为off,关闭全局变量注册
- 控制错误信息 不要把错误信息输出到浏览器上 应保存在错误日
(3)XSS攻击
XSS攻击的全称是跨站脚本攻击,指的是攻击者在网页中嵌入恶意脚本程序,如<script>alert(1111)</script>
。当用户打开该网页时,脚本程序便开始在客户端的浏览器上执行,以盗取客户端cookie,用户名密码,下载执行病毒密码程序,甚至是获取客户端admin权限等。
预防方式:
- 在前端进行输入验证,如果发现类似脚本程序的输入,就将其转义。
- 在后端接收到数据的时候进行验证和转义。
(4) CRSF攻击
CRSF攻击的全称是跨站请求伪造,是一种对网站的恶意利用,XSS利用的是站点内的信任用户,而CRSF则是通过伪装来自受信任用户的请求来利用受信任的网站。
预防方式:
- Cookie Hashing(所有表单都包含同一个伪随机值):
- 验证码
- One-Time Tokens(不同的表单包含一个不同的伪随机值)
(5)DDOS拒绝服务攻击
DDOS流量攻击
预防方式:
- 使用工具:DDOS deflate . 自动查封IP.e
- 析域名到127.0.0.1 让攻击方自己攻击自己
- 把网站做成静态页面
- 制S YN/ICMP流量
- 通过提高服务端的配置,比如:宽带,并发量
- 在服务端限制第二次握手的时间,如果时间超时,就直接把这个连接断开,并从链接列表中清除相关信息。
2、加密,系统安全措施
(1)加密三种方式:不可逆加密、对称加密、不对称加密
(2)不可逆加密
不可逆加密的加密结果没法解密,典型的有:MD5、sha256、HMAC加密
① MD5加密
- 不可逆运算,对不同的数据加密的结果是定长的32位字符(不管文件多大都一样),对相同的数据加密,得到的结果是一样的(也就是复制)。
- 抗修改性 : 信息“指纹”,对原数据进行任何改动,哪怕只修改一个字节,所得到的 MD5 值都有很大区别.
- 弱抗碰撞 : 已知原数据和其 MD5 值,想找到一个具有相同 MD5 值的数据(即伪造数据)是非常困难的.
- 强抗碰撞: 想找到两个不同数据,使他们具有相同的 MD5 值,是非常困难的
public class EncUtils {
public static String MD5Enc(String password) {
return DigestUtils.md5Hex(password);
}
}
如果想要加密更深,可以用循环再加密。
public class EncUtils {
public static String MD5Enc(String password) {
for(int i = 0; i < 1024; i++) {
password = DigestUtils.md5Hex(password);
}
return password;
}
}
② sha256加密
- 安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。
- 对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。当让除了SHA1还有SHA256以及SHA512等。
- SHA1特性:
- 不可以从消息摘要中复原信息;
- 两个不同的消息不会产生同样的消息摘要。
public class EncUtils {
public static String sha256Enc(String password) {
for(int i = 0; i < 1024; i++) {
password = DigestUtils.sha256Hex(password);
}
return password;
}
}
③ HMAC加密
- HMAC:给定一个密钥,对明文加密,做两次“散列”,得到的结果还是32为字符串。在实际开发中,密钥是服务器生成,客户端发送请求会拿到KEY,一个账号对应一个KEY。
- 以注册为例:当用户把账号提交给服务器,服务器会验证账号的合法性,如果合法就会生成个KEY给客户端(这个KEY只有在注册的时候会出现一次,一个账号只对应一个KEY);客户端会用拿到的KEY给密码用HMAC方式加密(32位字符串)发给服务器,最终服务器会保存这个HMAC密码。这样就注册成功了!以后再登录就会服务器就会比对这个HMAC密码是否相等决定能否登录成功。
(3)对称加密
对称加密算法又称传统加密算法,对称加密跟解密使用的是相同的密钥,加密解密过程:明文->密钥加密->密文,密文->密钥解密->明文
。典型的有:DES(80年代)、3DES、AES(目前常用)(相对不安全)
① DES(Data Encryption Standard)
数据加密标准(现在用的比较少,因为它的加密强度不够,能够暴力破解) 。
② 3DES
原理和DES几乎是一样的,只是使用3个密钥,对相同的数据执行三次加密,增强加密强度。(缺点:要维护3个密钥,大大增加了维护成本) 。
③ AES(Advanced Encryption Standard)
- AES是高级加密标准,目前美国国家安全局使用的,苹果的钥匙串访问采用的就AES加密。是现在公认的最安全的加密方式,是对称密钥加密中最流行的算法。
- AES的安全性相对RSA较低,但是效率比RAS高,AES应用于https协议的ssl协议的底层实现。
AES加密Demo
AES解密工具 AESUtils 类
public class AESUtils {
//密钥 (需要前端和后端保持一致)
private static final String KEY = "abcdefgabcdefg12";
//算法,ECB定义AES的计算模式,加密算法标准
private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";
//aes解密
public static String decr(String content) {
byte[] mes = null;
try {
@SuppressWarnings("restriction")
byte[] base64Content = new BASE64Decoder().decodeBuffer(content);
Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
cipher.init(Cipher.DECRYPT_MODE,new SecretKeySpec(KEY.getBytes(),"AES"));
mes = new byte[0];
mes = cipher.doFinal(base64Content);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return new String(mes);
}
}
控制层 UsersController 类
@RestController
public class UsersController {
@Resource
private UsersService us;
@GetMapping("login")
public String select(String userName, String userPassword) throws Exception {
System.out.println("AES解密前:" + userPassword);
userPassword = AESUtils.decr(userPassword);
System.out.println("AES解密后:" + userPassword);
System.out.println("userName=" + userName);
//sha256加密
// userPassword = EncUtils.sha256Enc(userPassword);
//MD5加密
userPassword = EncUtils.MD5Enc(userPassword);
String str = null;
Users users = us.selectByUserName(userName);
if(users != null) {
if(users.getUserPassword().equals(userPassword)) {
str = "success";
}else {
str = "error";
}
}else {
str = "账号不存在";
}
return str;
}
}
前端发送请求网页
<body>
<form id="login">
<input name="userName" id="userName"><br>
<input type="password" name="userPassword" id="userPassword"><br>
<input type="button" value="登录" onclick="login()">
</form>
<script type="text/javascript">
function login(){
var userName = $("#userName").val();
var userPassword = $("#userPassword").val();
console.log("AES加密前:" + userPassword);
/* 执行加密方法 */
userPassword = encrypt(userPassword);
console.log("AES加密后:" + userPassword);
$.ajax({
url:"/login",
type:"get",
data:{"userName":userName, "userPassword":userPassword},
success:function(data){
console.log(data);
if(data == "success"){
location.href="success.html";
}else if(data == "error"){
location.href="error.html";
}
}
});
}
/*加密方法(需要先加载aes.js文件)*/
function encrypt(word){
var key = CryptoJS.enc.Utf8.parse("abcdefgabcdefg12");
var srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
return encrypted.toString();
}
/* 解密方法*/
function decrypt(word){
var key = CryptoJS.enc.Utf8.parse("abcdefgabcdefg12");
var decrypt = CryptoJS.AES.decrypt(word, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
return CryptoJS.enc.Utf8.stringify(decrypt).toString();
}
</script>
</body>
一份比较完整的AES加密工具类
/**
*AES加密解密工具类
*/
public class AESUtils {
//密钥 (需要前端和后端保持一致)
private static final String KEY = "abcdefgabcdefg12";
//算法,ECB定义AES的计算模式,加密算法标准
private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";
//aes解密
public static String decr(String content) {
byte[] mes = null;
try {
@SuppressWarnings("restriction")
byte[] base64Content = new BASE64Decoder().decodeBuffer(content);
Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
cipher.init(Cipher.DECRYPT_MODE,new SecretKeySpec(KEY.getBytes(),"AES"));
mes = new byte[0];
mes = cipher.doFinal(base64Content);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return new String(mes);
}
/**
* aes解密
* @param encrypt 内容
* @return
* @throws Exception
*/
public static String aesDecrypt(String encrypt) {
try {
return aesDecrypt(encrypt, KEY);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
/**
* aes加密
* @param content
* @return
* @throws Exception
*/
public static String aesEncrypt(String content) {
try {
return aesEncrypt(content, KEY);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
/**
* base 64 encode
* @param bytes 待编码的byte[]
* @return 编码后的base 64 code
*/
public static String base64Encode(byte[] bytes){
return Base64.encodeBase64String(bytes);
}
/**
* base 64 decode
* @param base64Code 待解码的base 64 code
* @return 解码后的byte[]
* @throws Exception
*/
@SuppressWarnings("restriction")
public static byte[] base64Decode(String base64Code) throws Exception{
return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code);
}
/**
* AES加密
* @param content 待加密的内容
* @param encryptKey 加密密钥
* @return 加密后的byte[]
* @throws Exception
*/
public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));
return cipher.doFinal(content.getBytes("utf-8"));
}
/**
* AES加密为base 64 code
* @param content 待加密的内容
* @param encryptKey 加密密钥
* @return 加密后的base 64 code
* @throws Exception
*/
public static String aesEncrypt(String content, String encryptKey) throws Exception {
return base64Encode(aesEncryptToBytes(content, encryptKey));
}
/**
* AES解密
* @param encryptBytes 待解密的byte[]
* @param decryptKey 解密密钥
* @return 解密后的String
* @throws Exception
*/
public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
// KeyGenerator kgen = KeyGenerator.getInstance("AES");
// kgen.init(128);
Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES"));
byte[] decryptBytes = cipher.doFinal(encryptBytes);
return new String(decryptBytes);
}
/**
* 将base 64 code AES解密
* @param encryptStr 待解密的base 64 code
* @param decryptKey 解密密钥
* @return 解密后的string
* @throws Exception
*/
public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {
return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
}
}
(4)不对称加密
- 非对称加密算法又称现代加密算法,非对称加密是计算机通信安全的基石,保证了加密数据不会被破解,典型的有RSA(相对安全)。
- 非对称加密算法需要两个密钥:公开密钥(publickey) 和私有密(privatekey) ,公开密钥和私有密钥是一对
- 如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密。
- 如果用私有密钥对数据进行加密,只有用对应的公开密钥才能解密。
- 特点:
- 算法强度复杂,安全性依赖于算法与密钥。
- 加密解密速度慢。
- 与对称加密算法的对比:
- 对称加密只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。
- 非对称加密有两种密钥,其中一个是公开的。
① RSA加密
- 由于RSA算法的加密解密速度要比对称算法速度慢很多,在实际应用中,通常采取数据本身的加密和解密使用对称加密算法(AES),用RSA算法加密并传输对称算法所需的密钥。
RSA加密Demo:RSA+时间戳完成登录验证
控制层 UsersController 类
@RestController
public class UsersController {
//记录日志
private static final Logger log = LoggerFactory.getLogger(UsersController.class);
//私钥
private static final String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJX/0JqAPr0mdq9ZN0XqYEVWYApf\r\n" +
"EvoNkmzN0tgQ2CrI0Z8rcmw0CiPH1m1Rp0I/DqiA7XTbqQQMpLLjpUGs11MdLnWvhYqJ04CTK7Bm\r\n" +
"0OwqtIUYxnwPcfGjoOYI2tFB6xWqp6QkmqcF8NAKf8P5kL6L5mXLpI/+6hqlqiFNzM13AgMBAAEC\r\n" +
"gYBgx0ikJpJUH/j7caiD55BtrCkTB/yKcS9EHBgpZ9TQ5fi5paKRxZG1PD5REhnEWELw3R5YA1Gt\r\n" +
"Vyr8cHwzP309GaQVrdrmp5/NnnbuR9Eq7uw9CM+Qovs1Elg77DMh68PNoCbunDsrcOM03+Qm56EL\r\n" +
"ZqilosDyuf+T/uTMkK/OAQJBAM6Cg1c9xrKzcc/vf8c0dnfslSWGJSrA+0vwVJ2Tpb3zO/K4q9OT\r\n" +
"MTlde5QAvDAyBzcebHYcTgtN5wqq5bcLU8ECQQC58lmkx2kn5h9i2OtMFuKgdB8xDMvlOZhrOZ+n\r\n" +
"+Na2qyVr9fO4Zs4koy54t82FdxvVl5YSSifgRVi9Wzy31Y83AkBbndVNGBAV6bxdZsCGjQTd8Wy9\r\n" +
"yASnMhSKvSWc/wrx+X4oEbvRUqTs8UdJAwZ6T0eaMXom8CnDpB0TQ3xEN9dBAkBdGAtvZvKrqheu\r\n" +
"mmF7L734jQqMuRZfxe0cSbY4F2oP/DF5EqMZgEbWquf6QjNgDZje38yfjYbXToC2FLaaZw4TAkAG\r\n" +
"6rBZ4zdmOfkjRRJEpHKZ6ZKlqXW9oe+vqQPUBmBD6wKeBxE2ztdHhhjL4Jit87dxjXOLtP3DsZ0s\r\n" +
"vdKA/Eme";
@Resource
private UsersService us;
@GetMapping("login")
public String select(String userName, String userPassword) throws Exception {
System.out.println("私钥解密前:" + userPassword);
//获取私钥
PrivateKey priKey = RSAUtils.getPrivateKey(privateKey);
userPassword = userPassword.replaceAll(" ", "+");
//解密
userPassword = RSAUtils.decrypt(userPassword, priKey);
System.out.println("私钥解密后:" + userPassword);
//拆分密钥和时间戳
String time = userPassword.substring(userPassword.lastIndexOf("+") + 1);
userPassword = userPassword.substring(0, userPassword.lastIndexOf("+"));
System.out.println("userPassword =" +userPassword);
System.out.println("time =" +time);
long timeBefore = Long.parseLong(time);
//获取当前时间戳
long timeNow = System.currentTimeMillis();
System.out.println(timeBefore +" " + timeNow);
Users users = us.selectByUserName(userName);
String str = null;
if(users != null) {
//MD5加密
userPassword = EncUtils.MD5Enc(userPassword);
if(users.getUserPassword().equals(userPassword)) {
if((timeNow - timeBefore) < 1000) {
str = "success";
}else {
str = "登录超时";
}
}else {
str = "error";
}
}else {
str = "账号不存在";
}
return str;
}
}
前端发送请求代码
<body>
<form id="login">
<input name="userName" id="userName"><br>
<input name="userPassword" id="userPassword"><br>
<input type="button" value="登录" onclick="login()">
</form>
<script type="text/javascript">
function login(){
var userName = $("#userName").val();
var userPassword = $("#userPassword").val();
console.log("公钥加密前:" + userPassword);
/* 定义公钥 */
var publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCV/9CagD69JnavWTdF6mBFVmAKXxL6DZJszdLY" +
"ENgqyNGfK3JsNAojx9ZtUadCPw6ogO1026kEDKSy46VBrNdTHS51r4WKidOAkyuwZtDsKrSFGMZ8" +
"D3Hxo6DmCNrRQesVqqekJJqnBfDQCn/D+ZC+i+Zly6SP/uoapaohTczNdwIDAQAB";
//密码添加时间戳
var time = Date.parse(new Date());
time = time + 1000;
userPassword = userPassword + "+" + time;
console.log("userPassword:" + userPassword);
/* 执行公钥加密 */
var enctype = new JSEncrypt();//创建RSA对象
enctype.setPublicKey(publicKey);//为RSA对象设置公钥
userPassword = enctype.encrypt(userPassword);
console.log("公钥加密后:" + userPassword);
$.ajax({
url:"/login",
type:"get",
data:{"userName":userName, "userPassword":userPassword},
success:function(data){
alert(data);
if(data == "success"){
location.href="success.html";
}else if(data == "error"){
location.href="error.html";
}
}
});
}
}
</script>
</body>
获取公钥私钥字符串
KeyPair keyPair = RSAUtils.getKeyPair();
String publicKey = RSAUtils.getPublicKeyStr(keyPair);
String privateKey = RSAUtils.getPrivateKeyStr(keyPair);
System.out.println(publicKey);
System.out.println(privateKey);
SpringApplication.run(App.class, args);
RSA加密工具类
public class RSAUtils {
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 128;
//获取公钥字符串
public static String getPublicKeyStr(KeyPair keyPair){
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
byte[] by = publicKey.getEncoded();
return Base64.encode(by);
}
//获取私钥字符串
public static String getPrivateKeyStr(KeyPair keyPair){
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
byte[] by = privateKey.getEncoded();
return Base64.encode(by);
}
/**
* 获取密钥对
*
* @return 密钥对
*/
public static KeyPair getKeyPair() {
KeyPairGenerator generator = null;
try {
generator = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
generator.initialize(1024);
return generator.generateKeyPair();
}
/**
* 获取私钥
*
* @param privateKey 私钥字符串
* @return
*/
public static PrivateKey getPrivateKey(String privateKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.decode(privateKey.getBytes());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
return keyFactory.generatePrivate(keySpec);
}
/**
* 获取公钥
*
* @param publicKey 公钥字符串
* @return
*/
public static PublicKey getPublicKey(String publicKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.decode(publicKey.getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
return keyFactory.generatePublic(keySpec);
}
/**
* RSA加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return
*/
public static String encrypt(String data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
int inputLen = data.getBytes().length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offset = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offset > 0) {
if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
}
out.write(cache, 0, cache.length);
i++;
offset = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
// 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
// 加密后的字符串
return new String(Base64.decode(encryptedData));
}
/**
* RSA解密
*
* @param data 待解密数据
* @param privateKey 私钥
* @return
*/
public static String decrypt(String data, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] dataBytes = Base64.decode(data);
int inputLen = dataBytes.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offset = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offset > 0) {
if (inputLen - offset > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
}
out.write(cache, 0, cache.length);
i++;
offset = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
// 解密后的内容
return new String(decryptedData, "UTF-8");
}
/**
* 签名
*
* @param data 待签名数据
* @param privateKey 私钥
* @return 签名
*/
public static String sign(String data, PrivateKey privateKey) throws Exception {
byte[] keyBytes = privateKey.getEncoded();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey key = keyFactory.generatePrivate(keySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(key);
signature.update(data.getBytes());
return new String(Base64.decode(signature.sign()));
}
/**
* 验签
*
* @param srcData 原始字符串
* @param publicKey 公钥
* @param sign 签名
* @return 是否验签通过
*/
public static boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception {
byte[] keyBytes = publicKey.getEncoded();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(key);
signature.update(srcData.getBytes());
return signature.verify(Base64.decode(sign.getBytes()));
}
}