What is ๐Ÿช Cookie

23581 ๋‹จ์–ด deepdivesecuritydeepdive

โœ ๋“ค์–ด๊ฐ€๋ฉฐ

์„ธ์…˜์œผ๋กœ ๋ฐ”๋กœ ๋“ค์–ด๊ฐ€๋ ค๊ณ  ํ–ˆ์œผ๋‚˜ Cookie๋ฅผ ์„ค๋ช…ํ•˜์ง€ ์•Š๊ณ  ์“ฐ๊ธฐ๊ฐ€ ์• ๋งคํ•˜๋‹ค. ์ด์ „ TIL ํฌ์ŠคํŒ…์—์„œ ์กฐ๊ธˆ ์ž์„ธํ•˜๊ฒŒ ๋‹ค๋ค˜์œผ๋‹ˆ ํŽธ์•ˆํ•œ ํฌ์ŠคํŒ…์ด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

๐Ÿช Cookie

์ฟ ํ‚ค(cookie)๋ž€ HTTP์˜ ์ผ์ข…์œผ๋กœ ์ธํ„ฐ๋„ท ์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋– ํ•œ ์›น์‚ฌ์ดํŠธ๋ฅผ ๋ฐฉ๋ฌธํ•  ๊ฒฝ์šฐ ๊ทธ ์‚ฌ์ดํŠธ๊ฐ€ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด ์ธํ„ฐ๋„ท ์‚ฌ์šฉ์ž์˜ ์ปดํ“จํ„ฐ์— ์„ค์น˜๋˜๋Š” ์ž‘์€ ๊ธฐ๋ก ์ •๋ณด ํŒŒ์ผ์„ ์ผ์ปซ๋Š”๋‹ค.

์ฟ ํ‚ค๋Š” ์ž„์˜ ์กฐ๊ฐ์˜ ๋ฐ์ดํ„ฐ๋กœ์„œ, ์›น ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ์„ ๋ณ„๋˜์–ด ์ฒ˜์Œ ์†ก์‹ ๋˜๋ฉฐ ์›น ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ํด๋ผ์ด์–ธํŠธ ์ปดํ“จํ„ฐ์— ์ €์žฅ๋œ๋‹ค. ์ดํ›„ ๋ธŒ๋ผ์šฐ์ €๋Š” ์ƒํƒœ(์ด์ „ ์ด๋ฒคํŠธ ๊ธฐ์–ต)๋ฅผ ๋ฌด์ƒํƒœ HTTP ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์œ ์ž…์‹œํ‚ค๋ฉด์„œ ๋ชจ๋“  ์š”์ฒญ์„ ์„œ๋ฒ„๋กœ ๋˜๋Œ๋ ค ๋ณด๋‚ธ๋‹ค. ์ฟ ํ‚ค๊ฐ€ ์—†์œผ๋ฉด ์›น ํŽ˜์ด์ง€์˜ ๊ฐ๊ฐ์˜ ๊ฒ€์ƒ‰ ๋˜๋Š” ์›น ํŽ˜์ด์ง€์˜ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€, ๋Œ€์ฒด์ ์œผ๋กœ ์›น์‚ฌ์ดํŠธ์ƒ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๋งŒ๋“œ๋Š” ๋‹ค๋ฅธ ๋ชจ๋“  ํŽ˜์ด์ง€์™€ ๋ฌด๊ด€ํ•œ ๋ณ„๊ฐœ์˜ ์ด๋ฒคํŠธ๋กœ ์ทจ๊ธ‰๋œ๋‹ค.

HTTP๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฌด์ƒํƒœ์„ฑ์ด๋‹ค. ๋ชจ๋“  ์š”์ฒญ์€ ์ด์ „์˜ ์š”์ฒญ๊ณผ ๋ฌด๊ด€ํ•˜๋‹ค๋Š” ์ „์ œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ์ด๋ฅผ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์ดํŠธ๋Š” ์›น ๋ธŒ๋ผ์šฐ์ €์— ์ž‘์€ ํ…์ŠคํŠธ ํŒŒ์ผ์„ ๊ฑด๋‚ด์ฃผ๊ณ  ์ด๋ฅผ ํ†ตํ•˜์—ฌ ์ƒํƒœ์„ฑ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

