2 minute read

1. XSS (Cross-Site Scripting)(사이트 간 스크립팅)

개념

XSS는 공격자가 웹 사이트에 악성 클라이언트 측 스크립트를 삽입하여 다른 사용자의 브라우저에서 실행되게 하는 공격 기법입니다. 이 공격이 성공하면 공격자는 사용자의 쿠키나 세션 토큰을 탈취하거나, 웹 페이지를 변조하거나, 악성 코드를 실행할 수 있습니다.

XSS(사이트-간-스크립팅)

XSS 유형

  1. 저장형(Stored) XSS: 악성 스크립트가 서버에 영구적으로 저장되어 해당 페이지를 방문하는 모든 사용자에게 영향
  2. 반사형(Reflected) XSS: URL 파라미터 등을 통해 전달된 악성 스크립트가 즉시 페이지에 반영
  3. DOM 기반 XSS: 클라이언트 측 JavaScript가 DOM을 안전하지 않게 조작할 때 발생

방어 기법

  1. 입력 검증과 출력 인코딩
    // 잘못된 방식
    String userInput = request.getParameter("comment");
    response.getWriter().println("<div>" + userInput + "</div>");
       
    // 올바른 방식
    String userInput = request.getParameter("comment");
    String safeInput = StringEscapeUtils.escapeHtml4(userInput);
    response.getWriter().println("<div>" + safeInput + "</div>");
    
  2. 콘텐츠 보안 정책(CSP) 설정
    Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com
    
  3. HttpOnly 쿠키 사용: 클라이언트 측 스크립트가 쿠키에 접근할 수 없게 함
    Cookie cookie = new Cookie("sessionId", "abc123");
    cookie.setHttpOnly(true);
    response.addCookie(cookie);
    
  4. 프레임워크의 보안 기능 활용
    • Spring MVC의 th:text나 React의 ``와 같은 자동 이스케이프 기능 사용


2. CSRF (Cross-Site Request Forgery)(사이트 간 요청 위조)

개념

CSRF는 인증된 사용자가 의도하지 않은 요청을 서버에 보내도록 속이는 공격입니다. 사용자가 로그인한 상태에서 악성 웹사이트를 방문했을 때, 브라우저에 저장된 쿠키를 이용해 사용자 모르게 서버에 요청을 보내게 됩니다.

CSRF(사이트-간-요청-위조)

방어 기법

  1. CSRF 토큰 사용
    // 서버 측 토큰 생성
    String csrfToken = UUID.randomUUID().toString();
    session.setAttribute("csrfToken", csrfToken);
       
    // 뷰에 토큰 포함
    model.addAttribute("csrfToken", csrfToken);
       
    // 요청 시 토큰 검증
    String receivedToken = request.getParameter("csrfToken");
    String expectedToken = (String) session.getAttribute("csrfToken");
       
    if (receivedToken == null || !receivedToken.equals(expectedToken)) {
        throw new ForbiddenException("CSRF 토큰이 유효하지 않습니다.");
    }
    
  2. SameSite 쿠키 속성 사용
    Cookie cookie = new Cookie("sessionId", "abc123");
    cookie.setAttribute("SameSite", "Strict"); // 또는 "Lax"
    response.addCookie(cookie);
    
  3. Referer 검증
    String referer = request.getHeader("Referer");
    if (referer == null || !referer.startsWith("https://mywebsite.com")) {
        throw new ForbiddenException("올바르지 않은 출처입니다.");
    }
    
  4. Double Submit Cookie: 쿠키와 요청 파라미터로 동일한 값을 전송하고 서버에서 일치 여부 확인


3. SQL Injection (SQL 삽입)

개념

SQL Injection은 악의적인 SQL 쿼리를 입력하여 데이터베이스를 조작하는 공격 기법입니다. 이 공격으로 데이터 유출, 수정, 삭제는 물론 서버 명령어 실행까지 가능할 수 있습니다.

SQL-Injection

위 그림에서 보면 쿼리가 데이터베이스 테이블에서 일치하는 ID를 검색했다면, 이제 쿼리는 ID를 찾거나 1이 1과 같은지 테스트합니다. 예상할 수 있듯이 이 문은 열에 있는 모든 선생님에 대해 항상 참이므로, 그 결과 데이터베이스는 선생님 테이블의 모든 데이터를 쿼리를 수행한 공격자에게 반환합니다.

-- 또다른 예시
String query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";

-- 공격자 입력: username = "admin' --"
-- 결과 쿼리: SELECT * FROM users WHERE username = 'admin' --' AND password = ''
-- '--' 이후는 주석 처리되어 비밀번호 확인 없이 로그인 가능

방어 기법

  1. 파라미터화된 쿼리(Prepared Statements) 사용
    String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
    PreparedStatement statement = connection.prepareStatement(sql);
    statement.setString(1, username);
    statement.setString(2, password);
    ResultSet resultSet = statement.executeQuery();
    
  2. ORM 프레임워크 활용
    // JPA/Hibernate 예시
    User user = entityManager
        .createQuery("from User where username = :username and password = :password", User.class)
        .setParameter("username", username)
        .setParameter("password", password)
        .getSingleResult();
    
  3. 입력 값 검증 및 이스케이프
    // 특수 문자 이스케이프
    String safeInput = input.replaceAll("'", "''");
    
  4. 최소 권한 원칙 적용: 데이터베이스 사용자에게 필요한 최소한의 권한만 부여


4. 통합 방어 전략

  1. 입력 데이터 검증: 모든 사용자 입력은 서버와 클라이언트 양쪽에서 검증
    if (!Pattern.matches("[a-zA-Z0-9]{1,20}", username)) {
        throw new ValidationException("유효하지 않은 사용자명");
    }
    
  2. 보안 헤더 설정
    response.setHeader("X-XSS-Protection", "1; mode=block");
    response.setHeader("X-Content-Type-Options", "nosniff");
    response.setHeader("X-Frame-Options", "DENY");
    
  3. 웹 애플리케이션 방화벽(WAF) 사용: 일반적인 공격 패턴 차단

  4. 에러 처리: 상세한 오류 정보를 외부에 노출하지 않음
    try {
        // 데이터베이스 작업
    } catch (SQLException e) {
        logger.error("데이터베이스 오류: ", e);
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "서버 오류가 발생했습니다.");
    }
    

Leave a comment