상세 컨텐츠

본문 제목

컨트롤러 코드 취약점 오류

카테고리 없음

by esoesmio 2024. 6. 18. 23:22

본문

컨트롤러 코드에서 발생할 수 있는 여러 보안 취약점 및 오류를 살펴보고, 그 예시와 예방 방법을 설명하겠습니다.

### 잠재적인 보안 취약점 및 오류

1. **Cross-Site Scripting (XSS)**
2. **SQL Injection**
3. **Input Validation 부족**
4. **Unauthorized Access**
5. **Data Exposure**
6. **Improper Error Handling**

### 1. Cross-Site Scripting (XSS)

**문제**: 사용자가 입력한 데이터를 검증하지 않고 그대로 렌더링할 때 발생할 수 있습니다. 예를 들어, `description` 필드에 스크립트를 삽입할 수 있습니다.

**예시**:
```json

{
  "description": "<script>alert('XSS');</script>"
}


```

이 데이터가 검증 없이 렌더링되면, 스크립트가 실행되어 XSS 공격이 발생합니다.

**예방 방법**:
- 입력 데이터 검증 및 이스케이프 처리
- 서버에서 데이터 저장 전에 sanitize 처리

**코드**:
```typescript

import sanitizeHtml from 'sanitize-html';

// Sanitize description to prevent XSS
const sanitizedDescription = sanitizeHtml(body.description);
await this.sellersService.changeDescription(sellerId, sanitizedDescription);


```

### 2. SQL Injection

**문제**: 사용자 입력이 직접 SQL 쿼리에 포함될 때 발생할 수 있습니다. ORM을 사용하면 대부분의 경우 방지할 수 있지만, 직접 쿼리를 작성할 때는 주의해야 합니다.

**예시**:
```json

{
  "sellerId": "1 OR 1=1"
}


```

**예방 방법**:
- 파라미터화된 쿼리 사용
- ORM을 사용할 경우 ORM의 보안 기능 사용

**코드**:
ORM을 사용하면 보통 안전하지만, 직접 쿼리를 작성할 경우 다음과 같이 작성해야 합니다:
```typescript

const seller = await this.sellerRepository.findOne({
  where: { id: sellerId }
});


```

### 3. Input Validation 부족

**문제**: 입력 값의 유효성을 검증하지 않으면, 예상치 못한 입력이 시스템에 영향을 줄 수 있습니다.

**예시**:
- `sellerId`에 숫자가 아닌 값을 입력
- `shippingCharge`에 음수 값을 입력

**예방 방법**:
- DTO(Data Transfer Object)와 class-validator를 사용하여 입력 값 검증

**코드**:
```typescript

import { IsInt, IsString, IsPositive } from 'class-validator';

class SellerIdParam {
  @IsInt()
  sellerId: number;
}

class ModifyUserRequest {
  @IsString()
  description: string;

  @IsString()
  csPath: string;

  @IsString()
  shippingType: string;

  @IsPositive()
  shippingCharge: number;

  @IsString()
  plan: string;

  @IsArray()
  @IsString({ each: true })
  imageUrls: string[];

  @ValidateNested()
  bankInformation: BankInformation;
}

class BankInformation {
  @IsString()
  bank: string;

  @IsString()
  accountNumber: string;

  @IsString()
  customerName: string;
}


```

### 4. Unauthorized Access

**문제**: 인증되지 않은 사용자가 리소스에 접근할 수 있습니다.

**예시**: 인증이 필요한 엔드포인트에 인증 없이 접근

**예방 방법**:
- JWT 또는 OAuth를 사용하여 인증 처리
- 권한 검사를 통해 특정 사용자만 접근 가능하도록 설정

**코드**:
```typescript

// 인증 및 권한 검사 미들웨어 사용
import { UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@UseGuards(AuthGuard('jwt'))
@Patch('update')
async updateSeller(
  @User() user,
  @Body() body: ModifyUserRequest,
  @Param() param: SellerIdParam,
) {
  // ...
}


```

### 5. Data Exposure

