Cookie๐ช ์ ์ต์
์ ์ค์ ํ๋ ๋ฐฉ๋ฒ๊ณผ,
iframe์ ์ฌ์ฉํ์ ๋ cookie๊ฐ ์ ์ก๋์ง ์์๋ ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐ ํ๋์ง ์ ๋ฆฌํด๋ณด์.
์ฟ ํค์ ๋ชจ๋ ์ต์ ๋ค๊ณผ ๊ทธ์ ๋ํ ์ค๋ช ์ ๊ฐ์ฅ ํ๋จ์ ์ค๋ช ํ์๋ค. ์ฟ ํค ์ต์ ๋ง์ด ๊ถ๊ธํ๋ค๋ฉด ๊ฐ์ฅ ํ๋จ์ผ๋ก ๋ฐ๋ก ๋ด๋ ค๊ฐ์!
๋ฌธ์ ์ํฉ
ํ๋ ฅํ๋ ์ ์ฒด์์ ๋ณธ์ธ๋ค ์น ํ์ด์ง์์ iframe ํ๊ทธ๋ฅผ ํตํด ์ฐ๋ฆฌ ์๋ฒ ํ์ด์ง๋ฅผ ๋ถ๋ฌ์ค๊ณ ๋ก๊ทธ์ธ์ ํ๋ ค๊ณ ํ์ง๋ง, ๋ก๊ทธ์ธ์ด ์๋๋ ์ด์๊ฐ ์๋ค๊ณ ํ์๋ค.
ํ์ฌ ํ๋ก์ ํธ์์ ๋ก๊ทธ์ธ๊ด๋ฆฌ๋ฅผ ์ฟ ํค๋ฅผ ํตํด ํ๊ณ ์์๊ธฐ์, ์ฟ ํค์ ๊ด๋ จ๋์์ ๊ฑฐ๋ผ ์ง์ํ์ผ๋ฉฐ,
CSRF๊ณต๊ฒฉ์ ๋ง๊ธฐ ์ํด์ chrome80๋ถํฐ sameSite์ต์
์ ๊ธฐ๋ณธ ๊ฐ์ Lax๋ก ๋ฐ๊พผ ๊ฒ์ ์๊ณ ์์๊ธฐ์,
๊ทธ๊ฒ์ด ์์ธ ์ด๋ผ๊ณ ์๊ฐํ๊ณ ์ ๊ทผํ๋ค.
์์ธ ํ์
1. ์ฟ ํค ์ต์ ์ฒดํฌ
ํ์ฌ ์นํ์ด์ง์ ์ฟ ํค ์ต์ ์ด ์ด๋ป๊ฒ ์ธํ ๋์ด ์๋์ง ๋จผ์ ํ์ธํด๋ดค๋ค.
const accessOption = {
domain: this.configService.get('FRONT_HOST'),
path: '/',
sameSite: false,
httpOnly: true,
maxAge:
Number(this.configService.get('JWT_ACCESS_TOKEN_EXPIRATION_TIME')) *
1000,
}
res.cookie('Authentication', accessToken, accessOption);
์๋... ์ sameSite ์ต์ ์ด false๋ก ์ค์ ์ด ๋ผ์์ง? ๋ด๊ฐ ์๊ธฐ๋ก๋ 'None', 'Lax', 'Strict' ์ธ๊ฐ์ง ๊ฐ์ ๊ฐ์ง๊ณ ์์๋๋ฐ,
์ด์ํด์ ์ดํด ๋ณด๊ธฐ๋ก ํ๋ค. (์ฐธ๊ณ ๋ก, ๋๋ express๋ฅผ ์ฌ์ฉํ๊ณ ์์๋ค. )
express์์ ์ ๊ณตํ๋ Response interface์ cookie ๋ฉ์๋๋ฅผ ํ๊ณ ๋ค์ด๊ฐ์ ํ์ธํด ๋ณด๋ ์๋์ฒ๋ผ ๋์ ์์๊ณ ,
cookie(name: string, val: string, options: CookieOptions): this;
๋ด๊ฐ ๊ถ๊ธํ๋ CookieOptions interface๋ฅผ ํ์ธํด๋ณด๋, ์๋์ฒ๋ผ ๋์์์๋ค.
export interface CookieOptions {
maxAge?: number;
signed?: boolean;
expires?: Date;
httpOnly?: boolean;
path?: string;
domain?: string;
secure?: boolean;
encode?: (val: string) => string;
sameSite?: boolean | 'lax' | 'strict' | 'none';
}
express์์๋ sameSite ์ต์
์ boolean ํ์
์ผ๋ก๋ ์ ๋ฌ ํ ์ ์๋ค๋ ๊ฒ์ ์์๋ค.
๊ทธ๋ฆฌ๊ณ ํ์ธ์ ํด๋ณด๋, sameSite์ false๊ฐ์ ์ฃผ๊ฒ ๋๋ฉด sameSite์ต์
์ด ์๋ ๊ฒ ์ฒ๋ผ ์ ์ฉ๋๊ฒ ๋๊ณ
์ด๋ ํฌ๋กฌ ์น ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ณธ๊ฐ์ธ 'Lax'๋ก ์ค์ ๋๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค.
ํด๊ฒฐ ๋ฐฉ๋ฒ
1. ์ฟ ํค ์ต์ ์ค์ : sameSite: 'none' && secure: true
๊ทธ๋ผ, ์ธ๋ถ ํ๋ ฅ์ฌ์์ ์ฐ๋ฆฌ ์ชฝ ์น์ ์ ๊ทผ ํ์ ๋ ์ฟ ํค๋ฅผ ๋ฐ๊ธ ํด์ฃผ๊ธฐ ์ํด์ 'none'์ผ๋ก ์ค์ ํด์ฃผ๋ฉด ๋งค์ฐ ๊ฐ๋จํ ํด๊ฒฐ ๋ ๊ฒ ๊ฐ์๋ค.
์ฌ๊ธฐ์์ ์ฃผ์ํ ์ ์ sameSite: 'none'์ ์ค์ ํ๊ฒ ๋๋ฉด ๋ณด์์ ์ต์ฝํด ์ง ์ ์๊ธฐ๋๋ฌธ์ secure ์ต์ ์ ๋ฐ๋์ true๋ก ์ค์ ํด์ฃผ์ด์ผ ํ๋ค.
๊ทธ๋์ ์ต์ข ์ ์ผ๋ก ๋ด๊ฐ ์ค์ ํ ์ฟ ํค ์ต์ ์ ์๋์ ๊ฐ์๋ค.
{
domain: this.configService.get('FRONT_HOST'),
path: '/',
sameSite: 'none',
secure: true,
maxAge:
Number(this.configService.get('JWT_ACCESS_TOKEN_EXPIRATION_TIME')) *
1000,
}
2. ํจ์ ๋ฆฌํด ํ์ ์ง์ ํ๊ธฐ
์ด๋ ๊ฒ ์ค์ ์ ํ๊ณ ๋๋ ์ด์ TS์์ ์๋ฌ๋ฅผ ๋ด๋ฑ๊ธฐ ์์ํ๋ค.
No overload matches this call.
Overload 1 of 3, '(name: string, val: string, options: CookieOptions): Response<any, Record<string, any>>', gave the following error.
Argument of type '{ domain: any; path: string; sameSite: string; secure: boolean; maxAge: number; }' is not assignable to parameter of type 'CookieOptions'.
Types of property 'sameSite' are incompatible.
Type 'string' is not assignable to type 'boolean | "none" | "strict" | "lax"'.
Overload 2 of 3, '(name: string, val: any, options: CookieOptions): Response<any, Record<string, any>>', gave the following error.
๋๋ ๋ถ๋ช 'none'์ด๋ผ๊ณ ๊ฐ์ ์คฌ์์๋, 'string'ํ์ ์ด๋ผ๊ณ ์ธ์์ ํ๊ณ ์๋ฌ๋ฅผ ๋ด๋ฑ๋ ๋ฌธ์ ์๋ค.
์๊ณ ๋ณด๋, ์ฟ ํค ์ต์
์ ์ค์ ํ๋ ํจ์์ ๋ฆฌํด ํ์
์ ์ง์ ํ์ง ์์์๋ค.
์๋์ฒ๋ผ ํจ์์ ๋ฆฌํด ํ์
์ ์ง์ ํด์ฃผ๊ณ ๋๋ ์๋ฌ๊ฐ ์ฌ๋ผ์ง๊ณ ์ ์์ ์ผ๋ก ์๋ํ๋ ๊ฒ์ ๋ณผ ์ ์์๋ค.
interface CookieOptions {
accessToken?: string;
refreshToken?: string;
maxAge?: number;
signed?: boolean;
expires?: Date;
httpOnly?: boolean;
path?: string;
domain?: string;
secure?: boolean;
encode?: (val: string) => string;
sameSite?: boolean | 'none' | 'strict' | 'lax';
}
getCookieWithJwtAccessToken(id: string, role: string): CookieOptions
Cookie Options
cookie: {
domain: "localhost",
//์ฟ ํค์ต์
์ ๋๋ฉ์ธ ์กด์ฌํ๋ค๋ฉด, ํด๋ผ์ด์ธํธ์์๋ ์ฟ ํค์ ๋๋ฉ์ธ ์ต์
๊ณผ ์๋ฒ์ ๋๋ฉ์ธ์ด ์ผ์นํด์ผ๋ง ์ฟ ํค์ ์ก์ด ๊ฐ๋ฅ!!!
path: '/',
//Cookie ํค๋๋ฅผ ์ ์กํ๊ธฐ ์ํ์ฌ ์์ฒญ๋๋ URL ๋ด์ ๋ฐ๋์ ์กด์ฌํด์ผ ํ๋ URL ๊ฒฝ๋ก
//path: '/docs'๋ก ์ค์ ๋๋ค๋ฉด, /docs, /docs/web, /docs/web/http ์ ๊ฐ์ ๊ฒฝ๋ก๋ค์ ๋ชจ๋ ๋งค์น๋๋ค.
maxAge: 24 * 6 * 60 * 10000, // ์ฟ ํค๊ฐ ์ ํจํ ๊ธฐ๊ฐ์ ์ ํ๋ ์ต์
(or Expires)
sameSite: "none",
//Cross-Origin ์์ฒญ์ ๋ฐ์ ๊ฒฝ์ฐ ์์ฒญ์์ ์ฌ์ฉํ ๋ฉ์๋์ ํด๋น ์ต์
์ ์กฐํฉ์ผ๋ก ์๋ฒ์ ์ฟ ํค ์ ์ก ์ฌ๋ถ๋ฅผ ๊ฒฐ์
//Lax(allow only get method), Strict(deny), none(yes for every method but needed SECURE option mandatory) ์ธ๊ฐ์ง ์ต์
์ด ์กด์ฌ
httpOnly: true,
//์๋ฐ ์คํฌ๋ฆฝํธ์์ ์ฟ ํค์ ์ ๊ทผ์ฌ๋ถ๋ฅผ ๊ฒฐ์ . true-> ๋ถ๊ฐ, ๋ง์ฝ false๋ผ๋ฉด, document.cookie๋ก ์ ๊ทผ ๊ฐ๋ฅ
secure: true,
// true์ผ ๊ฒฝ์ฐ, https์์๋ง ์ฟ ํค ์ ์ก ๊ฐ๋ฅ
}
'JavaScript' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Solved] ์ฟ ํค ์ญ์ ๊ฐ ์๋๋ ๋ฌธ์ (cookies.remove doesn't work) (0) | 2022.10.14 |
---|---|
[JS] ๊น์ ๋ณต์ฌ, ์์ ๋ณต์ฌ (๊น์ ๋ณต์ฌ ํ๋ ๋ฐฉ๋ฒ) (0) | 2022.08.26 |
์ปค๋ง(Currying) Feat. ์ปค๋ง์ผ๋ก ๋ก๊ทธ ํจ์ ๊ตฌํ (0) | 2022.07.04 |
๋ง์ฐ์ค ํ ์ด๋ฒคํธ ๋ฑ๋ก(js) (0) | 2022.06.29 |
Mixed Content ๋ฌธ์ ํด๊ฒฐํ๊ธฐ(https์์ http ์ฌ์ดํธ๋ก ์์ฒญ) (1) | 2022.06.15 |