Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

보안 JSON 웹 토큰(JWT)으로 Ruby 앱을 강화하세요

웹 애플리케이션에 사용자가 관련된 경우 당연히 사용자의 데이터를 보호하고 보안을 유지해야 합니다.

웹 애플리케이션 보안은 여러 가지를 의미할 수 있습니다. 이 게시물에서는 JWT(JSON 웹 토큰) 및 Ruby on Rails 웹 애플리케이션 프레임워크를 사용한 인증과 관련된 웹 보안의 하위 집합에 대해 논의하겠습니다.

시작해 보세요!

JSON 웹 토큰이란 무엇인가요?

JSON 웹 토큰은 IETF(Internet Engineering Task Force)에서 "두 당사자 간에 전송될 클레임을 표현하는 URL 안전하고 컴팩트한 수단"으로 정의한 인터넷 표준입니다.

여기서 '주장'은 특정 주제에 대한 다양한 정보를 의미합니다. 클레임은 이름/값 쌍으로 표시되며 이름은 항상 문자열이고 값은 JSON 값일 수 있습니다.

JSON 웹 토큰의 기본 구조

JWT의 복잡성을 탐구하는 것은 이 게시물의 범위를 벗어납니다. 그렇긴 하지만, JWT의 구조를 아는 것은 가치가 있습니다.

JWT는 마침표로 구분된 헤더, 페이로드, 서명의 세 부분으로 구성됩니다.

JWT의 예는 다음과 같습니다:

 

가독성을 위해 토큰의 각 부분은 새 줄에서 시작됩니다. 실제로는 부품이 결합됩니다.

이 토큰은 서버에서 클라이언트로 전송됩니다. 클라이언트는 자신을 식별하고 요청을 처리하기 위해 서버에 토큰을 보냅니다.

첫 번째 부분인 헤더에는 토큰을 생성하는 데 사용되는 알고리즘과 토큰 유형에 대한 정보가 포함됩니다. 디코딩되면 다음과 같은 결과를 얻게 됩니다:

 

두 번째 부분인 페이로드에는 사용자에 대한 클레임 집합이 포함되어 있습니다. 대부분의 경우 이는 클라이언트-서버 설정의 클라이언트이며 다음과 같습니다:

 

서명(서명된 JWT의 마지막 부분)은 토큰의 유효성을 검사합니다. 이는 Base64url 인코딩(RFC 4648)을 사용하여 헤더와 페이로드를 인코딩한 다음 마침표 구분 기호로 연결하여 생성됩니다.

본질적으로 서명 비트에서는 다음과 같은 일이 발생합니다.

 

HMAC_SHA256 서명을 해시하는 SHA-256 해시 함수로 구성된 키 해시 알고리즘 유형입니다. 암호화 알고리즘의 선택은 "alg": "HS256"에서 나옵니다. 헤더에. 토큰이 서명되지 않은 경우 서명 없이 헤더와 페이로드만 갖게 됩니다.

JWT와 JWT 비교 Ruby 앱을 위한 기타 인증 방법

JSON 웹 토큰은 이름에서 알 수 있듯이 토큰 기반입니다. 스펙트럼의 반대편에는 사용자를 인증하는 보다 전통적인 방법인 세션 기반 인증이 있습니다. 세션 기반 인증의 흐름은 토큰 기반 인증의 흐름과 상당히 다릅니다.

세션 기반 인증의 흐름은 다음과 같습니다:

  1. 사용자 또는 클라이언트가 사용자 자격 증명이 포함된 요청을 보냅니다.
  2. 서버는 사용자를 인증하고 세션을 저장하며 브라우저에 쿠키로 저장된 세션 ID를 반환합니다.
  3. 클라이언트는 후속 요청과 함께 쿠키를 서버로 보냅니다.
  4. 서버는 제공된 세션 정보를 검사하고 유효한 경우 사용자를 인증한 후 요청된 정보를 클라이언트에 반환합니다.

세션 기반 인증은 주로 클라이언트-서버 연결에 사용되는 반면, 토큰 기반 인증은 서버-서버 연결(예:두 API 간)에 자주 사용됩니다.

그러나 주목해야 할 한 가지 중요한 차이점은 세션 기반 인증을 사용하면 인증 상태가 서버에서 처리되는 반면 토큰은 클라이언트에서 관리된다는 점입니다.

