개발일지

개발일지 71일차

index.ys 2023. 6. 9. 21:12

Cookie 옵션 중 sameSite에 대해서 자세히 설명해주시고 Non, Strict, Lax일 때 각각 어떻게 동작하는지 설명해주세요

쿠키

  • 쿠키는 브라우저에 데이터를 저장하기 위한 수단
  • 브라우저에서 서버로 요청을 전송할 때 그 요청에 대한 응답에 Set-Cookie 헤더가 포함되어 있는 경우 브라우저는 Set-Cookie에 있는 데이터를 저장 = 쿠키 key=value로 브라우저 쿠키를 저장함

sameSite

크롬 브라우저의 sameSite 기본 속성은  "Lax"로 설정되어 있음 이는 CSRF( Cross-site-request-forgery) 사이트간 요청 위조 및 의도하지 않은 정보유출에 대한 취약성에 대처하기 위함

cross-site로 요청을 보내는 경우 쿠키의 전송제약 조건을 설정하기 위한 옵션

쿠키는 앞서 언급한 서드 파티 쿠키의 보안적 문제를 해결하기 위해 만들어진 기술입니다. 크로스 사이트(Cross-site)로 전송하는 요청의 경우 쿠키의 전송에 제한을 두도록 합니다.

Strict

- Same Site간의 요청에서만 쿠키의 전송을 허용하는 옵션, 보안에 완벽하지만 편의성이 떨어짐

- ex) www.naver.com  에 쿠키가 있는 경우 http://www에 대한 요청에 대해서만 쿠키를 전송 

Lax

- 기본적으로는 Strict 이지만, 서드파티 쿠키는 전송되지 않지만, 몇가지 예외적인 요청에는 쿠키를 전송함

- 같은 웹사이트 일대는 쿠키가 당연히 전송된다는 의미, 이 외에는 top lever navigation(웹 페이지 이동)과, 안전한 http 메서드 요청의 경우 쿠키가 전송되는 옵션 (get 요청)

- 안전하지 않은 post나 delete 요청 (서버의 상태를 바꾸는 요청)의 경우 lax옵션에서 쿠키는 전송되지 않음 서드파티 쿠키에 한함

Non

- Same Site와 cross site의 요청에도 모두 전송을 허용함 보안에 취약함, https 프로토콜 하에서 secure속성과 함께사용함

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.COOKIE_SECRET,
  saveUninitialized: true,
  resave: false,
  name: 'connect.sid',
  cookie: {
    secure: false, // if true: only transmit cookie over https, in prod, always activate this
    httpOnly: true, // if true: prevents client side JS from reading the cookie
    maxAge: 1000 * 120 * 30, // session max age in milliseconds
    sameSite: 'lax',
  },
}));

서드파티 쿠키

- 서드파티 쿠키란 사용자가 접속한 페이지와 다른 도메인으로 전송하는 쿠키

퍼스트 파티 쿠키

- 사용자가 접속한 페이지와 같은 도메인으로 전송되는 쿠키

 

helmet을 통해 구현하신 xssFilter, frameguard, noSniff가 각각 어떤 보안 위협이 있고 그걸 어떻게 방어하는지에 대해 설명해주세요.

app.use(helmet.hsts({
  maxAge: ms("1 year"),
  includeSubDomains: true
}));
// 1분동안 하나의 ip 주소에서 들어오는 request의 숫자를 100회로 제한
app.use(rateLimit({
  windowMs: 1 * 60 * 1000,
  max: 100
}));
// xss(교차 사이트 스크립팅) 공격 방어
app.use(helmet.xssFilter());
// 클릭재킹으로 부터 보호
app.use(helmet.frameguard("deny"));
// 브라우저에서 파일 형식의 임의 추측 금지
app.use(helmet.noSniff());

xssFilter

  •  해킹 기법중 하나로 게시판이나 웹 메일등 자바스크립트와 같은 스크립트 코드를 삽입해 다른 사용자가 스크립트 코드를 실행 시켰을때 개발자가 고려하지 않은 기능이 작동하게 하는 공격 => 웹에 접속하는 사용자를 대상으로 한 공격
  • xss 공격에 취약한 웹사이트 탐색 => xss 공격을 위한 스크립트를 포함한 url을 웹 사이트 사용자에게 노출시킴 => 사용자가 해당 url을 클릭할 경우 웹 서버에 스크립트가 포함 된 url을 통해 requse를 전송하고 웹서버는 해당스크립트를 포함한 response를 사용자에게 전달함
  • 크로스 사이트 스크립팅 공격으로 부터 방어하기 위해 xss필터 활성화, 웹 애플리케이션이 응답의 컨텐츠에 포함된 잠재적인 악성 스크립트를 감지하고 제거함
  • X-XSS-Protection 헤더를 0으로 설정하여 XSS(Cross Site Scripting) 공격 스크립트를 비활성화여 예방
  • helmet은 src나 href, 인라인 자바스크립트 XSS 공격을 막는 용도로

