# 加密与安全 ## 问题 1. 对称加密和非对称加密的区别是什么? 2. Base64 是加密吗?为什么? 3. 什么是数字签名?如何防止数据篡改? 4. HTTPS 中的 TLS 握手过程是怎样的? 5. 什么是中间人攻击?如何防范? 6. SQL 注入和 XSS 攻击的原理和防范措施? 7. 密码存储的最佳实践是什么? 8. 在实际项目中如何保证数据安全? --- ## 标准答案 ### 1. 对称加密 vs 非对称加密 #### **对称加密** **特点**:加密和解密使用同一个密钥。 ``` 加密:明文 + 密钥 → 密文 解密:密文 + 密钥 → 明文 ``` **常见算法**: - **AES**(Advanced Encryption Standard):推荐 - **DES**(Data Encryption Standard):已过时(密钥太短) - **3DES**:DES 的改进,但速度慢 - **RC4**:已不安全 **Java 示例**: ```java import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import java.util.Base64; public class AESExample { public static void main(String[] args) throws Exception { // 生成密钥 KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); // AES-256 SecretKey secretKey = keyGen.generateKey(); // 加密 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encrypted = cipher.doFinal("Hello World".getBytes()); System.out.println("加密:" + Base64.getEncoder().encodeToString(encrypted)); // 解密 cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decrypted = cipher.doFinal(encrypted); System.out.println("解密:" + new String(decrypted)); } } ``` **优点**: - 速度快(比非对称加密快 100-1000 倍) - 适合加密大量数据 **缺点**: - 密钥分发困难(如何安全地共享密钥?) --- #### **非对称加密** **特点**:加密和解密使用不同的密钥(公钥和私钥)。 ``` 公钥加密:明文 + 公钥 → 密文 私钥解密:密文 + 私钥 → 明文 或者: 私钥签名:明文 + 私钥 → 签名 公钥验签:签名 + 公钥 → 验证 ``` **常见算法**: - **RSA**:最常用 - **ECC**(椭圆曲线加密):密钥更短,速度更快 - **DSA**:数字签名算法 **Java 示例**: ```java import java.security.*; import javax.crypto.Cipher; import java.util.Base64; public class RSAExample { public static void main(String[] args) throws Exception { // 生成密钥对 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); KeyPair keyPair = keyGen.generateKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); // 公钥加密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] encrypted = cipher.doFinal("Hello World".getBytes()); System.out.println("加密:" + Base64.getEncoder().encodeToString(encrypted)); // 私钥解密 cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decrypted = cipher.doFinal(encrypted); System.out.println("解密:" + new String(decrypted)); } } ``` **优点**: - 无需安全分发密钥(公钥可以公开) - 可用于数字签名 **缺点**: - 速度慢(比对称加密慢 100-1000 倍) - 不适合加密大量数据 --- #### **混合加密(HTTPS 使用)** ``` 1. 使用非对称加密协商对称密钥 2. 使用对称密钥加密数据 ``` **优点**: - 结合两者优点(安全 + 速度) --- ### 2. Base64 是加密吗? **答案:不是!** **Base64 是编码,不是加密。** **目的**: - 将二进制数据转换为 ASCII 字符串 - 方便在文本协议中传输(如 HTTP、Email) **原理**: ``` 二进制:01001000 0110101 01011011 Base64:SGVsbG8= 编码表(64 个字符): A-Z (26) + a-z (26) + 0-9 (10) + + / (2) = 64 ``` **特点**: - 可逆(可解码) - 无密钥(任何人都能解码) - 数据会增大 33% **示例**: ```java import java.util.Base64; public class Base64Example { public static void main(String[] args) { String original = "Hello World"; // 编码 String encoded = Base64.getEncoder().encodeToString(original.getBytes()); System.out.println("编码:" + encoded); // SGVsbG8gV29ybGQ= // 解码 String decoded = new String(Base64.getDecoder().decode(encoded)); System.out.println("解码:" + decoded); // Hello World } } ``` **错误用法**: ```java // ❌ 用 Base64 存储密码(不安全) String password = "123456"; String encoded = Base64.getEncoder().encodeToString(password.getBytes()); // 存储到数据库 // 攻击者可以直接解码! ``` --- ### 3. 数字签名 #### **原理** 数字签名用于验证: 1. **完整性**:数据未被篡改 2. **身份认证**:发送者确实是声称的人 3. **不可抵赖**:发送者无法否认发送过 **流程**: ``` 发送方(签名): 1. 对原文计算哈希:hash = SHA256(原文) 2. 用私钥加密哈希:signature = RSA_Encrypt(hash, 私钥) 3. 发送:原文 + signature 接收方(验签): 1. 对原文计算哈希:hash1 = SHA256(原文) 2. 用公钥解密签名:hash2 = RSA_Decrypt(signature, 公钥) 3. 对比 hash1 和 hash2 - 相同 → 签名有效 - 不同 → 数据被篡改 ``` --- #### **Java 实现** ```java import java.security.*; import java.util.Base64; public class DigitalSignatureExample { public static void main(String[] args) throws Exception { // 生成密钥对 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); KeyPair keyPair = keyGen.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); PublicKey publicKey = keyPair.getPublic(); // 待签名数据 String data = "重要合同内容"; // 签名 Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(privateKey); signature.update(data.getBytes()); byte[] signatureBytes = signature.sign(); System.out.println("签名:" + Base64.getEncoder().encodeToString(signatureBytes)); // 验签 signature.initVerify(publicKey); signature.update(data.getBytes()); boolean isValid = signature.verify(signatureBytes); System.out.println("验签结果:" + isValid); } } ``` --- #### **实际应用** 1. **软件签名**:验证软件来源 2. **代码签名**:防止代码被篡改 3. **PDF 签名**:电子合同 4. **JWT(JSON Web Token)**:API 认证 ```java // JWT 示例 String token = Jwts.builder() .setSubject("user123") .signWith(Keys.hmacShaKeyFor(secretKey), SignatureAlgorithm.HS256) .compact(); // 验证 JWT Claims claims = Jwts.parserBuilder() .setSigningKey(secretKey) .build() .parseClaimsJws(token) .getBody(); ``` --- ### 4. SQL 注入攻击 #### **原理** 攻击者在输入中注入恶意 SQL 代码。 **示例**: ```java // ❌ 不安全的代码(拼接 SQL) String username = request.getParameter("username"); String password = request.getParameter("password"); String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'"; // 攻击者输入: username = "admin' OR '1'='1"; password = "anything" // 实际执行的 SQL: SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = 'anything' // 结果:永远为真,绕过密码验证! ``` **危害**: - 绕过认证 - 窃取数据 - 删除数据 - 提升权限 --- #### **防范措施** **1. 使用预编译语句(PreparedStatement)**: ```java // ✅ 安全代码 String sql = "SELECT * FROM users WHERE username = ? AND password = ?"; PreparedStatement stmt = connection.prepareStatement(sql); stmt.setString(1, username); stmt.setString(2, password); ResultSet rs = stmt.executeQuery(); ``` **原理**:参数化查询,SQL 结构固定,参数不会被解析为 SQL 代码。 --- **2. 输入验证**: ```java // 验证用户名格式 if (!username.matches("^[a-zA-Z0-9_]{3,20}$")) { throw new IllegalArgumentException("用户名格式不正确"); } ``` --- **3. 最小权限原则**: ```sql -- 应用用户只授予必要权限 GRANT SELECT, INSERT, UPDATE ON app_db.* TO 'app_user'@'localhost'; -- 不要授予 DROP、ALTER 等危险权限 ``` --- **4. 使用 ORM**: ```java // JPA/Hibernate 自动防止 SQL 注入 User user = userRepository.findByUsernameAndPassword(username, password); ``` --- ### 5. XSS 攻击(跨站脚本攻击) #### **原理** 攻击者在网页中注入恶意 JavaScript 代码。 **示例**: ```html ``` **危害**: - 窃取 Cookie - 会话劫持 - 重定向到钓鱼网站 - 篡改网页内容 --- #### **防范措施** **1. 输出转义**: ```java // 使用 Spring 的 HTML 转义 import org.springframework.web.util.HtmlUtils; String userInput = ""; String escaped = HtmlUtils.htmlEscape(userInput); // 输出:<script>alert('XSS')</script> ``` --- **2. CSP(Content Security Policy)**: ```http # HTTP 响应头 Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' ``` --- **3. HttpOnly Cookie**: ```java // 设置 Cookie 为 HttpOnly(JavaScript 无法访问) Cookie cookie = new Cookie("session", token); cookie.setHttpOnly(true); cookie.setSecure(true); // 仅 HTTPS 传输 response.addCookie(cookie); ``` --- **4. 输入验证**: ```java // 白名单验证 if (!comment.matches("[a-zA-Z0-9 \\u4e00-\\u9fa5]+")) { throw new IllegalArgumentException("评论包含非法字符"); } ``` --- ### 6. 密码存储 #### **错误做法** ```java // ❌ 明文存储 password = "123456"; // ❌ Base64 编码(可逆) password = Base64.getEncoder().encodeToString("123456".getBytes()); // ❌ MD5 哈希(易被彩虹表破解) password = DigestUtils.md5Hex("123456"); // e10adc3949ba59abbe56e057f20f883e ``` --- #### **正确做法:BCrypt** **特点**: - 自动加盐(Salt) - 慢哈希(防暴力破解) - 可调整计算成本 ```java import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; public class PasswordExample { public static void main(String[] args) { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); // 加密(自动加盐) String rawPassword = "123456"; String encodedPassword = encoder.encode(rawPassword); System.out.println("加密后:" + encodedPassword); // 输出:$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy // 验证 boolean matches = encoder.matches(rawPassword, encodedPassword); System.out.println("验证结果:" + matches); // true } } ``` --- #### **密码存储最佳实践** 1. **使用慢哈希算法**: - BCrypt(推荐) - Argon2(最新) - PBKDF2 - Scrypt 2. **每个密码独立的盐**: ```java salt = random_bytes(16); hashed_password = hash(password + salt); ``` 3. **调整计算成本**: ```java // BCrypt 成本参数(10-12 为宜) BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12); ``` 4. **不要自己发明加密算法** --- ### 7. HTTPS 完整流程 ``` 客户端 服务器 │ │ │─────── ClientHello ────────────→│ │ (支持的加密套件、随机数1) │ │ │ │←────── ServerHello ─────────────│ │ (选择的加密套件、随机数2、证书) │ │←────── Certificate ─────────────│ │ │ │─────── 验证证书 ────────────────│ │ (检查 CA 签名、有效期等) │ │ │ │─────── 生成随机数3 ─────────────→│ │─────── 用公钥加密随机数3 ─────────→│ │ │ │ 服务器用私钥解密 │ 生成会话密钥: │ master_secret = PRF(随机数1, 随机数2, 随机数3) │ │ │←─────── ChangeCipherSpec ────────│ │←─────── Finished ────────────────│ │ (用会话密钥加密,证明安全) │ │ │ │─────── ChangeCipherSpec ─────────→│ │─────── Finished ────────────────→│ │ (用会话密钥加密) │ │ │ │══════ 加密通信 ══════════════════│ │ (使用会话密钥对称加密) │ ``` --- ### 8. 中间人攻击 #### **原理** 攻击者拦截并可能篡改通信双方的通信内容。 ``` 客户端 攻击者 服务器 │ │ │ ├─── HTTPS 请求 ────→│ │ │ ├─── 伪造请求 ───────→│ │ │ │ │←──── 伪造响应 ──────│←──── 真实响应 ──────│ │←────────────────────│ │ ``` --- #### **防范措施** **1. HTTPS + 证书验证**: - 验证服务器证书 - 检查证书链 - 验证证书有效期 **2. HSTS(HTTP Strict Transport Security)**: ```http Strict-Transport-Security: max-age=31536000; includeSubDomains ``` **3. 证书固定(Certificate Pinning)**: ```java // 移动应用中固定证书 public class CertificatePinner { private static final String KNOWN_CERT = "SHA256:AAAAAAAAA..."; public void verifyCertificate(X509Certificate cert) { String certHash = calculateCertificateHash(cert); if (!certHash.equals(KNOWN_CERT)) { throw new SSLException("证书不匹配"); } } } ``` --- ### 9. 实际项目安全实践 #### **安全检查清单** - [ ] 所有用户输入都经过验证和转义 - [ ] 密码使用 BCrypt 存储且每次加不同的盐 - [ ] 敏感数据在传输时使用 HTTPS 加密 - [ ] 敏感数据在存储时使用 AES 加密 - [ ] API 使用 JWT 或 OAuth2 认证 - [ ] 实施 CSRF 防护(CSRF Token) - [ ] 实施 CORS 限制 - [ ] 设置安全响应头(CSP、X-Frame-Options 等) - [ ] 定期进行安全审计和渗透测试 - [ ] 使用依赖扫描工具检查漏洞 --- ### 10. 阿里 P7 加分项 **深度理解**: - 理解各种加密算法的数学原理 - 理解 TLS 1.3 的改进 - 理解零知识证明等高级加密技术 **实战经验**: - 有处理线上安全漏洞的经验 - 有设计安全架构的经验 - 有加密性能优化的经验 **架构能力**: - 能设计安全的认证和授权体系 - 能设计数据加密方案 - 能制定安全开发规范 **合规要求**: - 了解 GDPR、等保等安全合规要求 - 有安全审计和风险评估经验