์›น ๋ธŒ๋ผ์šฐ์ €์— ์ ์šฉ๋˜๋Š” ๋‚ด์šฉ์ด๋ผ ์›น ๋ธŒ๋ผ์šฐ์ €๋ฅผ ํ†ตํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด(์•ˆ๋“œ๋กœ์ด๋“œ, IOS) ๊ธฐ๋ณธ์œผ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋‹ค.

๐Ÿ™… Cookie ๋ณด์•ˆ์˜ ํ•œ๊ณ„

์ฟ ํ‚ค๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ ์›น ์„œ๋ฒ„์— ์˜ํ•ด ์„ค์ •๋˜์ง€๋งŒ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ๊ฐ™์€ ์Šคํฌ๋ฆฝํŠธ ์–ธ์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์— ์˜ํ•ด ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.(์Šคํฌ๋ฆฝํŠธ ์–ธ์–ด์— ์˜ํ•ด ์ฟ ํ‚ค๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํ•˜๋Š” ์ฟ ํ‚ค์˜ HttpOnly ํ”Œ๋ž˜๊ทธ๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์— ํ•œํ•ด)

Cookie๋Š” ์„ค์ •์— ๋”ฐ๋ผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ <scirpt> ๋กœ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ Cookie๋กœ ์ค‘์š”ํ•œ ์ •๋ณด๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ๋•Œ๋Š” ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•ด์•ผ ํ•œ๋‹ค.

์ฟ ํ‚ค๋Š” ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ์•„๋‹ˆ๋‹ค. ์ฟ ํ‚ค๋Š” ์ปดํ“จํ„ฐ ๋‚ด์—์„œ ํ”„๋กœ๊ทธ๋žจ์ฒ˜๋Ÿผ ์‹คํ–‰๋  ์ˆ˜ ์—†์œผ๋ฉฐ ๋ฐ”์ด๋Ÿฌ์Šค๋ฅผ ์˜ฎ๊ธธ ์ˆ˜๋„, ์•…์„ฑ์ฝ”๋“œ๋ฅผ ์„ค์น˜ํ•  ์ˆ˜๋„ ์—†๋‹ค. ํ•˜์ง€๋งŒ ์ŠคํŒŒ์ด์›จ์–ด๋ฅผ ํ†ตํ•ด ์œ ์ €์˜ ๋ธŒ๋ผ์šฐ์ง• ํ–‰๋™์„ ์ถ”์ ํ•˜๋Š”๋ฐ์— ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๊ณ , ๋ˆ„๊ตฐ๊ฐ€์˜ ์ฟ ํ‚ค๋ฅผ ํ›”์ณ์„œ ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ์›น ๊ณ„์ • ์ ‘๊ทผ๊ถŒํ•œ์„ ํš๋“ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

์ค‘์š”ํ•˜๊ฒŒ๋Š” Session ID๋Š” ์ฟ ํ‚ค๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋˜๋ฉฐ, ์ž์ž˜ํ•˜๊ฒŒ๋Š” ์‚ฌ์ดํŠธ๋ฅผ ์ด์šฉํ•œ ์‚ฌ์šฉ์ž์˜ ๊ธฐ๋ก์„ ์ถ”์ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฟ ํ‚ค๋กœ ๋ณด๋‚ผ ๋•Œ๋Š” ๋ณด์•ˆ(HTTPS)์ด ์ ์šฉ๋˜์–ด์•ผ ํ•œ๋‹ค.

๐Ÿ“ฎ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ๊ฐ„ Cookie ์ „๋‹ฌํ•˜๊ธฐ

Frontend์™€ Backend ๋„๋ฉ”์ธ ์ฃผ์†Œ๊ฐ€ ๋‹ค๋ฅธ ๊ฒฝ์šฐ Cookie ์ „๋‹ฌ์ด ๋˜์ง€ ์•Š๋Š”๋‹ค. ํŠนํžˆ Network ํƒญ์—์„œ Response Header์— Set-Cookie๋Š” ์žˆ๋Š”๋ฐ Application ํƒญ์—์„œ Cookie๊ฐ€ ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค. ํ—ค๋”์˜ ์˜ต์…˜ ์กฐ์ •์œผ๋กœ ์ด๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

client / withCredentials: true

