Amazon Web Services 한국 블로그

[AWS Hero 특집] Amazon Cognito 기반 손쉬운 아이디 고유성 확인 방법

AWS 커뮤니티 히어로인 Larry Ogrodnek 의 기고입니다. Larry 는 AWS에서 클라우드 아키텍처, 데브옵스, 서버리스, 일반 소프트웨어 개발을 중점적으로 다루는 독립 컨설턴트입니다. 그는 커피를 마시면서 AWS에 대해 이야기하면서 개발하기를 즐기며, 다른 사람을 도울 준비가 되어 있습니다.

시스템에서 사용자는 어떻게 식별될까요? 사용자 이름은요? 이메일은 어떤가요? 이러한 항목이 고유한지가 중요한가요?

Amazon Cognito에서는 사용자 이름과 이메일에서 대소문자를 구분한다는 점을 알면 저처럼 놀랄지도 모르겠군요. “JohnSmith”는 “johnsmith”와 다르고, “jOhNSmiTh”와도 다릅니다. 이메일 주소도 마찬가지입니다. 사용자 “smith”와 “Smith”가 다른 메일박스를 갖고 있을 수도 있는 SMTP RFC에도 이 방식이 지정됩니다. 굉장하지 않나요!

최근에 Amazon Cognito에 대한 사용자 지정 등록 확인 기능을 추가했습니다. 이 글에서는 그 구현 과정을 소개하고자 합니다.

아이디 고유성의 문제

아이디 고유성을 확인한다는 것은 대소문자를 구분하는 것보다 훨씬 더 어렵습니다. 많은 사람들처럼 저도 IDN(다국어 도메인) 동형이의어 공격에 기반한 이메일을 받습니다. “example.com”에 사이트 하나가 등록되었지만, 키릴 문자 “a”를 포함하고 있기 때문에 합법적인 사이트를 가장하고 정보를 수집합니다. 사용자 이름 등록에서도 이러한 동일한 유형의 공격이 가능합니다. 이 부분을 확인하지 않으면 누군가 제 사이트에서 다른 사용자를 가장할 수도 있습니다.

예약어가 있나요?

애플리케이션에 사용자가 생성한 메시지나 콘텐츠를 포함하고 있나요? 고유성 처리 문제 외에도, 특정 이름을 예약하고 싶을 때가 있습니다. 예를 들어, user.myapp.com이나 myapp.com/user에서 사용자가 편집할 수 있는 정보가 있을 때 누군가 “signup”, “faq” 또는 “support”를 등록하면 어떻게 될까요? “billing”를 등록하면요?

악의적인 사용자가 제 사이트를 가장하고 공격의 일부로 이를 사용할 수도 있습니다. 사용자에게 일종의 수신함이나 메시징 기능이 있으면 유사한 공격도 가능합니다. 그래서 사용자 이름 예약 외에도, 혼동되지 않도록 사용자 콘텐츠를 고유한 도메인과 분리해야 합니다. 2013년에 Github에서 github.io로 사용자 페이지를 이전할 때  비슷한 문제를 처리했던 것이 기억납니다.

James Bennet 는 그의 블로그 글인 사용자 이름에 대해 이야기해볼까요?에서 이 문제에 대해 자세히 기술한 바 있습니다. 그는 django-registration 애플리케이션에서 수행한 확인 유형을 설명합니다.

Amazon Cognito에서 상호 작용

좋습니다. 이제 이 문제에 대해 조금은 알게 되었으니, Amazon Cognito에서 어떻게 처리할 수 있을까요?

저는 운이 좋았습니다. Amazon Cognito에서 AWS Lambda 트리거를 사용해 인증 워크플로우 대부분을 사용자 지정할 수 있었으니까요.

사용자 이름이나 이메일 확인 기능을 추가하기 위해 pre-sign-up Lambda 트리거를 구현할 수 있습니다. 그러면 이를 통해 사용자 지정 확인을 수행하고 등록 요청을 승인하거나 거부할 수 있습니다.

제가 이러한 요청을 수정할 수 없다는 점이 중요합니다. 강제로 소문자로 변환하는 등 모든 종류의 대소문자나 이름 표준화를 수행하려면 클라이언트에서 작업해야 합니다. 저는 Lambda 함수에서 이 작업이 수행되었다는 것만 확인할 수 있습니다. 나중에 이 기능이 사용 가능해지면 편리해지겠죠.

등록이 유효하지 않다고 선언하기 위해 제가 할 일은 Lambda 함수에서 오류를 반환하는 것입니다. Python에서 이 작업은 예외 생성만큼 간단합니다. 확인을 통과하면 이벤트를 반환하기만 하면 됩니다. 이미 이벤트에 일반적인 성공 응답에 필요한 필드가 포함되어 있습니다. 선택적으로 이러한 필드를 자동으로 확인할 수 있습니다.

프런트엔드에서 소문자로 표준화된 사용자 이름을 전송하도록 적용하려면 다음 코드만 있으면 됩니다.

def run(event, context):
  user = event[‘userName’]
  if not user.isLower():
    raise Exception(“Username must be lowercase”)
  return event

고유한 제한 조건 및 예약어 추가

Lambda에서 다음 유형의 고유성 검사를 간편하게 수행하기 위해 username-validator라는 Python 모듈에 대한 django-registration에서 확인 검사를 추출했습니다.

pip install username-validator

혼동을 일으키는 동형이의어를 감지하는 기능 외에도, “www”, “admin”, “root”, “security”, “robots.txt” 등과 같은 예약된 이름의 표준 세트도 포함합니다. 애플리케이션에 특정된 예약어를 추가하고 개별 검사를 수행하는 기능을 제공할 수 있습니다.

이러한 추가 확인과 몇 가지 사용자 지정 예약어를 추가하기 위해 다음과 같이 함수를 업데이트합니다.

from username_validator import UsernameValidator

MY_RESERVED = [
  "larry",
  "aws",
  "reinvent"
]

validator = UsernameValidator(additional_names=MY_RESERVED)

def run(event, context):
  user = event['userName']

  if not user.islower():
    raise Exception("Username must be lowercase")

  validator.validate_all(user)

  return event

이제 이 Lambda 함수를 Amazon Cognito 사용자 풀에 pre–sign-up 트리거로 연결하고 “aws”에 등록하려고 하면 400 오류가 발생합니다. 그리고 등록 양식에 포함할 수 있는 몇 가지 텍스트도 표시됩니다. 이메일(사용하는 경우)을 포함한 기타 속성은 [‘request’][‘userAttributes’]} 이벤트 아래에서 사용 가능합니다. 예를 들면 다음과 같습니다.

{ "request": {
    "userAttributes": {"name": "larry", "email": "larry@example.com" }
  }
}

다음 단계

같은 방식으로 기타 속성을 확인할 수 있습니다. 아니면, 몇 가지 검사를 더 추가하고 실패하면 사용자 지정 메시지와 함께 예외를 생성하여 기타 사용자 지정 확인 기능을 추가할 수도 있습니다.

이 글에서는 아이디 신분 확인과 고유성에 대한 중요성을 알리고, Amazon Cognito에서 사용자 등록에 추가 확인 기능을 추가하는 방법을 설명했습니다.

이제 사용자 지정 Lambda 함수에서 등록 확인을 제어하는 방법에 대해 많이 배우셨을 것입니다. Lambda 트리거로 가능한 다른 사용자 워크플로우 사용자 지정도 확인해보십시오.

– Larry Ogrodnek;