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

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

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

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

목차

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

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

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

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

  5GHz Wi-Fi가 2.4GHz Wi-Fi보다 항상 좋은 것은 아닙니다.

예를 들어 사용자가 브라우저에서 자신의 PayPal 계정에 로그인했다고 가정해 보겠습니다. 우리가 동일한 출처 요청을 보내는 것처럼 CORS 오류/차단 없이 다른 도메인 악성.com에 로드된 스크립트에서 paypal.com으로 교차 출처 요청을 보낼 수 있다면.

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

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

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

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

CORS는 어떻게 작동합니까?

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

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

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

간단한 요청:

  • 브라우저는 출처(https://app.koreantech.org.com)가 있는 교차 출처 도메인으로 요청을 보냅니다.
  • 서버는 허용된 방법과 허용된 출처와 함께 해당 응답을 다시 보냅니다.
  • 브라우저는 요청을 받은 후 보낸 원본 헤더 값(https://app.koreantech.org.com)과 받은 access-control-allow-origin 값(https://app.koreantech.org.com)이 동일한지 확인합니다. 와일드카드

. 그렇지 않으면 CORS 오류가 발생합니다.

  • 비행 전 요청:
  • 메소드(PUT, DELETE) 또는 사용자 정의 헤더 또는 다른 콘텐츠 유형 등과 같은 교차 출처 요청의 사용자 정의 요청 매개변수에 따라 브라우저는 실제 요청이 안전한지 확인하기 위해 실행 전 OPTIONS 요청을 보내기로 결정합니다. 아니면.

응답(상태 코드: 204, 콘텐츠 없음을 의미)을 수신한 후 브라우저는 실제 요청에 대한 액세스 제어 허용 매개변수를 확인합니다. 서버에서 요청 매개변수를 허용하는 경우. 전송 및 수신된 실제 교차 출처 요청

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

CORS를 활성화하는 방법?

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

  • 브라우저는 서버에서 CORS 헤더를 읽고 요청 매개변수를 확인한 후에만 클라이언트의 실제 요청을 허용합니다.
  • Access-Control-Allow-Origin: 정확한 도메인(https://app.geekflate.com, https://lab.koreantech.org.com) 또는 와일드카드를 지정하려면
  • Access-Control-Allow-Methods: 우리에게 필요한 HTTP 메소드(GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)를 허용합니다.
  • Access-Control-Allow-Headers: 특정 헤더만 허용(Authorization, csrf-token)
  • Access-Control-Allow-Credentials: 교차 출처 자격 증명(쿠키, 인증 헤더)을 허용하는 데 사용되는 부울 값입니다.
  내 iPhone 또는 iPad가 바이러스에 감염될 수 있습니까?

Access-Control-Max-Age: 브라우저에 프리플라이트 응답을 잠시 동안 캐시하도록 지시합니다.

Access-Control-Expose-Headers: 클라이언트 측 스크립트에서 액세스할 수 있는 헤더를 지정합니다.

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

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')
})

ExpressJS에서 CORS 활성화

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

npm install cors

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

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

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

접근-제어-허용-원점

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

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

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

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

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

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

액세스 제어 허용 방법

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

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

액세스 제어 허용 헤더

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

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
}));

액세스 제어 허용 자격 증명

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,
    maxAge: 600 
}));

액세스 제어 최대 연령

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

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']
}));

캐시된 실행 전 응답은 브라우저에서 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: ['*', 'Authorization', ]
}));

액세스 제어 노출 헤더

  웹사이트에서 사용자의 실제 위치를 볼 수 있습니까?

와일드카드를 넣으면

exposedHeaders에서는 Authorization 헤더를 노출하지 않습니다. 따라서 아래와 같이 명시적으로 노출해야 합니다.

위의 모든 헤더와 Authorization 헤더도 노출됩니다.

  • HTTP 쿠키란 무엇입니까?
  • 쿠키는 서버가 클라이언트 브라우저에 보내는 작은 데이터 조각입니다. 이후 요청에서 브라우저는 모든 요청에 ​​대해 동일한 도메인과 관련된 모든 쿠키를 보냅니다.
  • 쿠키에는 필요에 따라 쿠키가 다르게 작동하도록 정의할 수 있는 속성이 있습니다.
  • 이름 쿠키의 이름입니다.
  • 값: 쿠키 이름에 해당하는 쿠키의 데이터
  • 도메인: 쿠키는 정의된 도메인에만 전송됩니다.
  • 경로: 정의된 URL 접두사 경로 뒤에만 쿠키가 전송됩니다. path=’admin/’과 같은 쿠키 경로를 정의했다고 가정합니다. URL https://koreantech.org.com/expire/에 대해 쿠키가 전송되지 않았지만 URL 접두어가 https://koreantech.org.com/admin/과 함께 전송되었습니다.
  • Max-Age/Expires(초 단위 숫자): 쿠키가 만료되는 시점입니다. 쿠키의 수명은 지정된 시간이 지나면 쿠키를 무효화합니다. [Strict, Lax, None]HTTPOnly(Boolean): 백엔드 서버는 해당 HTTPOnly 쿠키에 액세스할 수 있지만 true인 경우 클라이언트 측 스크립트에는 액세스할 수 없습니다. 보안(부울): true인 경우 SSL/TLS 도메인을 통해서만 쿠키가 전송됩니다.sameSite(문자열

): 사이트 간 요청에서 전송되는 쿠키를 활성화/제한하는 데 사용됩니다. 쿠키 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는 와일드카드가 아니어야 합니다.

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') 
}); 

.

쿠키 sameSite 속성은 없음이어야 합니다.

sameSite 값을 없음으로 활성화하려면 secure 값을 true로 설정합니다. SSL/TLS 인증서가 있는 백엔드가 도메인 이름에서 작동하도록 활성화합니다.

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

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

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

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

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

withCredentials for Cross-Origin 요청

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

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

axios.defaults.withCredentials = true

XMLHttpRequest API

API 가져오기

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