토큰을 보호하기 위해 HTTPOnly 쿠키로 CORS를 활성화하는 방법은 무엇입니까?

게시 됨: 2021-12-29

이 기사에서는 HTTPOnly 쿠키를 사용하여 CORS(Cross-Origin Resource Sharing)를 활성화하여 액세스 토큰을 보호하는 방법을 살펴봅니다.

요즘에는 백엔드 서버와 프론트엔드 클라이언트가 서로 다른 도메인에 배포됩니다. 따라서 서버는 클라이언트가 브라우저에서 서버와 통신할 수 있도록 CORS를 활성화해야 합니다.

또한 서버는 더 나은 확장성을 위해 상태 비저장 인증을 구현하고 있습니다. 토큰은 클라이언트 측에서 저장 및 유지 관리되지만 세션과 같은 서버 측에서는 저장되지 않습니다. 보안을 위해 HTTPOnly 쿠키에 토큰을 저장하는 것이 좋습니다.

Cross-Origin 요청이 차단되는 이유는 무엇입니까?

프론트엔드 애플리케이션이 https://app.geekflare.com 배포되었다고 가정해 보겠습니다. https://app.geekflare.com 로드된 스크립트는 동일한 출처 리소스만 요청할 수 있습니다.

교차 출처 요청을 다른 도메인 https://api.geekflare.com 또는 다른 포트 https://app.geekflare.com:3000 또는 다른 체계 http://app.geekflare.com 에 보내려고 할 때마다 교차 출처 요청은 브라우저에 의해 차단됩니다.

그러나 브라우저가 차단한 동일한 요청이 컬 요청을 사용하여 백엔드 서버에서 보내지거나 CORS 문제 없이 우편 배달부와 같은 도구를 사용하여 보내지는 이유는 무엇입니까? 실제로 CSRF(Cross-Site Request Forgery)와 같은 공격으로부터 사용자를 보호하는 것은 보안을 위한 것입니다.

예를 들어 사용자가 브라우저에서 자신의 PayPal 계정에 로그인했다고 가정해 보겠습니다. 우리가에 대한 상호 원산지 요청 보낼 수있는 경우 paypal.com 다른 도메인에로드 된 스크립트에서 malicious.com 처럼 막고있는 CORS 오류없이 / 우리는 동일 출처 요청을 보냅니다.

공격자는 실제 URL을 숨기기 위해 짧은 URL로 변환하여 악성 페이지 https://malicious.com/transfer-money-to-attacker-account-from-user-paypal-account 를 쉽게 보낼 수 있습니다. 사용자가 악의적 인 링크를 클릭하면 스크립트는 도메인에로드 malicious.com 페이팔 계정이 실행 얻을 것이다 공격자에게 사용자의 양을 전송하기 위해 페이팔에 출처 간 요청을 보냅니다. PayPal 계정에 로그인하고 이 악성 링크를 클릭한 모든 사용자는 돈을 잃게 됩니다. PayPal 계정 사용자 지식 없이 누구나 쉽게 돈을 훔칠 수 있습니다.

위의 이유로 브라우저는 모든 교차 출처 요청을 차단합니다.

CORS(Cross-Origin Resource Sharing)란 무엇입니까?

CORS는 신뢰할 수 있는 도메인에서 교차 출처 요청을 보내도록 브라우저에 지시하기 위해 서버에서 사용하는 헤더 기반 보안 메커니즘입니다.
브라우저에 의해 차단된 교차 출처 요청을 피하기 위해 사용되는 CORS 헤더로 활성화된 서버.

CORS는 어떻게 작동합니까?

서버가 이미 CORS 구성에서 신뢰할 수 있는 도메인을 정의했기 때문입니다. 서버에 요청을 보내면 응답은 브라우저에 요청된 도메인이 헤더에서 신뢰할 수 있는지 여부를 알려줍니다.

두 가지 유형의 CORS 요청이 있습니다.

  • 간단한 요청
  • 실행 전 요청

간단한 요청:

CORS-simple 요청 흐름은 교차 출처 요청을 보내지만 응답을 받았을 때 알려줍니다. 헤더를 확인합니다.

  • 브라우저는 출처(https://app.geekflare.com) 가 있는 교차 출처 도메인으로 요청을 보냅니다 .
  • 서버는 허용된 방법허용된 출처 와 함께 해당 응답을 다시 보냅니다 .
  • 요청을 받은 후 브라우저는 보낸 원본 헤더 값( https://app.geekflare.com )과 받은 access-control-allow-origin 값( https://app.geekflare.com )이 동일하거나 와일드카드(*). 그렇지 않으면 CORS 오류가 발생합니다.

비행 전 요청:

CORS-Preflight Request Image which show the flow of cross-origin request with OPTIONS preflight request before sending actual request for verifying headers.

  • 메소드(PUT, DELETE) 또는 사용자 정의 헤더 또는 다른 콘텐츠 유형 등과 같은 교차 출처 요청의 사용자 정의 요청 매개변수에 따라. 브라우저는 실제 요청이 안전한지 확인하기 위해 실행 전 OPTIONS 요청을 보내기로 결정합니다. 아니면.
  • 응답(상태 코드: 204, 콘텐츠 없음을 의미)을 수신한 후 브라우저는 실제 요청에 대한 액세스 제어 허용 매개변수를 확인합니다. 서버에서 요청 매개변수를 허용하는 경우. 전송 및 수신된 실제 교차 출처 요청

access-control-allow-origin: * 이면 모든 출처에 대해 응답이 허용됩니다. 그러나 필요한 경우가 아니면 안전하지 않습니다.

CORS를 활성화하는 방법?

모든 도메인에 대해 CORS를 활성화하려면 CORS 헤더를 활성화하여 출처, 메서드, 사용자 지정 헤더, 자격 증명 등을 허용합니다.

브라우저는 서버에서 CORS 헤더를 읽고 요청 매개변수를 확인한 후에만 클라이언트의 실제 요청을 허용합니다.

  • Access-Control-Allow-Origin: 정확한 도메인 지정(https://app.geekflate.com, https://lab.geekflare.com) 또는 와일드카드(*)
  • Access-Control-Allow-Methods: 우리에게 필요한 HTTP 메소드(GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)를 허용합니다.
  • Access-Control-Allow-Headers: 특정 헤더만 허용(Authorization, csrf-token)
  • Access-Control-Allow-Credentials: 교차 출처 자격 증명(쿠키, 인증 헤더)을 허용하는 데 사용되는 부울 값입니다.
  • Access-Control-Max-Age: 브라우저가 실행 전 응답을 잠시 동안 캐시하도록 지시합니다.
  • Access-Control-Expose-Headers: 클라이언트 측 스크립트에서 액세스할 수 있는 헤더를 지정합니다.

Apache 및 Nginx 웹 서버에서 CORS를 활성화하려면 이 자습서를 따르십시오.

ExpressJS에서 CORS 활성화

CORS가 없는 ExpressJS 앱의 예를 살펴보겠습니다.

 const express = require('express'); const app = express() app.get('/users', function (req, res, next) { res.json({msg: 'user get'}) }); app.post('/users', function (req, res, next) { res.json({msg: 'user create'}) }); app.put('/users', function (req, res, next) { res.json({msg: 'User update'}) }); app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') })

위의 예에서는 POST, PUT, GET 메서드에 대해 사용자 API 엔드포인트를 활성화했지만 DELETE 메서드는 활성화하지 않았습니다.

ExpressJS 앱에서 CORS를 쉽게 활성화하려면 cors를 설치할 수 있습니다.

 npm install cors

접근-제어-허용-원점

모든 도메인에 대해 CORS 활성화

 app.use(cors({ origin: '*' }));

단일 도메인에 대한 CORS 활성화

 app.use(cors({ origin: 'https://app.geekflare.com' }));

원본 https://app.geekflare.comhttps://lab.geekflare.com에 대해 CORS를 허용하려는 경우

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ] }));

액세스 제어 허용 방법

모든 방법에 대해 CORS를 활성화하려면 ExpressJS의 CORS 모듈에서 이 옵션을 생략하십시오. 그러나 특정 메소드(GET, POST, PUT)를 활성화하려면.

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'] }));

액세스 제어 허용 헤더

기본값 이외의 헤더가 실제 요청과 함께 전송되도록 허용하는 데 사용됩니다.

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'] }));

액세스 제어 허용 자격 증명

withCredentials 가 true 로 설정된 경우에도 요청 시 자격 증명을 허용하도록 브라우저에 지시하지 않으려면 이것을 생략하십시오 .

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true }));

액세스 제어 최대 연령

지정된 초 동안 캐시에 실행 전 응답 정보를 캐시하도록 브라우저에 알립니다. 응답을 캐시하지 않으려면 이것을 생략하십시오.

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600 }));

캐시된 실행 전 응답은 브라우저에서 10분 동안 사용할 수 있습니다.

액세스 제어 노출 헤더

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['Content-Range', 'X-Content-Range'] }));

wildcard(*)를 exposedHeaders넣으면 Authorization 헤더가 노출되지 않습니다. 따라서 아래와 같이 명시적으로 노출해야 합니다.

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['*', 'Authorization', ] }));

위의 내용은 모든 헤더와 Authorization 헤더도 노출합니다.

HTTP 쿠키란 무엇입니까?

쿠키는 서버가 클라이언트 브라우저에 보내는 작은 데이터 조각입니다. 이후 요청에서 브라우저는 모든 요청에 ​​대해 동일한 도메인과 관련된 모든 쿠키를 보냅니다.