xssFilter 를 설정 했을때 기능

  • 유효성 검사, 긴 입력 불가
  • html태그 무력화 (escape 처리)
  • encodeURI 함수를 사용해 입력받은 URL이 안전한지 확인
  • html 속성에 사용자가 쌍따옴표(")를 넣을 수 없는지 확인. 또한 DB에 넣기 전 검사
  • HTTP헤더로 완화
  • helmet은 srchref, 인라인 자바스크립트 XSS 공격을 막는 용도

sanitize-html모듈

  • html태그에 대한 악성 스크립트를 방지하는 모듈
const sanitizeHtml = require('sanitize-html');

const dirty = `스크립트는 과연 
    <script>some really tacky HTML</script> 무시될까?
    h1태그는 <h1>링크</h1> 무시가 될까?`;  
    
const clean = sanitizeHtml(dirty);

console.log(clean);

frameguard

- 클릭재킹은 사용자의 클릭을 가로채 다른 것을 누르게 하는 방식이다. 제한적인 X-Frame-Option을 보내서 브라우저가 더이상 이 프레임을 로드하지 않도록 해 대응할 수 있다. html의 iframe태그 허용범위를 설정하는 옵션으로 여기서 설정한 deny 옵션은 다른 모든 사이트들안에 우리 사이트를 iframe 태그로 넣지 못하도록 설정하는 옵션이다.

  • deny : 프레임 내에 우리 사이트를 넣지 못하게 함
  • sameorigin : 다른 사람이 프레임 내에 우리 사이트를 넣지 못하지만 우리 사이트에서는 허용
  • allow-from : 지정한 사이트에서 프레임을 표시할 수 있음

noSniff

- MIME(Multipurpose Internet Mail Extensions) 유형을 임의로 추측하는 것을 방지함 브라우저에서 파일을 열 때 잠재적인 보안 취약성을 방지 할 수 있음.

  • 웹 브라우저가 특정 파일을 읽을때, 파일의 실제 내용과 Content-Type에 설정된 내용이 서로 다르면 파일의 내용으로부터 파일의 형식을 추축하여 실행하는 것임 편리한 기능이지만 공격자에게 악용될 가능성이 있음 만약에 해커가 html의 파일의 내용을 png등 여러 확장자로 만들어 업로드 하였을때, 그 파일을 읽은 사용자는 MIME 스니핑에 의해 이미지 파일을 요청하더라도 실제 파일의 내용이 HTML 형식인 것을 보고 HTML 코드를 실행 할 수 있다.

결론 : 브라우저가 요청된 파일의 내용을 보고 임의로 파일 형식을 추측할수 없도록 하는설정

JWT가 무엇이고 어떻게 구성되어 있나요? JWT가 아닌 다른 방식으로 토큰을 구성할 수도 있나요? 그럼 어떻게 해야 하나요?

jwt

- json web token 은 웹표준으로 두 개체에서 jswon 객체를 사용하여 가볍고 자가 수용적인 방식으로 정보를 전달함

jwt 구성

  • header : typ와 alg를 가지고 있음 typ는 토큰의 타입을 지정함 alg는 해싱 알고리즘을 나타냄 이 일고리즘은 토큰을 검증 할 때 사용되는 signature 부분에서 사용됨
  • payload: 토큰에 담을 정보가 들어있음 
  • signature : header의 인코딩 값과 payload의 인코딩 갑을 합친후 주어진 비밀키로 해쉬를 하여 생성함

jwt가 아닌 다른 방식으로 토큰 구성

 

jwt가 아닌 다른 방식으로 토큰 구성 어떻게?

 jwt 장단점

- 장점 : 상태를 유지 하지 않음, 서버가 유저의 세션 상태를 기억할 필요가 없기 때문에 확장성이 뛰어나고 서버 리소스를 효율적으로 사용가능 stateless 상태

- 단점 : 한번 발급된 jwt는 만료시간이 될 때까지 유효, 토큰이 탈취되면 보안에 문제가 발생할 수 있음 => 짧은 만료시간 설정하여 보안적으로 보완

jwt signature는 어떻게 검증되는지

- 시그니처는 서버에서 토큰을 검증할때 사용함, 헤더와 페이로드를 이어붙인 문자열에 대해 서버가 보유한 시크릿 키를 사용해 해시를 계산함 이 해시는 들어온 토큰의 시그니처와 비교됨, 두 값이 일치하면 토큰이 변조되지 않았음을 확인

   let { accesstoken, refreshtoken } = req.headers;


    try {
        accesstoken = !req.headers.refreshtoken ? req.cookies.accesstoken : accesstoken;
        refreshtoken = !req.headers.refreshtoken ? req.cookies.refreshtoken : refreshtoken;


        const [authAccessType, authAccessToken] = (accesstoken ?? "").split(" ");


        // refreshtoken이 없거나 accesstoken의 형식이 올바르지 않을 때
        if ((!refreshtoken) || (authAccessType !== "Bearer" || !authAccessToken)) {
            // 로컬에 사용자 정보를 null로 설정 // 가짜 사용자 객체를 만듬
            res.locals.user = { userId: null };
            // 다음 미들웨어로 진행
            return next();
        }

        const isAccessTokenValidate = validateAccessToken(authAccessToken);
        const isRefreshTokenValidate = validateRefreshToken(refreshtoken);


        if (!isRefreshTokenValidate) {
            return res.status(419).json({ errorMessage: "Refresh Token이 만료되었습니다." });
        }


        if (!isAccessTokenValidate) {
            const accessTokenId = await tokenRepository.findTokenId(refreshtoken);
            if (!accessTokenId) {
                return res.json({ errorMessage: "Refresh Token의 정보가 서버에 존재하지 않습니다.", });
            }
            const newAccessToken = createAccessToken(accessTokenId);

            console.log(newAccessToken)
            res.cookie("accesstoken", `Bearer ${newAccessToken}`);
            return res.status(203).json({ newAccessToken });
        }

        const { userId } = jwt.verify(authAccessToken, process.env.ACCESS_KEY);
        const user = await Users.findOne({ where: { userId: userId } });
        res.locals.user = user;

        next();
    } catch (error) {
        console.error(error);
        res.clearCookie("accesstoken");
        res.clearCookie("refreshtoken");
        return res.status(403).json({ errorMessage: "전달된 쿠키에서 오류가 발생하였습니다." });
    }
};