ํ—ค๋”์— withCredentials: true ์˜ต์…˜์œผ๋กœ ๋ณด์•ˆ ํ†ต์‹  ์ค‘ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ(Cross) ์‚ฌ์ด์˜ ์ฟ ํ‚ค ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•œ๋‹ค. ํ˜„์žฌ ์‹ค์Šต ํ™˜๊ฒฝ์€ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„์˜ ๋„๋ฉ”์ธ์ด ๋‹ค๋ฅธ ์ƒํ™ฉ์ด๋‹ค.

XMLHttpRequest.withCredentials ์†์„ฑ์€ ์ฟ ํ‚ค, ๊ถŒํ•œ ๋ถ€์—ฌ ํ—ค๋” ๋˜๋Š” TLS ํด๋ผ์ด์–ธํŠธ ์ธ์ฆ์„œ์™€ ๊ฐ™์€ ์ž๊ฒฉ ์ฆ๋ช…์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์ดํŠธ ๊ฐ„ ์•ก์„ธ์Šค ์ œ์–ด ์š”์ฒญ์„ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ถ€์šธ ๊ฐ’์ž…๋‹ˆ๋‹ค.
withCredentials ์„ค์ •์€ ๋™์ผ ์‚ฌ์ดํŠธ ์š”์ฒญ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
withCredentials๊ฐ€ true๋กœ ์„ค์ •๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ์ž์‹ ์˜ ๋„๋ฉ”์ธ์— ๋Œ€ํ•œ ์ฟ ํ‚ค ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. withCredentials๋ฅผ true๋กœ ์„ค์ •ํ•˜์—ฌ ์–ป์€ ํƒ€์‚ฌ ์ฟ ํ‚ค๋Š” ์—ฌ์ „ํžˆ ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…์„ ๋”ฐ๋ฅด๋ฏ€๋กœ document.cookie ๋˜๋Š” ์‘๋‹ต ํ—ค๋”๋ฅผ ํ†ตํ•ด ์š”์ฒญํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ์—์„œ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
XMLHttpRequest.withCredentials - mozilla

axios
      .post(
        'https://localhost:4000/users/login',
        {
          userId: this.state.username,
          password: this.state.password,
        },
        { 'Content-Type': 'application/json', withCredentials: true } // <------
      )

server / credentials: true

์ด ์˜ต์…˜์„ ๋„๋ฉด ์ฒซ๋ฒˆ์งธ ๋กœ๊ทธ์ธ ์š”์ฒญ์—์„œ ์•„๋ž˜์˜ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ž์ฃผ ๋ณด๋˜ ์—๋Ÿฌ๋‹ค. CORS ์ •์ฑ…์— ์œ„๋ฐ˜๋œ๋‹ค๋Š” ๋‚ด์šฉ์ด๋ฉฐ 'XMLHttpRequest์— ์˜ํ•ด ์‹œ์ž‘๋œ ์š”์ฒญ์˜ ์ž๊ฒฉ ์ฆ๋ช… ๋ชจ๋“œ๋Š” withCredentials ์†์„ฑ์— ์˜ํ•ด ์ œ์–ด๋œ๋‹ค'๊ณ  ํ•œ๋‹ค.

Access to XMLHttpRequest at 'https://localhost:4000/users/login' from origin 'https://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

์•„๋ž˜๋Š” ๊ณต์‹ ๋ฌธ์„œ์—์„œ ์–ธ๊ธ‰ํ•˜๋Š” Access-Control-Allow-Credentials ์˜ต์…˜์— ๋Œ€ํ•œ ์„ค๋ช…์ด๋‹ค. ์š”์•ฝํ•˜๋ฉด ๋‹ค๋ฅธ ๋„๋ฉ”์ธ๊ฐ„ ํ—ค๋”๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

