인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

gkdlsb1234님의 프로필 이미지

작성한 질문수

[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스

03-CORS

특정 API, 특정 IP 허용 (단일경로에 CORS 활성화)

해결된 질문

작성

·

147

0

 https://github.com/expressjs/cors?tab=readme-ov-file#enable-cors-for-a-single-route
참고하여
작성 해 보았습니다.

* 제일 하단부에는 전체 코드 첨부 하였습니다. *


문제는 2가지 입니다.
1. 단일 경로에 CORS 활성화를 구현하고 싶었으며
corsOptions 에 methods 를 'POST, OPTIONS'만 추가하였음에도 get은 호출시 허용이 되었습니다.

app.get('/users/', cors(corsOptions), function (req, res) {

제가 구현하고 싶었던 코드의 의도는 특정 IP만 사용 허용 이기 때문에 위와 같이 구현하고 싶었습니다.
하지만 아래와 같이 호출하여도 허용이 되었습니다.

app.get('/users/', (req, res) => {

나머지 문제는

위와 반대로 POST의 경우에는

app.post('/token/phone', cors(corsOptions), (req, res) => {

위처럼 작성 옵션도 적용되었음에도 불구하고 CORS 가 계속 발생합니다.

아래는 작성한 node 코드입니다.

import express from 'express'
import { createTokenOfPhone } from '../../../section01/01-03-token-api-facade/index.js'
import { swaggerUi, specs } from "./swagger/swagger.config.js";
import cors from 'cors'

const app = express()
app.use(express.json()) 

// Swagger API
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(specs, { explorer: true }));

// 허용할 IP WhiteList
const whitelist = [
  'http://127.0.0.1:5500'
];

const corsOptions = {
  origin: (origin, callback) => {
    if (whitelist.indexOf(origin) !== -1 || !origin) { // !origin은 로컬 요청을 허용
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  origin: whitelist,
  methods: 'POST, OPTIONS',
};

//CORS 설정
// app.use(cors())
// app.use(cors(corsOptions)) // 특정 IP 허용

app.get('/users/', cors(corsOptions), function (req, res) {
// app.get('/users/', (req, res) => {
  res.json({msg: 'getdms 왜 됨?'})
})

// OPTIONS 요청에 대한 응답 처리
app.options('/users/', cors(corsOptions), (req, res) => {
  res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  res.sendStatus(204); // No Content
});

// app.post('/token/phone', (req, res) => {
app.post('/token/phone', cors(corsOptions), (req, res) => {
  const {phoneNo} = req.body;
  console.log(`phoneNo ${phoneNo}`)
  const requestToken = createTokenOfPhone(phoneNo);
  const resultData = {
    data: requestToken
  };
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.send(resultData);
});

app.listen(3000); // 포트번호

아래는 get , options, post 를 호출하는데 사용한 코드입니다.
주석 되어있는 get 호출시에는 호출안되게 옵션 설정해도 호출되고
주석 안한 post 호출시에는 정상 호출되어야 할 것 같은데 안됩니다....

<script>
  let resultToken = '';
  const certification = () => {
    let myPhoneNo = document.getElementById("phoneNo").value;
    // let api = axios.options("http://localhost:3000/users");
    // let api = axios.get("http://localhost:3000/users");
    let api = axios.post("http://localhost:3000/token/phone", {phoneNo: myPhoneNo});
  };
</script>
<body>
  <div>휴대폰번호: 
    <input type="text" id="phoneNo">
    <button onclick="certification()">인증하기</button>
    <br/>
    <div>인증상태</div>
    <button>회원가입하기</button>
  </div>
</body>

 

 

답변 2

0

노원두님의 프로필 이미지
노원두
지식공유자

안녕하세요! gkdlsb1234님!

 

위에서 질문주신 2가지 모두 각각 답변 드려볼게요!
따라서, 맨 마지막까지 봐주세요!^^
먼저, 답변을 드리기 이전에 CORS와 preflight 에 대한 간략한 설명 후 답변을 드릴게요!



1. CORS와 preflight 간략 설명
=> 크로스사이트(서로다른 도메인 또는 포트)간에 API를 요청할 때, 브라우저의 쿠키 등을 보호하기 위해 요청 전 안전한지 검사를 위한 사전 요청(preflight)이 들어가게 됩니다!
이 요청은 OPTIONS 메서드로 요청되며, 이 요청의 응답을 받아온 브라우저는, 해당 응답을 열어서 허가된 도메인, 메서드, 헤더 등이 일치하는 경우진짜 요청을 이어서 보내게 돼요!

  • 참고1) 브라우저의 안전을 위하는 것이므로 브라우저에서 검사하고 브라우저에서 보낼지 말지 판단

  • 참고2) 간단한 요청(simple request)의 경우에는 preflight 생략

     

image.png

(위아래 순서가 바뀌어 보이는 것은 실제 순서와 상관없으므로 무시하셔도 됩니다)




  1. 자! 여기까지 이해를 하셨다면! 다시 본격적인 질문으로 돌아가 볼까요?!
    질문1) 아니!, preflight 요청 후 진짜 요청이 나가는거 알겠다 이거야!
    preflight 요청의 응답으로 허가된 Method에 GET, POST 등을 허가한 적이 없는데, 왜 요청이 나가는 거냐고?!

    => 네! 많이 궁금하셨죠?! 메서드 옵션엔 예외가 있는데요!
    이 예외를 CORS-safelisted method 라고 부른답니다!
    이 그룹에 해당되는 메서드는 GET, POST, HEAD 가 있어요!
    이녀석들은 메서드의 허용여부에 상관없이 점검을 패스하는 것이랍니다!
    (이유: 너무 일반적인 사용으로 매번 체크하는 리소스 문제 해결 위함)

    아래 관련 링크와 사진을 첨부하여 드립니다

image.png


출처)
[출처1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
[출처2] https://fetch.spec.whatwg.org/#cors-safelisted-method



3. 자! 그러면, 실제 실습도 해보면 좋겠죠?!

아래는 GET만 허용해 놓았는데, POST요청에 성공한 결과예요!

image.pngimage.pngimage.png



다음으로, POST만 허용해 놓았는데 GET과 HEAD가 성공한 결과를 볼까요?!

image.pngimage.pngimage.png

 








마지막으로, CORS-safelisted methods외의 DELETE, PUT 등은 허용이 안되므로 실패하는지 확인해 보면 되겠죠?!

image.pngimage.pngimage.pngimage.png




여기까지 질문1)에 대한 답변이에요!




다음으로, 질문2)에 대한 답변을 드려볼게요!

올려주신 코드를 살펴보니 preflight를 직접 처리하기 위해 options api를 직접 만드시길 원하시는 것 같아요!
이러한 경우에는 http://localhost:3000/token/phone 으로 요청을 하셨기 때문에
app.options("/token/phone", () => ...) 엔드포인트에 해당하는 옵션 메서드가 존재해야합니다!
질문주신 코드에는 app.options("/users/", () => ...) 관련 엔드포인트밖에 없네요!^^

0

인프런 AI 인턴님의 프로필 이미지

안녕하세요, 인프런 AI 인턴입니다. gkdlsb1234님!

특정 API에 대해 CORS 설정을 제대로 구현하는 것은 서버와 클라이언트 간의 보안 및 데이터 전송 정책을 올바르게 설정하는데 중요한 부분입니다. 이와 관련해 두 가지 문제를 설명해 드리겠습니다.

1. CORS 설정에서 GET 요청이 허용되는 문제

GET 요청이 허용되는 이유는 단순히 methods 옵션을 POST, OPTIONS으로 설정하더라도, Express와 CORS 패키지는 기본적으로 다른 모든 HTTP 메소드에 대해서도 허용을 진행할 수 있습니다. 특히, 브라우저의 검사 도구(Network 탭)에서는 브라우저 캐싱 및 다른 상황 때문에 GET 요청이 성공적으로 이루어질 수 있습니다.

해결 방법으로는 서버 레벨에서 특정 메소드에 대해서만 요청을 허용하도록 로직을 구성하는 것입니다. 예를 들면:

app.get('/users/', (req, res, next) => {
  res.setHeader('Allow', 'POST');
  res.status(405).send("Method Not Allowed for GET requests");
});

2. POST 요청에서 CORS 에러가 발생하는 문제

POST 요청에서 CORS 에러가 발생하는 이유는 서버에서 CORS 설정이 부분적으로 잘못 설정되었기 때문일 수 있습니다. 주의해야 할 사항은 다음과 같습니다.

  • Ensure OPTIONS 요청을 다룰 때, 서버가 적절히 동작하도록 처리하고 있는지 확인합니다. 예를 들어, OPTIONS 핸들러가 명확히 정의되어야 합니다.
  • app.post('/token/phone', cors(corsOptions), ...) 부분의 corsOptions 설정을 다시 한번 확인해 보세요. 허용할 출처들이 정확하게 구조화되어야 합니다.

전체적인 확인 사항

  1. CORS 설정 시 명확하게 허용할 메소드 제한:

    • methods 필드를 적절히 확인하고, Access-Control-Allow-Methods 헤더를 적절하게 추가합니다.
  2. 브라우저와 서버 설정 검사:

    • 브라우저의 저장된 캐시나 개발자 도구를 활용하여 정확한 요청/응답을 확인합니다.
  3. Express 미들웨어 순서 점검:

    • 미들웨어가 적절한 순서로 호출되고 있는지 확인하세요. Express에서 미들웨어는 순서가 중요합니다.

관련하여 보다 더 깊이 이해하실 수 있는 강의 또는 문서가 필요하시면 Express 공식 문서를 참조하시기 바랍니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.