쿠키에는 필요에 따라 쿠키가 다르게 작동하도록 정의할 수 있는 속성이 있습니다.

  • 이름 쿠키의 이름입니다.
  • 값: 쿠키 이름에 해당하는 쿠키의 데이터
  • 도메인: 쿠키는 정의된 도메인에만 전송됩니다.
  • 경로: 정의된 URL 접두사 경로 뒤에만 쿠키가 전송됩니다. path='admin/'과 같은 쿠키 경로를 정의했다고 가정합니다. URL https://geekflare.com/expire/에 대해 쿠키가 전송되지 않았지만 URL 접두사가 https://geekflare.com/admin/과 함께 전송되었습니다.
  • Max-Age/Expires(초 단위 숫자): 쿠키가 만료되는 시점입니다. 쿠키의 수명은 지정된 시간 후에 쿠키를 무효화합니다.
  • HTTPOnly(Boolean): 백엔드 서버는 해당 HTTPOnly 쿠키에 액세스할 수 있지만 true인 경우 클라이언트 측 스크립트에는 액세스할 수 없습니다.
  • 보안(부울): true인 경우에만 쿠키가 SSL/TLS 도메인을 통해 전송됩니다.
  • sameSite(string [Strict, Lax, None]): 사이트 간 요청에서 전송되는 쿠키를 활성화/제한하는 데 사용됩니다. 쿠키 sameSite 에 대한 자세한 내용은 MDN을 참조하십시오. Strict, Lax, None의 세 가지 옵션을 허용합니다. 쿠키 구성 sameSite=None에 대해 쿠키 보안 값이 true로 설정되었습니다.

토큰에 대한 HTTPOnly 쿠키가 필요한 이유는 무엇입니까?

서버에서 보낸 액세스 토큰을 로컬 저장소 , 인덱스 DB, 쿠키 (HTTPOnly가 true로 설정되지 않음)와 같은 클라이언트 측 저장소에 저장하는 것은 XSS 공격에 더 취약합니다. 페이지 중 하나가 XSS 공격에 취약하다고 가정합니다. 공격자는 브라우저에 저장된 사용자 토큰을 오용할 수 있습니다.

HTTPOnly 쿠키는 서버/백엔드에서만 설정/얻을 수 있지만 클라이언트 측에서는 설정/얻을 수 없습니다.

해당 HTTPonly 쿠키에 액세스하도록 제한된 클라이언트 측 스크립트입니다. 따라서 HTTPOnly 쿠키는 XSS 공격에 취약하지 않으며 더 안전합니다. 서버에서만 액세스할 수 있기 때문입니다.

CORS 지원 백엔드에서 HTTPOnly 쿠키 활성화

CORS에서 쿠키를 활성화하려면 애플리케이션/서버에서 아래 구성이 필요합니다.

  • Access-Control-Allow-Credentials 헤더를 true로 설정합니다.
  • Access-Control-Allow-Origin 및 Access-Control-Allow-Headers는 와일드카드(*)가 아니어야 합니다.
  • 쿠키 sameSite 속성은 없음이어야 합니다.
  • sameSite 값을 없음으로 활성화하려면 secure 값을 true로 설정합니다. SSL/TLS 인증서가 있는 백엔드가 도메인 이름에서 작동하도록 활성화합니다.

로그인 자격 증명을 확인한 후 HTTPOnly 쿠키에 액세스 토큰을 설정하는 예제 코드를 살펴보겠습니다.

 const express = require('express'); const app = express(); const cors = require('cors'); app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['*', 'Authorization' ] })); app.post('/login', function (req, res, next) { res.cookie('access_token', access_token, { expires: new Date(Date.now() + (3600 * 1000 * 24 * 180 * 1)), //second min hour days year secure: true, // set to true if your using https or samesite is none httpOnly: true, // backend only sameSite: 'none' // set to none for cross-request }); res.json({ msg: 'Login Successfully', access_token }); }); app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') });

백엔드 언어 및 웹 서버에서 위의 4단계를 구현하여 CORS 및 HTTPOnly 쿠키를 구성할 수 있습니다.

위의 단계에 따라 CORS를 활성화하기 위해 Apache 및 Nginx에 대한 이 자습서를 따를 수 있습니다.

withCredentials for Cross-Origin 요청

기본적으로 동일한 출처 요청으로 전송되는 자격 증명(쿠키, 권한 부여). 교차 출처의 경우 withCredentials를 true로 지정해야 합니다.

XMLHttpRequest API:

 var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://api.geekflare.com/user', true); xhr.withCredentials = true; xhr.send(null);

가져오기 API:

 fetch('http://api.geekflare.com/user', { credentials: 'include' });

제이쿼리 아약스:

 $.ajax({ url: 'http://api.geekflare.com/user', xhrFields: { withCredentials: true } });

액시오스 :

 axios.defaults.withCredentials = true

결론

위의 기사가 CORS의 작동 방식을 이해하고 서버의 교차 출처 요청에 대해 CORS를 활성화하는 데 도움이 되기를 바랍니다. HTTPOnly에 쿠키를 저장하는 것이 안전한 이유와 교차 출처 요청을 위해 withCredentials가 클라이언트에서 사용되는 방법.