인증 및 세션 관리를위한 SPA 모범 사례


307

Angular, Ember, React 등과 같은 프레임 워크를 사용하여 SPA 스타일 애플리케이션을 구축 할 때 사람들이 인증 및 세션 관리를위한 모범 사례는 무엇이라고 생각합니까? 문제에 접근하는 것을 고려하는 몇 가지 방법을 생각할 수 있습니다.

  1. API와 UI의 출처 도메인이 동일하다고 가정하고 일반 웹 애플리케이션을 사용한 인증과 다르게 처리하십시오.

    이것은 아마도 세션 쿠키, 서버 측 세션 스토리지 및 인증 된 웹 UI가 개인화를 돕기 위해 현재 사용자 정보를 얻거나 클라이언트 측의 역할 / 능력을 결정하기 위해 도달 할 수있는 일부 세션 API 엔드 포인트를 보유 할 수 있습니다. 서버는 여전히 데이터에 대한 액세스를 보호하는 규칙을 시행하지만 UI는이 정보를 사용하여 경험을 사용자 정의합니다.

  2. 공개 API를 사용하여 타사 클라이언트처럼 취급하고 OAuth와 유사한 일종의 토큰 시스템으로 인증하십시오. 이 토큰 메커니즘은 클라이언트 UI에서 서버 API에 대한 모든 요청을 인증하는 데 사용됩니다.

나는 여기에 전문가가별로 없지만 # 1은 대부분의 경우에 충분하다고 생각되지만 경험이 풍부한 의견을 듣고 싶습니다.


나는이 방법을 perfer, stackoverflow.com/a/19820685/454252
allenhwkim

답변:


476

이 질문은 길이가 약간 다른 형태로 다루어졌습니다.

RESTful 인증

그러나 이것은 서버 측에서 해결합니다. 이것을 클라이언트 쪽에서 봅시다. 하지만 그렇게하기 전에 중요한 전주곡이 있습니다.

자바 스크립트 암호화는 희망이 없다

이에 대한 Matasano의 기사는 유명하지만 여기에 포함 된 교훈은 매우 중요합니다.

https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/

요약:

  • 중간자 공격은 암호화 코드를 사소하게 대체 할 수 있습니다. <script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>
  • MITM (Man-in-the-Middle) 공격은 SSL 이외의 연결을 통해 모든 리소스를 제공하는 페이지에 대한 사소한 공격입니다.
  • SSL을 사용하면 실제 암호화를 사용하게됩니다.

그리고 내 자신의 목록을 추가하려면 :

  • 성공적인 XSS 공격으로 인해 SSL을 사용하는 경우에도 공격자가 클라이언트 브라우저에서 코드를 실행하게 될 수 있습니다. 따라서 모든 해치가 중단 된 경우에도 공격자가 실행 방법을 찾으면 브라우저 암호화가 여전히 실패 할 수 있습니다 다른 사람의 브라우저에있는 모든 자바 스크립트 코드

이것은 JavaScript 클라이언트를 사용하려는 경우 많은 RESTful 인증 체계를 불가능하거나 어리석게 만듭니다. 한번 보자!

HTTP 기본 인증

무엇보다도 HTTP 기본 인증. 가장 간단한 구성표 : 모든 요청마다 이름과 암호를 전달하십시오.

물론 모든 요청마다 Base64 (가역적으로) 인코딩 된 이름과 암호를 전달하기 때문에 SSL이 절대적으로 필요합니다. 전화를 듣는 사람은 누구나 사 용자 이름과 비밀번호를 추출 할 수 있습니다. "Basic Auth is insecure"인수의 대부분은 "Basic Auth over HTTP"에서 나온 것입니다.

브라우저는 기본 제공되는 HTTP 기본 인증 지원을 제공하지만, 추악하기 때문에 추악하기 때문에 앱에 사용해서는 안됩니다. 그러나 대안은 JavaScript에서 사용자 이름과 비밀번호를 숨기는 것입니다.

