Backend

99 Ngày Spring — Ngày 97: Security Hardening cho Production

SSite Admin
1 tháng 11, 2026 3 phút đọc 0 lượt xem
99 Ngày Spring — Ngày 97: Security Hardening cho Production

Xác thực và phân quyền đúng mới là điều kiện cần. Hardening là phần còn lại: thu nhỏ bề mặt tấn công và giới hạn thiệt hại khi một lớp phòng thủ thất bại. Bài này gom bốn nhóm việc nên rà trước mỗi lần đưa một service Spring Boot ra Internet.

Security Hardening cho Production

1. Security header

Spring Security mặc định đã thêm một số header: X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Cache-Control chống cache nội dung riêng tư, và Strict-Transport-Security khi request đi qua HTTPS. Thứ bạn cần tự thêm là Content-Security-Policy, vì nó phụ thuộc vào tài nguyên mà giao diện của bạn nạp. Một CSP chặt là lớp phòng thủ hiệu quả trước XSS. Luôn chạy sau HTTPS và chuyển hướng mọi request HTTP.

@Bean
SecurityFilterChain security(HttpSecurity http) throws Exception {
    return http
        .headers(headers -> headers
            .contentSecurityPolicy(csp -> csp.policyDirectives(
                "default-src 'self'; frame-ancestors 'none'"))
            .referrerPolicy(referrer -> referrer.policy(
                ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)))
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/actuator/health/**").permitAll()
            .requestMatchers("/actuator/**").hasRole("OPS")
            .anyRequest().authenticated())
        .build();
}

2. Không lộ thông tin

Stack trace, tên bảng và phiên bản thư viện trong response lỗi là bản đồ cho kẻ tấn công. Trả về một thông điệp chung kèm mã tra cứu, còn chi tiết nằm trong log. Với Actuator, chỉ mở những endpoint cần thiết, đặt trên cổng quản trị không ra Internet và yêu cầu xác thực cho mọi thứ ngoài health. Tắt các trang tài liệu API và công cụ dev ở production nếu chúng không dành cho bên ngoài.

server.error.include-stacktrace=never
server.error.include-message=never
server.error.include-binding-errors=never
management.endpoints.web.exposure.include=health,prometheus
management.endpoint.health.show-details=when-authorized
management.server.port=9091

3. Quét dependency

Phần lớn code chạy trong ứng dụng không do bạn viết. Lỗ hổng trong một thư viện bắc cầu vẫn là lỗ hổng của bạn. Đưa việc quét vào CI để build thất bại khi có CVE nghiêm trọng, và dùng công cụ tự mở pull request cập nhật phiên bản. Quét cả image, vì hệ điều hành nền cũng có lỗ hổng. Giữ Spring Boot ở bản vá mới nhất của nhánh đang dùng là cách đơn giản nhất để nhận bản sửa.

# fail the build on high-severity CVEs
./mvnw org.owasp:dependency-check-maven:check -DfailBuildOnCVSS=7

# what is actually on the classpath?
./mvnw dependency:tree

4. Quyền tối thiểu ở mọi tầng

Giả định rằng một ngày nào đó ứng dụng sẽ bị khai thác, và hỏi kẻ tấn công làm được gì tiếp theo. Tài khoản database của ứng dụng chỉ cần đọc ghi dữ liệu, không cần quyền tạo hay xóa bảng; migration chạy bằng tài khoản khác. Container chạy bằng user không phải root với hệ thống file chỉ đọc. Mỗi service có credential riêng, chỉ đủ cho việc của nó.

  • Giới hạn tốc độ ở gateway cho đăng nhập và các endpoint tốn kém.

  • Kiểm tra dữ liệu vào ở biên bằng Bean Validation; giới hạn kích thước request.

  • CORS chỉ cho phép đúng origin cần thiết, không dùng dấu sao cùng credentials.

  • Ghi nhật ký các sự kiện bảo mật: đăng nhập thất bại, bị từ chối quyền, đổi quyền.

  • Xoay vòng secret định kỳ và sau mỗi lần nghi lộ.

Hardening là chuẩn bị cho ngày một lớp phòng thủ thất bại. Ngày 98 gom lại những thực hành tốt nhất của cả series vào một checklist.

Tài liệu đối chiếu

  • https://docs.spring.io/spring-security/reference/servlet/exploits/headers.html

  • https://owasp.org/www-project-top-ten/

S

Site Admin

Engineer and writer. Building things with TypeScript and distributed systems.

Bình luận (0)

Bạn cần đăng nhập bằng Google để bình luận.

Hãy là người bình luận đầu tiên.

Bài viết liên quan

99 Ngày Java — Ngày 97: JDBC

Connection và PreparedStatement, chống SQL injection, transaction thủ công và vì sao luôn cần connection pool.

1 thg 11, 20263 phút0
Kafka trong dự án thực tế: Saga đặt hàng — Order, Inventory, Payment không cần distributed transaction

Thiết kế saga đặt hàng trên Kafka: orchestration hay choreography, command/reply topic, state machine với outbox và idempotent consumer, giữ hàng an toàn, timeout khi chưa biết kết quả thanh toán — kèm code Spring Kafka, SQL và sơ đồ.

31 thg 10, 202624 phút8
99 Ngày Spring — Ngày 96: Performance Tuning

Đo trước khi chỉnh, kích thước pool HikariCP, các nút thắt thường gặp ở tầng dữ liệu, tùy chọn JVM và load test sơ lược.

30 thg 10, 20263 phút6