인증을 위해 JWT를 사용하는 이유

구현이 상대적으로 간단하다는 것 외에도 인증에 JSON 웹 토큰을 사용하면 다음과 같은 몇 가지 다른 이점이 있습니다.

  • 무국적입니다. , 이는 세션 저장소가 필요하지 않음을 의미합니다. 토큰 자체에는 모든 사용자 정보가 포함되어 있으므로 각 요청에 대한 정보를 데이터베이스나 인증 서버에 쿼리할 필요가 없습니다.
  • JWT는 일반적으로 성능이 더 좋습니다 (서버가 사용자를 인증하기 위해 데이터베이스나 저장소에 대해 조회를 수행하지 않는 한) 대부분의 전통적인 인증 방법보다 훨씬 효율적입니다.
  • 또한 확실한 보안 보장을 제공합니다. , 서명된 JWT는 공격자나 클라이언트가 보호된 데이터에 액세스하기 위해 토큰을 수정할 수 없도록 보호 장치를 제공합니다.

Ruby 앱을 위한 JWT 모범 사례

JWT에 서명하는 데 사용되는 비밀 키는 길고 무작위이며 복잡한 문자 조합을 가져야 한다는 것은 말할 필요도 없습니다. 이렇게 하면 키가 적절하게 안전하고 공격자가 키를 무차별 공격하는 것을 방지할 수 있습니다.

Rails가 생성하는 비밀 키는 대부분 안전하지만 실수로 키를 커밋하고 공개하는 경우 안전 보장이 무효화됩니다.

네트워크의 당사자 간에 토큰을 전송할 때 TLS(전송 계층 보안)를 사용하는 것도 중요합니다. TLS는 중간자 공격을 완화할 수 있습니다(토큰 및 세션 기반 인증 방법 모두 이러한 공격에 취약함).

Rails 앱에서 JWT 인증 구현

토큰 기반 인증 흐름을 살펴보겠습니다.

보안 JSON 웹 토큰(JWT)으로 Ruby 앱을 강화하세요

세션 기반 인증과 달리 세션을 사용하여 인증된 사용자를 추적하는 상태 저장 인증 기술인 JWT를 사용한 토큰 기반 인증은 상태 비저장입니다. 사용자의 인증 상태에 대한 정보를 서버에 저장할 필요가 없습니다. 이는 애플리케이션 설계를 단순화합니다.

이 게시물에서는 애플리케이션이 프런트엔드와 백엔드로 분할되어 있다고 가정하겠습니다. 인증은 백엔드에서 이루어지므로 우리는 인증을 통해 Rails API 백엔드를 구축할 것입니다.

이 게시물의 샘플 코드는 Rails 7.0.5 및 Ruby 3.2.2를 기반으로 합니다.

jwt 사용 그리고 bcrypt 루비 보석

애플리케이션에는 jwt이라는 두 개의 보석이 필요합니다. 및 bcrypt .

jwt RFC 7519 OAuth JSON 웹 토큰 표준을 Ruby로 구현한 것입니다. bcrypt OpenBSD bcrypt()용 Ruby 바인딩입니다. 비밀번호 해싱 알고리즘.

이 코드 저장소의 샘플 코드를 따라가실 수 있습니다.

참고: jwt JWT 작업을 위한 유일한 솔루션은 아닙니다. 또 다른 잘 알려진 보석은 devise-jwt입니다. Devise 및 Rails에 대한 JWT 인증을 제공합니다. 하지만 우리는 jwt에 집중하겠습니다. 이 게시물에 있습니다.

시작해 보겠습니다.

Rails API 구축

가장 먼저 필요한 것은 API 애플리케이션입니다. 우리는 다음을 사용하여 하나를 만들 것입니다:

 

--api 여기 옵션은 API 애플리케이션 전용으로 더 작은 Rails 스택을 사전 구성합니다.

Gemfile 내부에 첫 번째 종속성인 jwt를 추가할 수 있습니다. . 두 번째 보석, bcrypt 는 이미 새로 생성된 Rails 애플리케이션의 Gemfile에 있습니다. 주석 처리만 제거하면 됩니다.

bcrypt이 필요합니다 데이터베이스에서 사용자 비밀번호를 안전하게 해시합니다. bcrypt를 사용하지 않는다는 점에 유의하는 것이 중요합니다. 직접. Active Model의 has_secure_password을 활용하겠습니다. bcrypt에 의존하는 클래스 메소드 .