이것이 가장 RESTful 한 솔루션입니다. 서버는 상태에 대한 지식이 필요하지 않으며 사용자와의 모든 개별 상호 작용을 인증합니다. 일부 REST 애호가 (대부분은 짚맨)는 어떤 종류의 상태를 유지하는 것이 이단이며 다른 인증 방법을 생각하면 입에서 거품을 일으킬 것이라고 주장합니다. 이런 종류의 표준 준수에는 이론적으로 이점이 있습니다. Apache가 기본적으로 지원합니다. 원하는 경우 객체를 .htaccess 파일로 보호되는 폴더에 파일로 저장할 수 있습니다!

문제 ? 클라이언트 측에서 사용자 이름과 비밀번호를 캐싱합니다. 이로 인해 evil.ru에 더 나은 균열이 발생합니다. 가장 기본적인 XSS 취약점조차도 클라이언트가 자신의 사용자 이름과 암호를 악의적 인 서버로 전송시킬 수 있습니다. 비밀번호를 해시하고 솔트하여이 위험을 완화하려고 시도 할 수 있지만 다음을 기억하십시오. JavaScript Crypto is Hopeless . 앞에서 언급했듯이 브라우저의 기본 인증 지원에 영향을 미치지 만이 위험을 완화 할 수 있습니다.

HTTP 다이제스트 인증

jQuery로 다이제스트 인증이 가능합니까?

보다 "안전한"인증은 요청 / 응답 해시 챌린지입니다. 제외 자바 스크립트 암호화 가망 이 더 HTTP 기본 인증보다 복잡하지만 만드는 없으며, 그것은 단지 SSL을 통해 작동하고 당신은 여전히 클라이언트 측에서 사용자 이름과 암호를 캐시 할 수 있도록, 더 이상 보안 .

추가 서명 매개 변수를 사용한 쿼리 인증.

nonse 및 타이밍 데이터로 매개 변수를 암호화하고 (반복 및 타이밍 공격으로부터 보호) 또 다른 "보안"인증. 이것의 가장 좋은 예 중 하나는 OAuth 1.0 프로토콜입니다. 이는 내가 아는 한 REST 서버에서 인증을 구현하는 아주 놀라운 방법입니다.

http://tools.ietf.org/html/rfc5849

그러나 JavaScript 용 OAuth 1.0 클라이언트는 없습니다. 왜?

JavaScript Crypto는 희망이 없습니다 . JavaScript는 SSL없이 OAuth 1.0에 참여할 수 없으며 클라이언트의 사용자 이름과 비밀번호를 로컬에 저장해야합니다. 이는 다이제스트 인증과 같은 범주에 있습니다. HTTP 기본 인증보다 복잡하지만 더 안전하지는 않습니다 .

토큰

사용자는 사용자 이름과 비밀번호를 보내며, 요청을 인증하는 데 사용할 수있는 토큰을 얻습니다.

사용자 이름 / 암호 트랜잭션이 완료 되 자마자 민감한 데이터를 버릴 수 있기 때문에 이는 HTTP 기본 인증보다 조금 더 안전합니다. 토큰이 "상태"를 구성하고 서버 구현을 더 복잡하게 만들기 때문에 RESTful도 적습니다.

여전히 SSL

문제는 토큰을 얻기 위해 여전히 초기 사용자 이름과 비밀번호를 보내야한다는 것입니다. 민감한 정보는 여전히 타협 할 수있는 JavaScript에 영향을줍니다.

사용자의 자격 증명을 보호하려면 공격자가 JavaScript를 사용하지 않도록해야하며 여전히 유선으로 사용자 이름과 암호를 보내야합니다. SSL이 필요합니다.

토큰 만기

"이 토큰이 너무 길면 버리고 사용자를 다시 인증하게하십시오"와 같은 토큰 정책을 시행하는 것이 일반적입니다. 또는 "이 토큰을 사용할 수있는 유일한 IP 주소는 XXX.XXX.XXX.XXX"입니다. 이러한 정책 중 다수는 매우 좋은 아이디어입니다.

파이어시 핑

그러나 SSL이없는 토큰을 사용하는 것은 여전히 ​​'사이드 재킹'이라는 공격에 취약합니다 : http://codebutler.github.io/firesheep/

공격자는 사용자의 자격 증명을 얻지 못하지만 여전히 사용자 인 것처럼 가장 할 수 있습니다.

