REST 인증 및 API 키 노출


93

나는 REST에 대해 읽고 있었고 그것에 대한 많은 질문과 다른 많은 사이트와 블로그에 대한 질문이 있습니다. 이 특정 질문을 본 적이 없지만 ... 어떤 이유로이 개념에 대해 마음을 감쌀 수는 없습니다 ...

RESTful API를 구축하고 있고 보안을 유지하고 싶다면 내가 본 방법 중 하나는 보안 토큰을 사용하는 것입니다. 다른 API를 사용했을 때 토큰과 공유 비밀이있었습니다. 내가 이해하지 못하는 것은 나머지 서비스 작업에 대한 요청이 자바 스크립트 (XHR / Ajax)를 통해 이루어지고 있다는 것입니다. 누군가가 FireBug (또는 브라우저에서 "소스보기")와 같은 간단한 것으로이를 스니핑하는 것을 방지하는 것입니다. API 키를 복사 한 다음 키와 시크릿을 사용하여 그 사람을 가장하는 것입니까?


내가 본 방법 중 하나는 보안 토큰을 사용하는 것입니다. 실제로 많은 방법이 있습니다. 구체적인 예가 있습니다. "REST"와 "등록 된 사용자에게만 자바 스크립트 API를 제공"(예 : Google지도)과 혼동되는 것 같습니다.
PeterMmm

1
거의 2 년 전에 질문 한 이후 : 결국에는 무엇을 사용 했습니까?
Arjan

나는 실제로 아무것도 사용하지 않았으며 개념을 만드는 데 머리를 감싸려고 노력했습니다. 위의 PeterMmm의 의견은 아마도 사실 일 것입니다 ... 여전히 이것 중 어떤 것도 구현할 필요가 없었지만, 저는 더 나은 나 자신을 원했습니다 ... 후속 조치에 감사드립니다.
tjans

답변:


22

api 비밀은 명시 적으로 전달되지 않고 비밀은 현재 요청 의 서명 을 생성하는 데 사용됩니다 . 서버 측에서 서버는 동일한 프로세스에 따라 서명을 생성합니다. 두 서명이 일치하면 요청이 성공적으로 인증됩니다. 서명 은 비밀이 아닌 요청을 통해 전달됩니다.


9
그래서 통과 된 신호 만 있다면 ... 자바 스크립트에 여전히 노출되어 있지 않습니다 ... 그래서 API (자바 스크립트라고 함)를 통해 내 웹 페이지에 깜박임 사진을 올리고 내 페이지를 방문하면 t 내 페이지를 방문하는 모든 사람에게 내 API 키를 노출합니까?
tjans

6
제 질문을 제대로하고 있지 않다고 생각합니다 ... 아마 제가 처음에 찾고 있던 것을 찾지 못한 이유의 일부일 것입니다. jquery를 사용하여 ajax 호출을 할 때 ajax 호출에 api 키를 삽입하여 서버로 전달되도록해야합니다 ... 그 시점에서 누군가가 API 키를 볼 수 있습니다. 내가 잘못 이해하고 있다면 API 키가 클라이언트 스크립트에 포함되지 않은 경우 요청과 함께 어떻게 전송됩니까?
tjans 2011 년

4
결론 : 사람들은 openapi / restapi를 사용하기 전에 apikey + apisecret 쌍을 할당 받게 될 것이며, apikey + sign은 서버 측으로 전송되어 서버가 누가 요청하는지 알 수 있도록 할 것입니다. apisecret은 보안을 위해 절대로 서버 측으로 전송되지 않습니다. .
James.Xu

7
그래서 @ James.Xu '의'비밀은 현재 요청의 신호를 생성하는 데 사용됩니다 '라는 진술은 FALSE입니다! 클라이언트가 비밀을 알지 못하기 때문에 그 비밀을 그에게 보내는 것이 안전하지 않기 때문에 (그리고 어떻게 알 수 있을까요?) 기술적으로 '개인 키'인 '비밀'은 서버에서만 사용됩니다 (왜냐하면 아무도 알지 못합니다) 클라이언트의 사인과 비교할 사인을 생성합니다. 그래서 질문 : 클라이언트와 서버 외에는 아무도 모르는 'API 키'와 어떤 종류의 데이터가 결합되고 있습니까? Sign = api_key + 무엇입니까?
ACs