**문제**: 민감한 정보가 외부에 노출될 수 있습니다.

**예시**: 응답 데이터에 민감한 정보가 포함될 경우

**예방 방법**:
- 필요하지 않은 민감한 정보는 응답에서 제외
- 데이터 전달 전에 필요한 부분만 선택적으로 전달

**코드**:
```typescript

const seller = await this.sellersService.findSeller(sellerId);
// 필요한 데이터만 응답
return {
  id: seller.id,
  name: seller.name,
  description: seller.description,
  // 기타 필요한 필드만 포함
};


```

### 6. Improper Error Handling

**문제**: 에러 메시지가 너무 상세하여 공격자에게 시스템 정보를 제공할 수 있습니다.

**예시**: 데이터베이스 에러 메시지를 그대로 사용자에게 전달

**예방 방법**:
- 에러 메시지를 사용자 친화적으로 작성
- 내부 에러 메시지를 로깅 시스템에만 기록

**코드**:
```typescript

if (!seller) {
  throw new NotFoundException({
    status: HttpStatus.NOT_FOUND,
    error: 'Not Found Seller',
    message: '셀러를 찾을 수 없습니다.',
  });
}


```

### 결론

위에서 언급한 보안 취약점 및 오류를 예방하기 위해서는 입력 데이터의 철저한 검증, 인증 및 권한 검사, 민감한 정보의 보호, 그리고 적절한 에러 처리가 필요합니다. 각 예시에 대한 해결 방법을 통해 보안을 강화할 수 있습니다.

 

물론입니다. 더 많은 보안 취약점 및 오류를 포함한 예시와 그에 대한 예방 방법을 추가로 설명하겠습니다.

### 7. **Mass Assignment**

**문제**: 사용자가 의도하지 않은 데이터 필드를 변경할 수 있습니다.

**예시**: 클라이언트에서 전달된 데이터가 직접적으로 객체에 할당될 때, 악의적인 사용자가 권한이 없는 필드를 변경할 수 있습니다.

**예방 방법**:
- DTO(Data Transfer Object)를 사용하여 변경 가능한 필드를 명시적으로 정의
- 안전한 객체 할당 방법 사용

**코드**:
```typescript

class UpdateSellerDto {
  @IsString()
  description: string;

  @IsString()
  csPath: string;

  @IsString()
  shippingType: string;

  @IsPositive()
  shippingCharge: number;

  @IsString()
  plan: string;

  @IsArray()
  @IsString({ each: true })
  imageUrls: string[];

  @ValidateNested()
  bankInformation: BankInformation;
}

// 서비스 메서드에서만 DTO를 통해 값을 할당
await this.sellerRepository.update(sellerId, updateSellerDto);


```

### 8. **File Upload Security**

**문제**: 파일 업로드 기능에서 악성 파일을 업로드할 수 있습니다.

**예시**: 악의적인 사용자가 스크립트나 바이러스가 포함된 파일을 업로드

**예방 방법**:
- 파일 확장자 및 MIME 타입 검증
- 파일 크기 제한
- 업로드된 파일의 경로를 안전하게 처리

**코드**:
```typescript

import { diskStorage } from 'multer';
import { extname } from 'path';

const storage = diskStorage({
  destination: './uploads',
  filename: (req, file, callback) => {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    callback(null, file.fieldname + '-' + uniqueSuffix + extname(file.originalname));
  },
});

const fileFilter = (req, file, callback) => {
  if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
    return callback(new Error('Only image files are allowed!'), false);
  }
  callback(null, true);
};

@Controller('upload')
export class UploadController {
  @Post('image')
  @UseInterceptors(FileInterceptor('file', { storage, fileFilter }))
  uploadFile(@UploadedFile() file) {
    // 파일 업로드 처리
  }
}


```

### 9. **Rate Limiting**

**문제**: 서비스가 무차별 대입 공격 또는 과도한 요청으로 인해 장애가 발생할 수 있습니다.

**예시**: 동일한 IP에서 다수의 로그인 시도