tl; dr : 암호화되지 않은 토큰을 유선으로 전송한다는 것은 공격자가 해당 토큰을 쉽게 탐색하여 사용자 인 것처럼 가장 할 수 있다는 의미입니다. FireSheep는 이것을 매우 쉽게 만드는 프로그램입니다.

더 안전한 별도의 영역

실행중인 응용 프로그램이 클수록 민감한 데이터 처리 방식을 변경하는 일부 코드를 삽입 할 수없는 것이 절대적으로 어렵습니다. CDN을 절대 신뢰하십니까? 광고주? 자신의 코드 기반?

신용 카드 세부 사항에 공통적이며 사용자 이름과 암호에 덜 일반적입니다. 일부 구현자는 응용 프로그램의 나머지 부분과 별도의 페이지에 '민감한 데이터 입력'을 유지합니다. 사용자를 피싱하기가 어렵습니다.

쿠키 (토큰을 의미)

인증 토큰을 쿠키에 넣는 것이 가능하며 일반적입니다. 이것은 토큰으로 auth의 속성을 변경하지 않으며 더 편리한 것입니다. 이전의 모든 주장이 여전히 적용됩니다.

세션 (여전히 토큰을 의미합니다)

세션 인증은 단지 토큰 인증이지만 약간 다른 점이 있지만 약간 다른 것 같습니다.

  • 사용자는 인증되지 않은 토큰으로 시작합니다.
  • 백엔드는 사용자의 토큰에 연결된 '상태'개체를 유지 관리합니다.
  • 토큰은 쿠키로 제공됩니다.
  • 응용 프로그램 환경은 세부 정보를 추상화합니다.

그러나 그 외에도 토큰 인증과 다르지 않습니다.

이것은 상태 저장 서버에서 일반 ol RPC의 경로를 따라 더 나아가는 상태 객체를 사용하여 RESTful 구현에서 더 멀어집니다.

OAuth 2.0

OAuth 2.0은 "소프트웨어 B가 소프트웨어 X가 사용자 X의 로그인 자격 증명에 액세스하지 않고 소프트웨어 X가 사용자 X의 데이터에 액세스하는 방법"문제를 검토합니다.

구현은 사용자가 토큰을 얻는 표준 방법 일 뿐이며 타사 서비스에서 "이 사용자와이 토큰이 일치하면 현재 데이터를 얻을 수 있습니다."

그러나 기본적으로 OAuth 2.0은 토큰 프로토콜 일뿐입니다. 다른 토큰 프로토콜과 동일한 속성을 나타냅니다. 토큰을 보호하려면 SSL이 필요합니다. 토큰 생성 방식 만 변경됩니다.

OAuth 2.0이 도움을 줄 수있는 두 가지 방법이 있습니다.

  • 다른 사람에게 인증 / 정보 제공
  • 다른 사람으로부터 인증 / 정보 얻기

그러나 그것이 올 때, 당신은 단지 ... 토큰을 사용하고 있습니다.

질문으로 돌아 가기

따라서 귀하가 묻는 질문은 "토큰을 쿠키에 저장하고 환경의 자동 세션 관리가 세부 정보를 처리하도록해야합니까, 아니면 Javascript로 토큰을 저장하고 해당 세부 정보를 직접 처리해야합니까?"입니다.

그리고 그 답은 행복 합니다.

그러나 자동 세션 관리에 대한 것은 당신을 위해 무대 뒤에서 많은 마술이 일어나고 있다는 것입니다. 종종 이러한 세부 사항을 직접 관리하는 것이 더 좋습니다.

저는 21 살이므로 SSL은 그렇습니다

다른 대답은 다음과 같습니다. 모든 것을 위해 https를 사용하면 뇌물이 사용자의 비밀번호와 토큰을 훔칩니다.


3
좋은 대답입니다. 토큰 인증 시스템과 기본 쿠키 인증 (웹 프레임 워크에 종종 내장 됨)의 동등성에 감사드립니다. 그것은 내가 찾던 일종의 것입니다. 고려할 많은 잠재적 인 문제를 다루는 것에 감사드립니다. 건배!
Chris Nicola