1
맞아요, @ACs. 두 서버 (웹 사이트 및 타사 API)가 동일한 비밀을 알고 있더라도 웹 사이트 서버에서 일부 서명을 계산 한 다음 그 결과 를 HTML / JavaScript에 넣은 다음 브라우저가이를 API에 전달하도록 할 수는 없습니다. 이렇게, 어떤 다른 서버가 첫 번째 웹 서버에서 해당 HTML을 요청 응답의 서명을 받고, 사용하는 수 자신의 웹 사이트에 HTML한다. (정말로 위의 게시물은 HTML공개 API 키 가 안전 할 수있는 방법에 대한 질문에 대답하지 않는다고 생각합니다 .)
Arjan

61

파트너가 등록한 도메인에서만 사용할 수있는 API를 공개하고 있습니다. 콘텐츠는 부분적으로 공개되지만 (하지만 우리가 알고있는 도메인에만 표시되는 것이 바람직 함) 대부분 사용자에게 비공개입니다. 그래서:

  • 결정 어떤 표시됩니다, 우리의 사용자는 우리와 함께 로그인해야하지만,이 별도로 처리됩니다.

  • 어디 를 결정하려면데이터가 표시 공개 API 키를 사용하여 우리가 알고있는 도메인에 대한 액세스를 제한하고 무엇보다도 개인 사용자 데이터가 CSRF에 취약하지 않도록합니다 .