์‘๋‹ตํ—ค๋” Access-Control-Allow-Credentials ๋Š” ์š”์ฒญ์˜ ์ž๊ฒฉ์ฆ๋ช… ๋ชจ๋“œ(Request.credentials)๊ฐ€ "include" ์ผ๋•Œ, ๋ธŒ๋ผ์šฐ์ €๋“ค์ด ์‘๋‹ต์„ ํ”„๋กœํŠธ์—”๋“œ ์ž๋ฐ”์ŠคํŠธ๋ฆฝํŠธ ์ฝ”๋“œ์— ๋…ธ์ถœํ• ์ง€์— ๋Œ€ํ•ด ์•Œ๋ ค์ค๋‹ˆ๋‹ค.
์š”์ฒญ์˜ ์ž๊ฒฉ์ฆ๋ช… ๋ชจ๋“œ๊ฐ€ (Request.credentials)๊ฐ€ "include" ์ผ ๋•Œ, Access-Control-Allow-Credentials ๊ฐ’์ด true ์ผ ๊ฒฝ์šฐ์—๋งŒ ๋ธŒ๋ผ์šฐ์ €๋“ค์€ ํ”„๋กœํŠธ์—”๋“œ ์ž๋ฐ”์ŠคํŠธ๋ฆฝํŠธ์— ์‘๋‹ต์„ ๋…ธ์ถœ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
Access-Control-Allow-Credentials - mozilla

credentials: Configures the Access-Control-Allow-Credentials CORS header. Set to true to pass the header, otherwise it is omitted.
cors - express

server / origin: 'https://localhost:3000' or true

origin์„ *๋กœ ์ ์œผ๋ฉด ์•ˆ๋œ๋‹ค. ์ •ํ™•ํ•˜๊ฒŒ ์ ์–ด์•ผ ์ฟ ํ‚ค๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค. express์—์„  true๋„ ๋ฐ›์•„์ค€๋‹ค.
๋กœ๊ทธ์ธ ์š”์ฒญ์ด ์ฒด์ธ์œผ๋กœ ์—ฐ๊ฒฐ๋˜์–ด์„œ 2๋ฒˆ ์ด๋ฃจ์–ด์ง€๋Š”๋ฐ ๊ทธ ๊ณผ์ •์—์„œ ์ž„์‹œ ํ—ค๋”๊ฐ€ ๋งŒ๋“ค์–ด์ ธ CORS ์ •์ฑ…์— ์œ„๋ฐ˜ํ•œ๋‹ค. ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ๋ณด๋ฉด ์ฒซ Login Post ์š”์ฒญ์€ ๋„˜์–ด๊ฐ€์ง€๋งŒ ์ด์–ด์ง€๋Š” userinfo Get ์š”์ฒญ์€ CORS ์—์„œ ๋ง‰ํžŒ๋‹ค.
๊ทธ ์ด์œ ๋Š” origin ์˜ต์…˜์ด *์ผ ๋•Œ๋Š” 'Access-Control-Allow-Origin' ์˜ต์…˜์ด ์ง€์›๋˜์ง€ ์•Š๋Š”๋‹ค.
Reason: Credential is not supported if the CORS header โ€˜Access-Control-Allow-Originโ€™ is *

app.use(
  cors({
    origin: 'https://localhost:3000', // <-----
    methods: ['GET', 'POST', 'OPTIONS'],
    credentials: true, // <------
  })
);

๐Ÿ’ป NodeJS Cookie ๊ตฌํ˜„

Headers Set-Cookies

๋ฐฐ์—ด ์š”์†Œ๋กœ ๋“ค์–ด๊ฐ€๋ฉฐ ['ํ‚ค=๊ฐ’', 'ํ‚ค=๊ฐ’'] ํ˜•ํƒœ๋กœ ์ €์žฅ๋œ๋‹ค.
res.writeHead ๋ฉ”์†Œ๋“œ๋กœ ์ฟ ํ‚ค๋ฅผ ์ ์–ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

res.writeHead(200, {
  'Set-Cookie': [
    `my-cookie=programmer`,
  ],
  'Content-Type': 'text/html'
});

res.setHeader ๋ฉ”์†Œ๋“œ๋กœ ๋‹ฌ์•„์ค„ ์ˆ˜ ์žˆ๋‹ค. ์˜ต์…˜์„ ๋‹ฌ์•„์ฃผ๊ณ  ์‹ถ์œผ๋ฉด ํ•ด๋‹น ์ฟ ํ‚ค ๋”ฐ์˜ดํ‘œ ์•ˆ์—์„œ ๋’ค์— ์„ธ๋ฏธ์ฝœ๋ก  ;์„ ์ฐ๊ณ  ์ฟ ํ‚ค๋ณ„๋กœ ๊ฐ๊ฐ ์˜ต์…˜์„ ์ž…๋ ฅํ•œ๋‹ค.

res.setHeader('Set-Cookie', 
  [`my-cookie=programmer; HttpOnly`,
  `more-data=imNotSecure`]
);