11
나는 그것이 오래되었다는 것을 알고 있지만 이것이 JWT를 포함하도록 확장되어야하는지 궁금합니다. auth0.com/blog/2014/01/07/…
Chris Nicola

14
토큰 It's also less RESTful, as tokens constitute "state and make the server implementation more complicated." (1) REST는 서버 가 상태 비 저장 상태 여야합니다. 클라이언트에 저장된 토큰 은 서버에 대해 의미있는 방식으로 상태를 나타내지 않습니다. (2) 조금 더 복잡한 서버 측 코드는 RESTfulness와 관련이 없습니다.
soupdog

10
lol_nope_send_it_to_me_instead나는이 기능의 이름을 좋아했다. : D
Leo

6
간과하는 것 한 가지 : 쿠키는 httpOnly로 표시 될 때 XSS 안전하며 안전하고 동일한 사이트를 사용하여 추가로 잠글 수 있습니다. 그리고 쿠키 처리는 훨씬 더 오래 === 더 많은 전투 강화되었습니다. 토큰 보안을 처리하기 위해 JS와 로컬 스토리지에 의존하는 것은 바보 같은 게임입니다.
Martijn Pieters

57

JWT (JSON 웹 토큰) 및 SSL / HTTPS 를 사용하여 인증 프로세스의 보안을 강화할 수 있습니다 .

기본 인증 / 세션 ID는 다음을 통해 도난 당할 수 있습니다.

  • SSL / HTTPS가없는 MITM 공격 (Man-In-The-Middle)
  • 사용자의 컴퓨터에 액세스하는 침입자
  • XSS

JWT를 사용하면 사용자의 인증 세부 정보를 암호화하여 클라이언트에 저장하고 모든 요청과 함께 API로 전송하여 서버 / API가 토큰의 유효성을 검사합니다. 그것은 개인 키 (서버 / API 저장 비밀리에)없이 읽기 / 해독 할 수 없습니다 읽기 업데이트 .

새로운 (보다 안전한) 흐름은 다음과 같습니다.

로그인

  • 사용자가 로그인하고 로그인 자격 증명을 API (SSL / HTTPS를 통해)로 보냅니다.
  • API는 로그인 자격 증명을받습니다.
  • 유효한 경우 :
    • 데이터베이스에 새 세션 등록 읽기 업데이트
    • 개인 키를 사용하여 JWT에서 사용자 ID, 세션 ID, IP 주소, 타임 스탬프 등을 암호화하십시오.
  • API는 JWT 토큰을 클라이언트 (SSL / HTTPS를 통해)로 다시 보냅니다.
  • 클라이언트는 JWT 토큰을 받아 localStorage / 쿠키에 저장합니다

API에 대한 모든 요청

  • 사용자는 HTTP 헤더에 저장된 JWT 토큰 을 사용하여 API (SSL / HTTPS를 통해)에 HTTP 요청을 보냅니다.
  • API는 HTTP 헤더를 읽고 개인 키로 JWT 토큰을 해독합니다.
  • API는 JWT 토큰의 유효성을 검사하고 HTTP 요청의 IP 주소를 JWT 토큰의 IP 주소와 일치시키고 세션이 만료되었는지 확인합니다.
  • 유효한 경우 :
    • 요청한 내용으로 응답 반환
  • 유효하지 않은 경우 :
    • 예외 발생 (403/401)
    • 시스템의 플래그 침입
    • 사용자에게 경고 이메일을 보냅니다.

업데이트 된 30.07.15 :

JWT 페이로드 / 청구서는 실제로 개인 키 (비밀)없이 읽을 수 있으며 localStorage에 저장하는 것이 안전하지 않습니다. 이 거짓 진술에 대해 유감입니다. 그러나 그들은 JWE 표준 (JSON Web Encryption) 에서 작동하는 것 같습니다 .