이 API 키는 실제로 누구나 볼 수 있으며 다른 방법으로 파트너를 인증하지 않습니다. 하지 않으며 REFERER가 필요하지 않습니다 . 그래도 안전합니다.

  1. 때 우리의 get-csrf-token.js?apiKey=abc123IS 요청 :

    1. 열쇠를 찾아 abc123데이터베이스 를 찾고 해당 키에 대한 유효한 도메인 목록을 가져옵니다.

    2. CSRF 유효성 검사 쿠키를 찾습니다. 존재하지 않는 경우 보안 임의 값을 생성하여 입력하십시오. 하고 HTTP 전용 세션 쿠키 . 쿠키가 존재하는 경우 기존 임의 값을 가져옵니다.

    3. API 키에서 CSRF 토큰을 만들고 쿠키에서 임의의 값을 만듭니다. 서명합니다 . (서버에 토큰 목록을 유지하는 대신 값에 서명합니다. 서명 된 토큰에서 두 값을 모두 읽을 수 있습니다. 괜찮습니다.)

    4. 응답이 캐시되지 않도록 설정하고 쿠키를 추가하고 다음과 같은 스크립트를 반환합니다.

      var apiConfig = apiConfig || {};
      if(document.domain === 'expected-domain.com' 
            || document.domain === 'www.expected-domain.com') {
      
          apiConfig.csrfToken = 'API key, random value, signature';
      
          // Invoke a callback if the partner wants us to
          if(typeof apiConfig.fnInit !== 'undefined') {
              apiConfig.fnInit();
          }
      } else {
          alert('This site is not authorised for this API key.');
      }

    노트:

    • 위의 내용은 서버 측 스크립트가 요청을 위조하는 것을 방지하지 않지만 다음과 같은 경우 에만 도메인이 일치 하는지 확인합니다. 브라우저에서 요청한 합니다.

    • 자바 스크립트에 대한 동일 출처 정책은 브라우저가로드 한 후 자바 스크립트 소스를 검사 XHR (아약스)를 사용할 수 없도록합니다. 대신 일반 브라우저는 <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123">(또는 동적 인 동급)을 사용해서 만로드 할 수 있으며 그런 다음 코드를 실행합니다. 물론 서버가 Cross-Origin 리소스 공유를 지원 해서는 안됩니다. 생성 된 JavaScript에 대해 또는 JSONP를 .

    • 브라우저 스크립트는 document.domain위 스크립트를로드하기 전에 의 값을 변경할 수 있습니다 . 그러나 동일한 출처 정책은 just , to 또는 일부 브라우저에서 다시 쓰기와 같은 접두사 를 제거 하여 도메인을 단축하는 것만 허용 합니다.subdomain.example.comexample.commyblog.wordpress.comwordpress.combbc.co.ukco.uk .

    • 서버 측 스크립트를 사용하여 JavaScript 파일을 가져 오면 서버도 쿠키를 가져옵니다. 그러나 제 3 자 서버는 사용자의 브라우저가 해당 쿠키를 당사 도메인에 연결하도록 할 수 없습니다. 따라서 서버 측 스크립트를 사용하여 가져온 CSRF 토큰 및 유효성 검사 쿠키는 브라우저가 아닌 후속 서버 측 호출에서만 사용할 수 있습니다. 그러나 이러한 서버 측 호출에는 사용자 쿠키가 포함되지 않으므로 공개 데이터 만 가져올 수 있습니다. 이것은 서버 측 스크립트가 파트너의 웹 사이트에서 직접 스크랩 할 수있는 것과 동일한 데이터입니다.

  2. 사용자가 로그인 할 때 원하는 방식으로 사용자 쿠키를 설정하십시오. (사용자는 JavaScript를 요청하기 전에 이미 로그인했을 수 있습니다.)

  3. 서버에 대한 모든 후속 API 요청 (GET 및 JSONP 요청 포함)에는 CSRF 토큰, CSRF 유효성 검사 쿠키 및 사용자 쿠키 (로그온 한 경우)가 포함되어야합니다. 이제 서버는 요청을 신뢰할 수 있는지 결정할 수 있습니다.

    1. 유효한 CSRF 토큰이 있으면 브라우저에서로드 한 경우 JavaScript가 예상 도메인에서 로드 되었는지 확인 합니다.

    2. 유효성 검사 쿠키가 없는 CSRF 토큰의 존재 는 위조임을 나타냅니다.

    3. CSRF 토큰과 CSRF 유효성 검사 쿠키가 모두 존재한다고해서 어떤 것도 보장되지 않습니다. 위조 된 서버 측 요청이거나 브라우저의 유효한 요청 일 수 있습니다. (지원되지 않는 도메인에서 만든 브라우저의 요청 일 수 없습니다.)

    4. 사용자 쿠키의 존재는 사용자가 로그온되었는지 확인하지만 사용자가 주어진 파트너의 구성원인지, 사용자가 올바른 웹 사이트를보고 있는지 확인하지 않습니다.

    5. CSRF 유효성 검사 쿠키가 없는 사용자 쿠키의 존재 는 위조임을 나타냅니다.

    6. 사용자 쿠키가 있으면 브라우저를 통해 현재 요청이 이루어집니다. (사용자가 알 수없는 웹 사이트에 자신의 자격 증명을 입력하지 않는다고 가정하고 사용자가 자신의 자격 증명을 사용하여 서버 측 요청을 수행하는 것을 신경 쓰지 않는다고 가정합니다.) CSRF 유효성 검사 쿠키 있는 경우 해당 CSRF 유효성 검사 쿠키는 또한 브라우저를 사용하여 받았습니다. 다음으로 에도 유효한 서명 토큰 CSRF를 가지고, 그리고CSRF 유효성 검사 쿠키의 임의 번호가 해당 CSRF 토큰의 임의 번호와 일치하면 해당 토큰에 대한 JavaScript도 CSRF 쿠키가 설정된 이전 요청 중에 수신되어 브라우저를 사용합니다. 이것은 또한 토큰이 설정되기 전에 위의 JavaScript 코드가 실행되었으며 그 당시 도메인이 주어진 API 키에 대해 유효했음을 의미합니다.

      따라서 서버는 이제 서명 된 토큰의 API 키를 안전하게 사용할 수 있습니다.

    7. 서버가 요청을 신뢰하지 않는 경우 403 Forbidden이 반환됩니다. 위젯은 사용자에게 경고를 표시하여 이에 대응할 수 있습니다.