새로운 Rails 애플리케이션과 함께 제공되는 기본 gem을 무시하면 Gemfile은 다음과 같아야 합니다:

 

이제 bundle install로 gem을 설치하기에 좋은 시간입니다. .

User 생성 및 Product 모델

다음으로 User이라는 두 가지 모델을 생성하겠습니다. 및 Product . User 사용자를 나타내는 모델이 될 것이며 Product로 표시되는 제품에 대한 액세스를 허용하도록 이를 인증할 것입니다. 모델.

 

rails db:migrate로 마이그레이션을 실행한 후 , 모델 설정이 완료되었으며 스키마는 db/schema.rb에 있습니다. , 이제 다음과 유사하게 보일 것입니다:

 

이 단계에서는 데이터베이스 제약 조건, 유효성 검사, 고유성 보장 등과 같은 잠재적인 문제를 의도적으로 무시하고 있다는 점을 지적하는 것이 중요합니다. 이 게시물에서는 특히 오류 처리를 무시하겠습니다. 여기서 우리의 목적은 Ruby 애플리케이션을 보호하기 위한 상태 비저장 인증 형태로 JWT가 작동하는 모습을 보여주는 것입니다. 프로덕션 애플리케이션에서는 이러한 사항이 모두 포함되었는지 확인하고 싶을 것입니다.

jwt 빌드 보석 포장지

다음 단계는 jwt 주위에 래퍼를 만드는 것입니다. 이전에 설치한 gem입니다. 이 래퍼를 사용하여 서버에서 클라이언트로의 클레임을 인코딩하고 디코딩합니다. 이를 위해 app/lib을 생성하겠습니다. 폴더.

lib를 사용하지 않는 이유 Rails와 함께 제공되는 폴더는 자동으로 로드되지 않는다는 것입니다. app 아래의 모든 것 기본적으로 자동 로드 및 즉시 로드되므로 우리의 경우 설정이 더 간단해집니다.

래퍼 클래스는 app/lib/json_web_token.rb에 있습니다. 다음과 같습니다:

 

여기서 주요 메소드는 encode입니다. — 사용자 정보 인코딩 — 및 decode — 나중에 서버에서 사용자 정보를 디코딩합니다. 인코딩 및 디코딩 작업을 jwt에 어떻게 위임하는지 참고하세요. JWT.encode을 통한 보석 및 JWT.decode .

이제 Rails 콘솔에서 이 클래스를 이미 테스트할 수 있습니다:

 

JsonWebToken.encode(data)의 결과가 어떻게 나타나는지 확인하세요. 헤더, 페이로드, 서명을 생성하는 점으로 세 부분으로 나뉩니다. Rails가 Rails.application.secrets.secret_key_base를 통해 제공하는 비밀 키로 페이로드에 서명했기 때문에 서명 비트가 있습니다. .

User로 돌아가기 모델

이제 User를 방문해 보세요. app/models/user.rb의 모델 . 여기서 해야 할 일은 has_secure_password을 추가하는 것뿐입니다. 수업 방법:

 

has_secure_password 사용자의 비밀번호를 데이터베이스에 안전하게 해시합니다.

Rails에서 샘플 사용자 및 제품 생성

이제 Rails 콘솔로 이동하여 데이터베이스에 샘플 사용자와 제품을 생성하고 애플리케이션의 보안을 테스트할 수 있습니다.

 

seeds.rb에 동일한 코드 조각을 배치할 수 있습니다. 데이터베이스를 재설정할 경우를 대비해 입력 내용을 저장하기 위한 파일입니다.

Rails 컨트롤러에서 JWT 사용

다음 단계에서는 컨트롤러 내부의 JWT를 사용하여 보안을 구현하겠습니다. 시작하기 좋은 곳은 ApplicationController입니다. app/controllers/application_controller.rb에서 :

 

여기서는 authenticate를 생성합니다. 사용자가 보내는 JSON 웹 토큰을 디코딩하는 방법입니다. 토큰을 성공적으로 확인할 수 있으면 User를 반환합니다. 요청하는 사용자를 나타내는 개체입니다. 우리는 여기서 행복한 경로에 주로 관심이 있으며 많은 확인을 우회할 것입니다.