나는 JWT에 클레임 (userID, exp)을 저장하고 API / 백엔드가 알고있는 개인 키 (비밀)로 서명하여 클라이언트에서 보안 HttpOnly 쿠키로 저장했습니다. 그렇게하면 XSS를 통해 읽을 수없고 조작 할 수 없습니다. 그렇지 않으면 JWT가 서명 확인에 실패합니다. 또한 보안 HttpOnly 쿠키 를 사용 하면 쿠키가 HTTP 요청 (스크립트에 액세스 할 수 없음)을 통해서만 전송되고 보안 연결 (HTTPS)을 통해서만 전송됩니다.

17.07.16 업데이트 :

JWT는 본래 상태 비 저장입니다. 그것은 그들이 스스로를 무효화 / 만료한다는 것을 의미합니다. 토큰의 클레임에 SessionID를 추가하면 유효성이 서명 확인 및 만료 날짜뿐 아니라 서버의 세션 상태에도 의존하기 때문에 상태를 유지하게됩니다. 그러나 단점은 토큰 / 세션을 쉽게 무효화 할 수 있다는 것인데, 이전에는 상태 비 저장 JWT로는 할 수 없었습니다.


1
결국 JWT는 여전히 보안 관점에서 '단지 토큰'입니다. 서버는 여전히 사용자 ID, IP 주소, 타임 스탬프 등을 불투명 세션 토큰과 연관시킬 수 있으며 JWT보다 안전하지 않습니다. 그러나 JWT의 상태 비 저장 특성으로 인해 구현이 더 쉬워졌습니다.
제임스

1
@ 제임스 JWT는 검증 가능하고 주요 세부 정보를 전달할 수있는 이점이 있습니다. 이는 도메인 간 인증이 필요한 경우와 같은 다양한 API 시나리오에 매우 유용합니다. 세션이 좋지 않은 것. 또한 구현에 유용한 정의 된 (또는 적어도 진행중인) 스펙입니다. 그것은 다른 좋은 토큰 구현보다 낫다는 것은 아니지만 잘 정의되고 편리합니다.
Chris Nicola

1
@Chris 예 나는 모든 요점에 동의합니다. 그러나, 상기 답변에서 설명 된 흐름은 본질적으로 JWT의 사용으로 인해 청구 된 것처럼 더 안전한 흐름이 아니다. 또한 식별자를 JWT와 연결하고 서버에 상태를 저장하지 않으면 위에서 설명한 체계에서 JWT를 취소 할 수 없습니다. 그렇지 않으면 사용자 이름 / 암호를 요청하여 정기적으로 새 JWT를 가져 오거나 (사용자 경험이 좋지 않음) 만료 시간이 매우 긴 JWT를 발행해야합니다 (토큰을 도난당한 경우 불량).
James

1
JWT는 실제로 개인 키 (비밀)없이 해독 / 읽을 수 있으며 localStorage에 저장하는 것이 안전하지 않기 때문에 내 대답은 100 % 정확하지 않습니다. 나는 JWT에 클레임 (userID, exp)을 저장하고 API / 백엔드가 알고있는 개인 키 (비밀)로 서명하여 클라이언트에서 HttpOnly 쿠키로 저장했습니다. 그렇게하면 XSS에서 읽을 수 없습니다. 그러나 MITM 공격으로 토큰을 도난 당할 수 있으므로 HTTPS를 사용해야합니다. 이를 반영하여 답변을 업데이트하겠습니다.
가우이

1
@vsenko 쿠키는 클라이언트의 각 요청과 함께 전송됩니다. JS에서 쿠키에 액세스하지 않으며 클라이언트에서 API 로의 각 HTTP 요청과 연결됩니다.
Gaui

7

나는 두 번째 토큰 시스템으로 갈 것입니다.

ember-auth 또는 ember-simple-auth 에 대해 알고 있습니까? 그들은 ember-simple-auth 상태와 같이 토큰 기반 시스템을 사용합니다.

Ember.js 애플리케이션에서 토큰 기반 인증을 구현하기위한 가볍고 눈에 띄지 않는 라이브러리. http://ember-simple-auth.simplabs.com

세션 관리 기능이 있으며 기존 프로젝트에도 쉽게 연결할 수 있습니다.

ember-simple-auth의 Ember App Kit 예제 버전도 있습니다. OAuth2 인증에 ember-simple-auth를 사용하는 ember-app-kit의 작업 예제입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.