cookie-parser ๋ชจ๋“ˆ

Token ์„น์…˜์—์„œ ๋“ฑ์žฅํ–ˆ๋‹ค. cookie-parser ๋ชจ๋“ˆ์„ ํ™œ์šฉํ•œ๋‹ค.
ํ•ด๋‹น ์„น์…˜์—์„  tokenFunction ๋””๋ ‰ํ† ๋ฆฌ์— ๋ชจ๋“  ๊ธฐ๋Šฅ๋“ค์„ ๊ตฌํ˜„ํ•ด๋†“๊ณ  ์“ฐ๊ธฐ๋งŒ ํ•ด์„œ ์™€๋‹ฟ์ง€๊ฐ€ ์•Š์•˜๋‹ค.

const {
  generateAccessToken,
  generateRefreshToken,
  sendRefreshToken,
  sendAccessToken,
} = require('../tokenFunctions');
...

    .then((data) => {
      if (!data) {
        // return res.status(401).send({ data: null, message: 'not authorized' });
        return res.json({ data: null, message: 'not authorized' });
      }
      delete data.dataValues.password;
      // ์ฟ ํ‚ค์— ์“ฐ๋Š” ๋ถ€๋ถ„์„ ๋ณ„๋„์˜ ํ•จ์ˆ˜๋กœ ๋นผ๋†จ๋‹ค. 
      const accessToken = generateAccessToken(data.dataValues);
      const refreshToken = generateRefreshToken(data.dataValues);

      sendRefreshToken(res, refreshToken);
      sendAccessToken(res, accessToken);
    })
    .catch((err) => {
      console.log(err);
    });

์‹ค์ œ๋กœ tokenFunctions ํŒŒ์ผ์„ ์—ด์–ด๋ณด๋ฉด ์ฟ ํ‚ค์— ์“ฐ๋Š” ๋™์ž‘์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. RefreshToken์€ ์ฟ ํ‚ค์— ํƒ‘์žฌ๋œ๋‹ค.

  sendRefreshToken: (res, refreshToken) => {
    res.cookie("refreshToken", refreshToken, {
      httpOnly: true,
    });
  },
  sendAccessToken: (res, accessToken) => {
    res.json({ data: { accessToken }, message: "ok" });
  },

์„œ๋ฒ„์—์„œ ์ฟ ํ‚ค๋ฅผ ์ฝ์„ ๋•Œ๋Š” req.cookies ๊ฐ์ฒด๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

const refreshToken = req.cookies.refreshToken;

express-session ๋ชจ๋“ˆ

express-session ๋ชจ๋“ˆ์„ ํ†ตํ•ด ์„ธ์…˜์„ ๋งŒ๋“œ๋Š” ๊ณผ์ •์—์„œ ์ž๋™์œผ๋กœ ์ฟ ํ‚ค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. connect.sid ๊ฐ’์„ ํ†ตํ•ด ์„ธ์…˜ ID๋ฅผ ์ „๋‹ฌํ•œ๋‹ค. ์„œ๋ฒ„์—์„œ ์ฟ ํ‚ค๋ฅผ ๋‹ฌ์•„์ฃผ๋ฉด ์›น ๋ธŒ๋ผ์šฐ์ €๋Š” ์ด ์ดํ›„๋ถ€ํ„ฐ ์„ธ์…˜ ID๊ฐ€ ๋‹ด๊ธด connect.sid ๋ผ๋Š” ์ฟ ํ‚ค๋ฅผ ๋‹ฌ๊ณ  ๋‹ค๋‹ˆ๊ฒŒ ๋œ๋‹ค. ์ด ์ด๋ฆ„์€ ๊ธฐ๋ณธ๊ฐ’์ด๋ฉฐ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

