본문 바로가기
AWS/Cognito

Flask AWS Cognito S3 파일 업로드 | 안전한 OAuth2 인증 및 보안 구축 방법

by drinkdog 2025. 2. 9.
반응형
웹 애플리케이션에서 파일을 저장하고 공유하는 것은 필수적인 기능 중 하나입니다.
특히 **AWS S3(Simple Storage Service)**는 확장성이 뛰어나고 보안 설정이 용이하여 많은 애플리케이션에서 사용됩니다.

하지만 안전한 사용자 인증 및 권한 관리가 함께 이루어지지 않으면, 데이터 유출 및 보안 사고의 위험이 있습니다.

이번 글에서는 Flask 웹 애플리케이션에서 AWS Cognito를 활용한 OAuth 2.0 로그인과 S3 파일 관리 기능을 구현하는 방법을 공부했습니다.

시작하기 전에.

Cognito에 대해서 잘 모르시는 분들은 이전에 작성했던 글 참고 부탁드리겠습니다.

 

- Cognito란?
- Cognito & ELB 실습(1)
- Cognito & ELB 실습(2)


1. Flask에서 OAuth2 및 AWS Cognito를 활용한 인증 처리

AWS Cognito는 OAuth 2.0 및 OpenID Connect (OIDC)를 지원하여 사용자가 안전하게 로그인할 수 있도록 도와줍니다. Flask에서는 authlib 라이브러리를 사용하여 Cognito와 OAuth 2.0 연동을 쉽게 구현할 수 있습니다.

1-1. Cognito 설정 로드 (config.yaml)

애플리케이션에서 필요한 설정 값을 config.yaml에 저장하고 불러옵니다.

flask:
  secret_key: "your_flask_secret_key"

aws:
  region: "us-east-1"

cognito:
  authority: "https://cognito-idp.us-east-1.amazonaws.com/your_user_pool_id"
  metadata_url: "https://cognito-idp.us-east-1.amazonaws.com/your_user_pool_id/.well-known/openid-configuration"
  client_id: "your_client_id"
  client_secret: "your_client_secret"
  identity_pool_id: "your_identity_pool_id"
  user_pool_provider_name: "cognito-idp.us-east-1.amazonaws.com/your_user_pool_id"

1-2. Flask OAuth 설정

from authlib.integrations.flask_client import OAuth

oauth = OAuth(app)
oauth.register(
    name='oidc',
    authority=config['cognito']['authority'],
    server_metadata_url=config['cognito']['metadata_url'],
    client_id=config['cognito']['client_id'],
    client_secret=config['cognito']['client_secret'],
    client_kwargs={'scope': 'openid email'}
)

이제 /login 라우트를 설정하여 로그인 페이지로 리디렉션합니다.

@app.route('/login')
def login():
    return oauth.oidc.authorize_redirect('http://localhost:5000/authorize')

사용자가 Cognito에서 로그인하면, /authorize에서 토큰을 받아 세션에 저장합니다.

@app.route('/authorize')
def authorize():
    token = oauth.oidc.authorize_access_token()
    session['user'] = token.get('userinfo')
    session['id_token'] = token.get('id_token')
    return redirect(url_for('index'))

2. AWS S3와 통합하여 사용자별 버킷 및 파일 관리

인증된 사용자가 S3에 접근할 수 있도록, Cognito Federated Identities를 이용해 임시 자격 증명을 발급합니다.

2-1. 임시 AWS 자격 증명 가져오기

import boto3

def get_temporary_credentials(id_token):
    cognito_identity_client = boto3.client('cognito-identity', region_name=config['aws']['region'])
    id_response = cognito_identity_client.get_id(
        IdentityPoolId=config['cognito']['identity_pool_id'],
        Logins={config['cognito']['user_pool_provider_name']: id_token}
    )
    identity_id = id_response['IdentityId']
    creds_response = cognito_identity_client.get_credentials_for_identity(
        IdentityId=identity_id,
        Logins={config['cognito']['user_pool_provider_name']: id_token}
    )
    return creds_response['Credentials']

S3 클라이언트를 사용자가 생성할 수 있도록 get_user_s3_client를 구현합니다.

def get_user_s3_client():
    id_token = session.get('id_token')
    if not id_token:
        raise Exception("ID token not found in session")
    temp_credentials = get_temporary_credentials(id_token)
    return boto3.client(
        's3',
        region_name=config['aws']['region'],
        aws_access_key_id=temp_credentials['AccessKeyId'],
        aws_secret_access_key=temp_credentials['SecretKey'],
        aws_session_token=temp_credentials['SessionToken']
    )

2-2. S3 파일 업로드 기능

사용자가 디렉토리를 지정하여 파일을 업로드할 수 있도록 합니다.

@app.route('/buckets/<bucket_name>/upload', methods=['GET', 'POST'])
def upload_file(bucket_name):
    s3_client = get_user_s3_client()

    if request.method == 'POST':
        file_obj = request.files.get('file')
        folder_path = request.form.get('folder_path', '').strip()

        if file_obj:
            object_key = f"{folder_path.rstrip('/')}/{file_obj.filename}" if folder_path else file_obj.filename
            s3_client.upload_fileobj(file_obj, bucket_name, object_key)
            flash(f"{object_key} 업로드 성공", "success")
            return redirect(url_for('list_objects', bucket_name=bucket_name, prefix=folder_path))

    return render_template('upload.html', bucket_name=bucket_name)

2-3. S3 Presigned URL을 통한 안전한 파일 다운로드

Presigned URL을 사용하면 임시적으로 다운로드 권한을 부여할 수 있습니다.

@app.route('/buckets/<bucket_name>/download/<path:object_key>')
def download_file(bucket_name, object_key):
    s3_client = get_user_s3_client()
    presigned_url = s3_client.generate_presigned_url(
        'get_object',
        Params={'Bucket': bucket_name, 'Key': object_key},
        ExpiresIn=3600
    )
    return redirect(presigned_url)

3. 보안 고려 사항

3-1. 민감한 정보 보호

  • Presigned URL의 유효기간을 최소한으로 설정합니다. (현재 3600초)
  • config.yaml 파일을 깃허브에 올리지 않도록 .gitignore에 추가합니다.
    • 환경 변수(os.environ)를 사용하여 보안성 강화
import os
app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', 'default_secret')

4. 마무리

Flask와 AWS Cognito를 활용하여 안전한 인증 기반의 파일 업로드 및 다운로드 시스템을 구축했습니다.

실전 프로젝트에서 활용하려면 IAM 정책 적용, 클라우드프론트 연동, CI/CD 배포까지 고려해야 할 것 같습니다.

** 전체 코드 (링크) **

 

참고

https://docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html

https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-oidc-idp.html

https://docs.authlib.org/en/latest/

반응형