AWS Community Day Seoul 2024

2ㅣ2ㅣ·2024년 11월 2일

Web

목록 보기
5/5
post-thumbnail

AWS 클라우드를 사용하는 개발자 및 엔지니어를 위해 분야별 아키텍처 경험을 공유하는 기술 컨퍼런스인 AWS Community Day에 다녀왔다. 올해는 11월 2일(토) 강연 행사와 11월 3일(일) 핸즈온랩 행사로 나누어 진행한다. 이번 포스팅에서는 [웹 애플리케이션 보안 위험 Top 10 방어하기] 강연에 대해 간단히 정리해보았다.

API Security가 중요해진 이유

과거 모놀리틱 구조에서는 애플리케이션이 단일 유저 엔드포인트만을 노출하여 보안 관리가 상대적으로 단순했다.
MSA 구조를 사용하는 현재, 기능 단위로 수많은 API 엔드포인트가 존재하고 각 서비스 간의 통신이 이루어진다. 이를 통해 유연성과 확장성을 얻었지만, 보안의 중요성도 크게 증가했다.

특히 API는 광범위한 접근이 허용되고 URI, 메소드, 헤더파라미터들과 같은 웹 공격을 위한 요소들로 이뤄져 있으며 구조와 정보를 알아보기 쉽다는 점에서 공격자들의 대상이 되기 쉽다.

OWASP(국제 웹 보안 표준기구, The Open Web Application Security Project)

API 보안이 중요해짐에 따라 API 보안을 지키기 위해 준수해야 하는 사항을 국제 웹보안 분야의 대표적인 비영리단체인 OWASP가 제시한다.

OWASP Security TOP 10

1. Broken Object Level Authorization (객체 수준 권한 부여 오류)

인증된 사용자가 다른 사용자의 데이터를 조회할 수 없도록 권한을 정확히 검증해야 한다. 예를 들어, JWT와 같은 토큰을 사용하여 요청자의 정보와 일치하는지 확인하고, UUID등 객체 식별자를 무작위 값으로 설정해 무차별 대입 공격을 방어하는 것이 좋다.

💡 인증이란 사용자가 누구인지 확인하는 것이다.
Security에서 중요한 개념 중 하나로, 후에 등장할 인가와 헷갈리지 않도록 유의하실 바란다.

  • 인증 : 신원 확인 - 누구세요?
  • 인가 : 권한 확인 - 누군지 알겠는데 이럴 권한 있어요?

2. Broken Authentication (인증 오류)

무제한 로그인 시도로 인한 Brute Force 공격이나 약한 패스워드 규칙 등으로 보안 취약점이 발생할 수 있다. 가능한 한 검증된 인증 모듈(예: Spring Security, OAuth2)을 사용하는 것이 좋으며, 세션을 직접 생성할 경우 예측할 수 없는 값으로 구성해야 한다.

💡 Brute Force 공격이란?
다양한 비밀번호 조합을 무작위로 시도해 로그인을 강제로 뚫으려는 공격이다. 비밀번호가 약할수록 쉽게 뚫릴 수 있어 보안이 취약해진다.

💡 검증된 인증 모듈 예시
Spring Security: 강력한 인증 및 인가 기능을 제공하고, Brute Force 공격 방어와 같은 보안 기능을 쉽게 적용할 수 있다.
OAuth2: 토큰 기반 인증 방식으로 세션을 안전하게 관리하고, 접근 권한을 세분화하여 인증을 처리한다.

3. Broken Object Property Level Authorization (속성 수준 권한 부여 오류)

필요한 속성만 받아서 의도치 않은 데이터가 들어오는 걸 막아야 한다. 만약 이게 제대로 안 되면, 사용자가 API 요청을 통해 물건 가격이나 할인율을 마음대로 바꾸는 상황이 생길 수 있다.

예를 들어, 사용자가 장바구니에 있는 물건의 수량을 수정하는 API가 있다고 하자. 그런데 이 API가 상품 가격까지 수정할 수 있게 열려 있다면? 사용자가 가격을 임의로 바꿔서 결제할 수 있는 상황이 된다.

잘못된 코드 예시

  • 사용자가 상품 수량뿐만 아니라 가격도 수정할 수 있는 API
@Getter
@Setter
@Entity
public class CartItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Long productId;
    private int quantity;
    private int price; 

위의 엔티티를 그대로 컨트롤러에 넘긴다고 생각하면 {"productId": 123, "quantity": 2, "price": 100} 형태로 요청이 갈 것이다.

@PutMapping("/item")
public ResponseEntity<CartItem> updateCartItem(@RequestBody CartItem cartItem) {
    CartItem existingCartItem = cartService.getCartItem(cartItem.getProductId());

    
    existingCartItem.setQuantity(cartItem.getQuantity());
    existingCartItem.setPrice(cartItem.getPrice()); // 💥 사용자가 가격을 바꿀 수 있음

    return ResponseEntity.ok(cartService.save(existingCartItem));
}