// TODO: express-session ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ์ฟ ํ‚ค ์„ค์ •์„ ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
app.use(
  session({
    secret: '@codestates', // ์•”ํ˜ธํ™”๋กœ ์“ฐ์ด๋Š” ํ‚ค
    resave: false, // ์„ธ์…˜์„ ํ•ญ์ƒ ์ €์žฅํ• ์ง€ ์„ค์ •
    saveUninitialized: true, // ์„ธ์…˜์ด ์ €์žฅ๋˜๊ธฐ ์ „ uninitialized ์ƒํƒœ๋กœ ์ €์žฅ
    cookie: { // ์„ธ์…˜ ์ฟ ํ‚ค ์„ค์ •
      domain: 'localhost', // ์ฟ ํ‚ค๋ฅผ ๋ณด๋‚ด๋Š” ์‚ฌ์ดํŠธ(์„œ๋ฒ„)๋ฅผ ๋ช…์‹œ
      path: '/', // URL ๊ฒฝ๋กœ. ํ•˜์œ„ ๊ฒฝ๋กœ๊นŒ์ง€ ๋ชจ๋‘ ํฌํ•จ.
      maxAge: 24 * 6 * 60 * 10000, // ์ฟ ํ‚ค ๋งŒ๋ฃŒ ์‹œ๊ฐ„. ์ดˆ๋‹จ์œ„.
      // XSRF(cross-site request forgery) ๊ณต๊ฒฉ ๋ฐฉ์–ด ์˜ต์…˜
      sameSite: 'None', // ์‚ฌ์ดํŠธ ์™ธ๋ถ€์—์„œ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์ฟ ํ‚ค ์ •์ฑ…
      // Strict : ์™ธ๋ถ€ ๋„๋ฉ”์ธ ์ฟ ํ‚ค ์ฐจ๋‹จ
      // Lax : ์™ธ๋ถ€ ๋„๋ฉ”์ธ ์ฟ ํ‚ค ์ผ๋ถ€ ํ—ˆ์šฉ(HTTP get method / a href / link href)
      // None : ์™ธ๋ถ€ ๋„๋ฉ”์ธ ์ฟ ํ‚ค ๋ชจ๋‘ ํ—ˆ์šฉ. ๊ฐ•์ œ๋กœ origin ์˜ต์…˜๊ณผ ๊ฐ™์ด ์จ์•ผํ•จ
      httpOnly: true, // ํด๋ผ์ด์–ธํŠธ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ฟ ํ‚ค๋ฅผ ๋ชป๋ณด๊ฒŒ ํ•จ (document.cookie)
      secure: true, // HTTPS ํ™˜๊ฒฝ๋งŒ ์ฟ ํ‚ค๋ฅผ ์ „์†ก
    },
  })
);
// CORS ์ฟ ํ‚ค ์„ค์ • ํ™œ์„ฑํ™”
app.use(
  cors({
    // origin: 'https://localhost:3000',
    origin: true,
    methods: ['GET', 'POST', 'OPTIONS'],
    credentials: true, // ๋‹ค๋ฅธ ๋„๋ฉ”์ธ๊ฐ„ ์ž๊ฒฉ์ฆ๋ช…(credential, ์ฟ ํ‚ค ํฌํ•จ)์„ ์ „์†กํ• ์ง€ ์—ฌ๋ถ€
  })
);

ํด๋ผ์ด์–ธํŠธ๋Š” ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•  ๋•Œ connect.sid ์ฟ ํ‚ค์— ๋Œ€ํ•ด์„œ ์•„๋ฌด๋Ÿฐ ์–ธ๊ธ‰์ด ์—†๋‹ค. ์ฟ ํ‚ค๋Š” ๋ธŒ๋ผ์šฐ์ €์— ์ €์žฅ๋˜๋ฉฐ ํ•ด๋‹น ์‚ฌ์ดํŠธ์— ์ ‘์†ํ•  ๋•Œ ์ž๋™์œผ๋กœ ๋‹ฌ๊ณ  ๋‹ค๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

withCredentials ์˜ต์…˜์€ Origin์ด ๋‹ค๋ฅธ ํ†ต์‹ ์—์„œ ์ฟ ํ‚ค ์กฐํšŒ๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ์˜ต์…˜์ด๋‹ค.

    axios
      .post('https://localhost:4000/users/logout', null, {
        'Content-Type': 'application/json',
        withCredentials: true,
      })
      .then(() => props.logoutHandler())
      .catch((e) => alert(e));

ํด๋ผ์ด์–ธํŠธ์—์„œ ์–ธ๊ธ‰์ด ์—†์–ด๋„ ์„œ๋ฒ„์—์„  req.session ์ด๋ผ๋Š” ๊ฐ์ฒด๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

const result = await Users.findOne({
        where: { userId: req.session.userId },
      })

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