authenticate 정의 ApplicationController의 메소드 before_action로 설정 여기에서 상속되는 모든 컨트롤러를 보호합니다. 다른 컨트롤러에 대한 요청에는 해당 컨트롤러에 액세스하기 위한 유효한 JWT가 필요합니다(다른 모든 컨트롤러가 이 기본 컨트롤러를 상속하기 때문입니다).

다음으로 AuthenticationController이 필요합니다. 사용자가 요청을 보내고 서버에서 서명된 JSON 웹 토큰을 얻을 수 있습니다. 이 컨트롤러는 app/controllers/authentication_controller.rb에 배치되어야 합니다. 다음과 같이 보일 수 있습니다:

 

요청이 이 컨트롤러에 도달하면 사용자가 토큰을 요청하기 때문에 처음에는 인증을 원하지 않습니다. 이 컨트롤러의 목적은 사용자가 서버의 나머지 리소스에 액세스하는 데 사용할 수 있는 토큰으로 응답하는 것입니다. 따라서 skip_before_action :authenticate이 필요합니다. 두 번째 줄에.

login에서 작업(사용자가 토큰을 위해 누른 작업)에서 username를 가져옵니다. 그리고 password 이 컨트롤러에 대한 요청과 함께 제공되는 매개변수에서. 사용자를 인증할 수 있으면(즉, 사용자 이름과 비밀번호가 데이터베이스에 저장된 것과 일치하는지 확인하면) 서명된 토큰과 해당 토큰이 만료되는 시기에 대한 정보를 제공합니다.

우리의 경우 토큰의 만료 기간을 사용하지 않습니다. 그러나 프로덕션 애플리케이션에서는 리소스에 대한 액세스를 취소하는 데 사용될 수 있습니다.

curl를 사용하여 이 모든 단계를 진행하겠습니다. 나중에.

보호된 리소스로 Ruby 애플리케이션 테스트

이전 'Rails 앱에서 JWT 인증 구현' 섹션의 흐름도에서 흐름을 부분적으로 다루었습니다. 모든 것을 완벽하게 다루고 흐름도의 모든 단계를 테스트하려면 보호할 리소스가 필요합니다.

Product이 있습니다. 이미 모델. 이제 제품 모델에 대한 컨트롤러와 제품 및 토큰에 액세스하기 위한 경로가 필요합니다.

rails g controller Product index을 사용하여 컨트롤러를 만들어 보겠습니다. 그래서 우리는 다음과 같은 것을 갖게 됩니다:

 

물론 이러한 컨트롤러에 액세스하려면 경로가 필요합니다. 우리 config/routes.rb 다음과 같아야 합니다:

 

이제 curl로 테스트하겠습니다. 모든 것이 예상대로 작동하는지 확인합니다. 인증할 사용자와 액세스할 제품 리소스가 이미 있다는 점에 유의하세요.

존재하지 않는 사용자로 JWT를 가져와 보겠습니다.

 

우리는 다음과 같은 응답을 받아야 합니다:

 

이제 이전에 생성한 사용자로 동일한 작업을 시도해 보세요.

 

그러면 다음과 같은 서명된 JSON 웹 토큰이 제공됩니다.

 

이 토큰을 잠시 보관하고 잘못된 토큰으로 제품 리소스에 액세스해 보겠습니다(토큰에서 임의의 문자를 변경했습니다):

 

그리고 우리는 다음을 얻어야 합니다:

 

그러나 이전에 서버에서 얻은 유효한 토큰을 사용하여 제품 리소스에 액세스하기 위해 동일한 요청을 하는 경우:

 

제품 리소스에 대한 액세스 권한이 부여되었습니다:

 

그게 다야! JSON 웹 토큰으로 Ruby 애플리케이션을 성공적으로 보호했습니다!

마무리

이 게시물에서는 JSON 웹 토큰과 그 작동 방식에 대해 논의했습니다. 먼저 구조와 몇 가지 모범 사례를 포함하여 JWT의 기본 사항을 다루었습니다. 그런 다음 jwt을 사용하여 간단한 JWT 인증을 구현했습니다. 보석.

이 게시물이 도움이 되었기를 바랍니다. 즐거운 코딩 되세요!

추신 Ruby Magic 게시물이 보도되는 즉시 읽으려면 Ruby Magic 뉴스레터를 구독하고 단 하나의 게시물도 놓치지 마세요!