웹 애플리케이션에서 파일을 저장하고 공유하는 것은 필수적인 기능 중 하나입니다.
특히 **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/
'AWS > Cognito' 카테고리의 다른 글
Flask와 Amazon Cognito를 활용한 OAuth 2.0 로그인 인증 구현하기 (0) | 2025.01.27 |
---|---|
Cognito & ELB 실습(2) : Amazon ELB와 Cognito로 안전하게 S3 연결하기 (2) | 2025.01.07 |
Cognito란? (1) | 2024.12.22 |
Cognito & ELB 실습(1) (0) | 2024.12.21 |