소켓 서버에 트래픽이 많아져서 로드밸런싱을 해야 하는 경우 어떻게 로드밸런싱을 해야 문제 없이 구성이 가능한가요?

upstream 업스트림이름{
	#ip별로 균등하게 서버분배 defalut는 round robbin
	ip_hash
	server ubuntu주소:3001
    server ubuntu주소:3002
    server ubuntu주소:3003
}


server{
	listen 80
	server_name 푸댕푸댕.net;
	
	location / { #root경로로 요청시
     proxy_pass http://nodejs_servers;
     proxy_http_version 1.1;
     proxy_set_header Upgrade $http_upgrade;
     proxy_set_header Connection 'upgrade';
     proxy_set_header Host $host;
     proxy_cache_bypass $http_upgrade;

		}
	}
}

nginx를 사용한 로드밸런스 서버 구축

리버스 프록시 서버는 로드밸런싱에 사용함 => 트래픽이 증가하면 2가지 선택지가 있는데 하나는 scale up ( 서버의 컴퓨터의 사양을 늘리는것 ) 다른 하나는 scale out 서버의 갯수를 늘려 트래픽을 분산시키는 방법 nginx는 이중에서 scale out 방식으로 서버의 갯수를 늘려 트래픽을 분산시키는 방법이다.

hop-by-hop

  • 네트워크에서 데이터 패킷이 한 노드에서 다른 노드로 전달되는 방식을 나타냄
  • 패킷이 각각 중간 노드에서 처리되고 검사되며 패킷을 최종 목적지로 전달하는 동안 중간 노드에서의 동작을 제어하는 것을 의미
  • 각 노드는 패킷을 받으면 특정 동작을 수행하고 다음노드로 패킷을 전달함, 라우팅, 패킷검사 ,오류 제어 ,흐름제어 등을 포함 할 수 있음
  • 노드는 자신이 수행해야할 동작을 처리하고 패킷을 적절한 다음 노드로 전달하는 방식으로 작동
  • ip 프로토콜 기반의 네트워크에서 사용되는 일반적인 통신 방식으로 ip패킷은 출발지에서 목적지로 전달되는 동안 여각 라우터는 독립적으로 패킷을 처리함

웹소켓과 nginx

reverse proxy서버는 웹소켓을 지원하는데 몇가지 문제가 있습니다.

1. 웹소켓은 hop-by-hop 프로토콜이기때문에 프록시 서버가 클라이언트로 부터 upgrade request를 가로챌때 백엔드 서버에 자체 업그레이드 요청을 명시 해주어야 합니다. 웹소켓 연결은 긴 시간동안 살아있기대문에 reverse proxy는 이러한 연결이 유휴상태 인것처럼 보입니다. 그러므로 소켓 연결을 닫지 않고 연결상태를 유지하도록 허용해야합니다.

- 업그레이드 요청을 보내기위해 upgrade와 connection 헤더가 반드시 명시되어야 합니다

 추가적으로 Upgrade header와 Connection header를 명시해주어 수신하는  웹서버가 이 요청이 websocket요청임을 알 수 있습니다.

  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "Upgrade";
  proxy_set_header Host $host;

답변 1.  Cookie 옵션중 SameSite에 대해

sameSite : 웹에서 쿠키를 전송할 때 동일 한 출처에 대해서만 요청을 보내도록 제한하는 옵션으로, CSRF 공격을 방지하기 위해 SameSite 옵션을 설정하여 쿠키를 동일한 출처로 제한할 수 있습니다.