위 코드에서는 사용자가 price 필드를 마음대로 수정해서 price: 100처럼 보내면 실제 가격 대신 수정된 값이 적용된다.
결과적으로 사용자가 원래 가격 대신 임의로 설정한 가격으로 결제할 수 있다. 해고각이다..

올바른 코드 예시

public class CartItemRequestDto {
    private Long productId;   
    private int quantity;     
    // private int price; (X)

CartItemRequestDto.java 를 이용하여 사용자에게 노출할 필드를 선별한다.

@Getter
@Setter
@Entity
public class CartItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Long productId;
    private int quantity;

    private int originalPrice; // 서버에거 관리하는 가격
    private int price; // 사용자에게 노출한 가격

CartItem 엔티티에서 사용자에게 노출할 가격과 서버에서 관리하는 가격을 별도로 정의하여 사용한다.

@PutMapping("/item")
public ResponseEntity<CartItem> updateCartItem(@RequestBody CartItemRequestDto cartItemRequestDto) {
        CartItem existingCartItem = cartService.getCartItem(cartItemRequestDto.getProductId());


        existingCartItem.setQuantity(cartItemRequestDto.getQuantity());

        // 가격은 DB에 저장된 값을 그대로 사용하고 변경 불가
        existingCartItem.setPrice(existingCartItem.getOriginalPrice());

        return ResponseEntity.ok(cartService.save(existingCartItem));
    }

이렇게 수량만 업데이트하고, 가격은 수정할 수 없도록 설정하면 사용자가 가격을 조작할 수 없다.
즉, 민감한 속성은 서버에서만 관리하도록 하고, 필요한 속성만 명확히 지정하는 것이 안전하다.

4. Unrestricted Resource Consumption (자원 무제한 사용)

API에 과도한 요청이 들어오면 서버 자원이 급속도로 소모돼 서비스가 느려지거나 중단될 수 있다. 이를 방지하기 위해, Rate Limiting을 적용하여 일정 횟수를 초과하는 요청을 제한한다. 예를 들어, 특정 사용자가 초당 10번 이상 API를 호출하지 못하게 막는 방식이다. 또한, 이미지 업로드 시 파일 크기를 제한해 불필요하게 큰 파일로 인해 서버 자원이 낭비되지 않도록 관리할 수 있다.

💡 Rate Limiting이란?
Rate Limiting은 서버가 일정 시간 동안 받을 수 있는 요청 수를 제한하는 기법이다. 예를 들어, 한 사용자가 1분에 60번 이상의 API 요청을 보내면 요청을 차단하여, 서버가 과도한 요청에 의해 고갈되지 않도록 한다.
Rate Limiting 적용 예시
1. IP 기반 제한: 특정 IP가 1초에 10번 이상 요청하면 차단.
2. API 키 기반 제한: 각 API 키당 1분에 최대 100번의 요청으로 제한.
3. 사용자 기반 제한: 로그인된 사용자가 1시간에 최대 500번 요청 가능.

5. Broken Function Level Authorization (기능 수준 권한 부여 오류)

Broken Object Property Level Authorization를 준수해 필요한 파라미터만 전달했어도 사용자가 어드민 URL에 접근해버린다면..? 위에선 가격을 예시로 들었지만 해당 경우 주문 정보 삭제 등 심각한 데이터 유출 등 문제가 발생할 것이다. 따라서, 각 사용자 별로 기능 접근 권한을 세분화해 레벨을 설정해야 한다.

6. Unrestricted Access to Sensitive Business Flows (민감 비즈니스 흐름에 대한 무제한 접근)

중요한 비즈니스 로직(예: 기차 좌석 예약)을 악용하여 서버에 피해를 줄 수 있다. 이런 흐름에 대해 사용 제한을 걸어두고, 불필요한 액세스를 방지하는 것이 필요하다.

7. Server Side Request Forgery (SSRF, 서버 측 요청 위조)

외부 요청을 통해 서버 내부 네트워크에 접근할 수 있는 SSRF는 IMDS 버전 1에서 발생한 보안 이슈의 원인이 됐다. 민감한 데이터는 반드시 프라이빗 네트워크 내에서만 접근하도록 설정하고, 필요 시 IMDS 버전 2를 사용해야 한다.

💡 SSRF란?
SSRF(Server Side Request Forgery)는 공격자가 서버를 통해 내부 네트워크에 접근하는 공격 기법이다. 주로 서버가 외부 요청을 처리할 때 발생하며, 공격자는 이를 통해 서버가 원래는 접근할 수 없는 내부 서비스나 데이터에 접근할 수 있다.

💡 IMDS란?
IMDS(Instance Metadata Service)는 AWS 인스턴스의 메타데이터에 접근할 수 있는 서비스이다. 예를 들어, 인스턴스의 정보나 IAM 역할 등을 조회할 수 있다. IMDS v1은 보안상 문제점이 있어 SSRF 공격에 노출될 위험이 있었고, 이를 해결하기 위해 v2가 도입됐다.

💡 캐피탈원 사의 SSRF 사례
캐피탈원은 IMDS v1을 사용하고 있었는데, SSRF 공격을 통해 공격자가 내부 메타데이터에 접근한 사건이 발생했다. 공격자는 AWS 메타데이터 서비스에 접근해 내부 시스템 정보와 자격 증명을 탈취했다.
이 사건 이후 AWS는 IMDS를 v2로 업데이트하며 메타데이터 접근 시 세션 토큰을 요구하게 변경해 보안을 강화했다. IMDS v2는 세션 기반으로 더 안전한 요청 검증을 제공해 SSRF 공격 방어에 강해졌다.

8. Security Misconfiguration (보안 설정 오류)

기본 설정을 그대로 사용하면 보안 위험이 발생할 수 있다. 예를 들어,
1. 모든 IP에서 SSH 접속 허용 (0.0.0.0 설정)

sshd_config 설정:
ListenAddress 0.0.0.0

0.0.0.0은 모든 IP에서 접근할 수 있는 설정으로, SSH 포트를 전 세계에 공개하는 위험이 있다.

sshd_config 설정:
ListenAddress 192.168.1.10  # 내부망 IP만 허용

특정 IP에서만 SSH 접속을 허용하도록 한다.

2. root 계정 노출

PermitRootLogin yes

기본적으로 제공되는 root 계정을 유지하거나, 기본 비밀번호를 변경하지 않으면 루트 권한 탈취의 위험이 있다.

PermitRootLogin no

root 계정 사용을 금지하고, 필요한 경우 sudo 권한이 있는 일반 사용자 계정을 사용한다.

3. 디버그 모드 활성화 및 스택 트레이스 노출
application.yml에서 디버그 모드 활성화

debug: true

배포 환경에서 디버그 모드를 활성화하면, 애플리케이션의 내부 구조와 스택 트레이스가 공개되어 공격자가 이를 악용할 수 있다.

debug: false

디버그 모드는 로컬 또는 개발 환경에서만 활성화하고, 배포 시에는 비활성화해야 한다.

9. Improper Inventory Management (잘못된 인벤토리 관리)

API 문서나 인벤토리를 관리하지 않으면 사각지대가 발생한다. 개발 중인 환경에서 관리되지 않은 API가 노출될 경우 공격자가 이를 악용할 수 있다. 따라서 API의 활성 상태와 파라미터, CORS 수준을 정확히 관리하는 것이 중요하다.

💡 인벤토리란?
시스템에서 관리되는 모든 API와 리소스 목록을 의미한다. 여기에는 API의 활성 상태, 파라미터, 접근 제어(CORS) 등과 같은 정보가 포함된다.
예를 들어, 레거시 API를 비활성화를 한다던가

swagger: # Swagger 설정에서 특정 API 버전 비활성화
  enabled: false
  apiVersions:
    - v1  # 더 이상 사용하지 않는 v1 API는 비활성화

필요한 도메인만 CORS에 접근할 수 있게 한다던가.

@CrossOrigin(origins = "https://example.com") // 특정 도메인만 접근 가능하도록 설정
public class SomeController { ... }

10. Unsafe Consumption of APIs (안전하지 않은 API 사용)

외부 API 응답을 신뢰하고 바로 사용하는 경우 문제가 될 수 있다. 특히 결제와 같은 민감한 데이터는 프론트엔드가 아닌 서버에서 검증해야 하며, 요청의 응답 값을 항상 서버에서 확인하고 입력값도 철저히 검증하는 습관이 필요하다. 모든 통신에는 TLS를 사용하고, 민감한 값은 타임아웃을 설정해 안전성을 확보해야 한다.

AWS 추천 보안 Tools

1. Amazon CodeGuru Reviewer

깃허브 레포와 연동해 코드 품질과 보안 취약점을 자동으로 검토해주는 서비스다. 코드 내 보안 위험을 미리 발견하는 데 도움이 된다.

2. Amazon CodeGuru Security

현재 무료로 제공되는 이 서비스는 코드 보안 점검을 지원한다. 테스트 목적으로 사용해보면 좋다.

3.ECR의 취약점 스캔 기능

ECR에 이미지를 업로드할 때 보안 취약점을 스캔하는 기능이 무료로 제공된다. 패키지 보안, 모의 해킹 등의 방법으로 위험 요소를 탐지할 수 있다.



간단한 후기


인프라에 대한 지식이 없는 상태로 가벼운 마음으로 들었다. 강연 내용을 완전히 흡수하진 못했을지라도 AWS의 서비스에 대해 알아보고, 관심을 갖는 좋은 계기가 되었다. 또, 기업에서 실제 발생하는 에러나 현업 프로세스를 간략하게나마 접할 수 있어 큰 동기부여가 되었다! 그리고 선물이 매우 알찼다 짱짱^_^


참고
✔️ OWASP Security Top 10 블로그 포스팅
✔️ 2024년 최신 OWASP Security Top 10 목록

profile
https://sususoo.tistory.com/

0개의 댓글