CSRF 유효성 검사 쿠키는 서명 된 CSRF 토큰과 비교하기 때문에 서명 할 필요가 없습니다. 쿠키에 서명하지 않으면 각 HTTP 요청이 더 짧아지고 서버 유효성 검사가 조금 더 빨라집니다.

생성 된 CSRF 토큰은 무기한 유효하지만 유효성 검사 쿠키와 결합해서 만 유효하므로 브라우저가 닫힐 때까지 효과적입니다.

토큰 서명의 수명을 제한 할 수 있습니다. OWASP 권장 사항 을 충족하기 위해 사용자가 로그 아웃 할 때 CSRF 유효성 검사 쿠키를 삭제할 수 있습니다. 그리고 여러 파트너간에 사용자 별 난수를 공유하지 않으려면 쿠키 이름에 API 키를 추가 할 수 있습니다. 그러나 새로운 토큰이 요청되면 CSRF 유효성 검사 쿠키를 쉽게 새로 고칠 수 없습니다. 사용자가 여러 창에서 동일한 사이트를 탐색하고 단일 쿠키를 공유 할 수 있기 때문입니다 (새로 고침 할 때 모든 창에서 업데이트되고 그 후에 다른 창의 JavaScript 토큰은 더 이상 해당 단일 쿠키와 일치하지 않습니다.)

OAuth를 사용하는 사람들 은 JavaScript 아이디어를 얻은 OAuth 및 클라이언트 측 위젯을 참조하십시오 . 를 들어 서버 측 우리가 도메인을 제한하기 위해 자바 스크립트 코드에 의존 할 수있는 API의 사용, 우리는 비밀 키 대신의 공개 API 키를 사용하고 있습니다.


1
CORS를 사용할 때 아마도 안전하게 확장 할 수 있습니다. 위의 대신 OPTIONSURL에 공개 API 키가 있는 사전 요청을 처리 할 때 서버는 허용되는 도메인 (또는 요청 취소)을 브라우저에 알릴 수 있습니다. 있다고하더라도 조심 일부 요청이 사전 플라이트 요청을 필요로하지 않는, 또는 전혀 CORS를 사용하지 않습니다 , 그리고 CORS는 IE8 +를 필요로. 일부 플래시 대체가 IE7에 사용되는 경우에, 어쩌면 약간의 동적은 crossdomain.xml그에 대해 동일한을 달성하는 데 도움이 될 수 있습니다. 아직 CORS / Flash를 시도하지 않았습니다.
Arjan 2013 년

10

이 질문에는 허용되는 답변이 있지만 명확히하기 위해 공유 비밀 인증은 다음과 같이 작동합니다.

  1. 클라이언트는 공개 키를 가지고 있으며 누구와도 공유 할 수 있으며 중요하지 않으므로 자바 스크립트에 삽입 할 수 있습니다. 서버에서 사용자를 식별하는 데 사용됩니다.
  2. 서버에는 비밀 키가 있으며이 비밀은 보호되어야합니다. 따라서 공유 키 인증을 사용하려면 비밀 키를 보호 할 수 있어야합니다. 따라서 비밀을 보호하기 위해 서버 중개자가 필요하기 때문에 다른 서비스에 직접 연결하는 공용 자바 스크립트 클라이언트는 불가능합니다.
  3. 서버는 비밀 키 (비밀 키는 솔트와 유사 함)를 포함하는 일부 알고리즘을 사용하여 요청에 서명하고 바람직하게는 타임 스탬프가 요청을 서비스에 보냅니다. 타임 스탬프는 "재생"공격을 방지하는 것입니다. 요청의 서명은 약 n 초 동안 만 유효합니다 . 서명에 포함 된 타임 스탬프 값을 포함해야하는 타임 스탬프 헤더를 가져 와서 서버에서 확인할 수 있습니다. 해당 타임 스탬프가 만료되면 요청이 실패합니다.
  4. 이 서비스는 서명뿐만 아니라 일반 텍스트로 서명 된 모든 필드가 포함 된 요청을받습니다.
  5. 그런 다음 서비스는 공유 비밀 키를 사용하여 동일한 방식으로 요청에 서명하고 서명을 비교합니다.