**예방 방법**:
- 요청 횟수 제한 (Rate Limiting)
- 특정 시간 동안 허용된 요청 횟수를 초과하면 요청을 차단

**코드**:
```typescript

import { Throttle, ThrottlerGuard } from '@nestjs/throttler';
import { UseGuards } from '@nestjs/common';

@Controller('auth')
@UseGuards(ThrottlerGuard)
export class AuthController {
  @Throttle(5, 60) // 1분에 최대 5번 요청 가능
  @Post('login')
  async login(@Body() loginDto: LoginDto) {
    // 로그인 처리
  }
}


```

### 10. **CSRF (Cross-Site Request Forgery)**

**문제**: 사용자가 의도하지 않은 요청을 다른 사이트에서 보낼 수 있습니다.

**예시**: 악의적인 웹사이트에서 사용자의 인증 정보를 사용하여 요청을 보냄

**예방 방법**:
- CSRF 토큰 사용
- 서버에서 요청 시마다 CSRF 토큰을 검증

**코드**:
```typescript
import { CsrfModule, CsrfGuard } from 'nestjs-csrf';

import { UseGuards } from '@nestjs/common';

@Module({
  imports: [CsrfModule],
})
export class AppModule {}

@Controller('form')
@UseGuards(CsrfGuard)
export class FormController {
  @Post('submit')
  submitForm(@Body() formData: FormData) {
    // 폼 처리
  }
}


```

### 11. **Sensitive Data Exposure**

**문제**: 로그 또는 응답에서 민감한 데이터가 노출될 수 있습니다.

**예시**: 비밀번호나 신용카드 정보가 로그에 기록됨

**예방 방법**:
- 민감한 데이터는 절대 로그에 기록하지 않음
- 필요 시 민감한 데이터를 마스킹 처리

**코드**:
```typescript

class UserDto {
  @Exclude()
  password: string;

  @Expose()
  username: string;

  @Expose()
  email: string;
}

// User 엔티티를 UserDto로 변환하여 응답
const userDto = plainToClass(UserDto, user);
return userDto;


```

### 12. **Insecure Direct Object References (IDOR)**

**문제**: 사용자가 권한이 없는 리소스에 접근할 수 있습니다.

**예시**: URL에 다른 사용자의 ID를 입력하여 그 사용자의 정보에 접근

**예방 방법**:
- 접근 권한을 철저히 검증
- 사용자의 요청에 대해 권한 확인

**코드**:
```typescript

async updateSeller(
  @User() user,
  @Body() body: ModifyUserRequest,
  @Param() param: SellerIdParam,
) {
  const sellerId = param.sellerId;
  const detail = await this.sellersService.isExist(sellerId);
  if (!detail || detail.ownerId !== user.id) {
    throw new NotFoundException({
      status: HttpStatus.NOT_FOUND,
      error: 'Not Found Seller',
      message: '셀러를 찾을 수 없거나 접근 권한이 없습니다.',
    });
  }
  // 업데이트 로직
}


```

### 13. **Improper Logging**

**문제**: 로그에 민감한 정보가 포함될 수 있습니다.

**예시**: 사용자 비밀번호, 토큰, 신용카드 정보 등이 로그에 기록

**예방 방법**:
- 민감한 정보는 로그에 포함하지 않음
- 로그 레벨을 적절히 설정

**코드**:
```typescript

const sensitiveFields = ['password', 'token', 'creditCard'];
const filteredBody = Object.keys(body)
  .filter(key => !sensitiveFields.includes(key))
  .reduce((obj, key) => {
    obj[key] = body[key];
    return obj;
  }, {});

this.logger.log(`Request body: ${JSON.stringify(filteredBody)}`);


```

### 결론

위에서 설명한 보안 취약점 및 오류들은 실무에서 자주 발생할 수 있는 문제들입니다. 이를 예방하기 위해서는 철저한 입력 검증, 적절한 인증 및 권한 검사, 민감한 정보 보호, 그리고 적절한 에러 처리가 필요합니다. 각 예시에 대한 해결 방법을 통해 시스템의 보안성을 강화할 수 있습니다.

댓글 영역