사실이지만 설계 상 귀하의 답변은 API 키를 노출 하지 않습니다 . 그러나 일부 API에서는 API 키 공개적으로 표시되며 "자바 스크립트 (XHR / Ajax)를 통해 만들어진 나머지 서비스 작업에 대한 요청 [...] 요청" 에 대한 질문 입니다. (수용된 대답도 그것에 대해 틀렸다고 생각합니다. 귀하의 요점 2는 그것에 대해 분명합니다.)
Arjan

1

API 키가 아닌 세션 키를 의미한다고 생각합니다. 이 문제는 http 프로토콜에서 상속되며 세션 하이재킹으로 알려져 있습니다. 있습니다. 일반적인 "해결 방법"은 모든 웹 사이트에서와 마찬가지로 https로 변경하는 것입니다.

REST 서비스를 안전하게 실행하려면 https 및 클라이언트 인증을 활성화해야합니다. 그러나 결국 이것은 REST 아이디어를 넘어서는 것입니다. REST는 보안에 대해 이야기하지 않습니다.


8
나는 실제로 열쇠를 의미했다. 내가 올바르게 기억한다면 API를 사용하기 위해 나머지 서비스에 API 키와 시크릿을 전달하여 인증하는 것입니까? 그것은 SSL로 암호화하려는 와이어를 통해 전달되는 일단 알아,하지만 전송되기 전에, 그 용도가 ...하는 클라이언트 코드에 의해 완벽하게 볼 수있어
tjans

1

서버 측에서 원하는 것은 로그인 또는 가입시 클라이언트로 다시 전송되는 만료 세션 ID를 생성하는 것입니다. 그런 다음 클라이언트는 해당 세션 ID를 공유 비밀로 사용하여 후속 요청에 서명 할 수 있습니다.

세션 ID는 한 번만 전달되며 SSL을 통해 전달되어야합니다.

여기에서 예보기

세션 하이재킹을 방지하려면 요청에 서명 할 때 임시 값과 타임 스탬프를 사용하십시오.


1
그러나 제 3자가 귀하의 API를 사용할 때 어떻게 로그인이있을 수 있습니까? 사용자 가 로그인 할 경우 작업은 쉽습니다. 세션을 사용 하시겠습니까? 그러나 다른 웹 사이트 가 API에 인증해야하는 경우 에는 도움이되지 않습니다. (또한 이것은 블로그를 홍보하는 것과 같은 냄새가납니다.)
Arjan

1

원래의 맥락에서 질문에 답하려고 노력할 것입니다. 그래서 질문은 "비밀 (API) 키는 JavaScript에 넣어도 안전합니까?"입니다.

제 생각에는 시스템 간의 인증 목적을 무너 뜨리기 때문에 매우 안전하지 않습니다. 키가 사용자에게 노출되므로 사용자는 권한이없는 정보를 검색 할 수 있습니다. 일반적인 나머지 통신에서 인증은 API 키만을 기반으로하기 때문입니다.

제 생각에 해결책은 JavaScript 호출이 본질적으로 나머지 호출을 담당하는 내부 서버 구성 요소에 요청을 전달한다는 것입니다. 내부 서버 구성 요소는 Servlet이 권한 기반 파일 시스템과 같은 보안 소스에서 API 키를 읽고 HTTP 헤더에 삽입하고 외부 나머지 호출을 수행한다고 가정합니다.

이게 도움이 되길 바란다.

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