[TIL] 24.11.25 MON

GDORI·2024년 11월 25일
0

TIL

목록 보기
113/143
post-thumbnail

주기적으로 포트로 신호를 보내는 넌, 누구냐

기본적으로 알려져있는 포트인 3000번으로 서비스를 배포해놓았더니, 특정 IP에서 공격에 가까운 패킷 전송을 시도하였다.
클라이언트 개발자분들도 아니고, 서버 개발자분들도 아니라고 하는 것을 보아하니 봇이 3000포트가 열려있는 아이피를 조회하여
접속 시도한다는 느낌을 받았다.

해결방안이 두 개지요.

생각한 방안은 두 개다.
1. 포트를 바꾼다.
2. 정해진 패킷 형식과 다른 시도를 N번 이상 시도 시 해당 IP를 블락처리 시킨다.

최종적으로는 둘 다 할 것이지만, 일단은 추후에 악성 이용자 밴처리를 생각해서 IP밴을 생각했다.
근데 우리는 모바일 게임이다.

모바일의 IP 변경조건은 다음과 같다

  • 네트워크 재연결 (모바일 데이터를 껐다가 켜는 경우)
  • 항공기 모드 켜고 끄기
  • 셀 타워 전환 (장소 이동 시)
  • 통신사 설정 변경

아.. IP밴은 공격 방지용으로만 쓸 수 있겠구나.. 그래도 없는 것보단 나을 것 같아서 만들어봤다.

Net 은 IP BLOCK을 지원해요

Node.JS 공식 홈페이지 - blockList

Node JS 는 Class: net.BlockList 를 내장으로 지원하고 있다.

  • blockList.addAddress(address[, type])
  • blockList.addRange(start, end[, type])
  • blockList.addSubnet(net, prefix[, type])
  • blockList.check(address[, type])
const blockList = new net.BlockList();
blockList.addAddress('123.123.123.123');
blockList.addRange('10.0.0.1', '10.0.0.10');
blockList.addSubnet('8592:757c:efae:4e45::', 64, 'ipv6');

console.log(blockList.check('123.123.123.123'));  // Prints: true
console.log(blockList.check('10.0.0.3'));  // Prints: true
console.log(blockList.check('222.111.111.222'));  // Prints: false

// IPv6 notation for IPv4 addresses works:
console.log(blockList.check('::ffff:7b7b:7b7b', 'ipv6')); // Prints: true
console.log(blockList.check('::ffff:123.123.123.123', 'ipv6')); // Prints: true 

그럼 이걸 어떻게 활용하지?

Node.js 네트워크 차단 규칙 설정

위 사이트에서 예시로 차단 방법에 대하여 설명하고 있다.

사이트 참고하여 아래와 같이 작성하였다.

server.js에서 blockList 인스턴스 생성

import net from 'net';
import { onConnection } from './events/onConnection.js';
import { initServer } from './init/initServer.js';
import { config } from './config/config.js';

export const blockList = new net.BlockList();

const server = net.createServer(onConnection);

initServer().then(() => {
  server.listen(config.server.port, config.server.host, () => {
    console.log(`SERVER ON - ${config.server.host} : ${config.server.port}`);
  });
});

export 시켜서 다른 파일에서도 참조할 수 있게 설정해주었다.

initServer에서 구동되는 loadBanList

export function readBanList() {
  try {
    const data = fs.readFileSync(filePath, 'utf8');
    return JSON.parse(data);
  } catch (error) {
    logger.error('Error reading the ban list:', error);
    return null;
  }
}

export const loadBanList = async () => {
  const banList = await readBanList();
  banList.banned_ips.forEach((ban) => {
    blockList.addAddress(ban.ip);
  });
};

서버가 실행될 때 banned_ips.json에서 차단된 IP를 읽어 blockList에 등록시킨다.

onConnection 부분

export const onConnection = (socket) => {
  const remoteAddress = socket.remoteAddress;

  if (blockList.check(remoteAddress)) {
    console.log('Blocked connection from', remoteAddress);
    handleErr(socket, { code: ERR_CODES.SOCKET_ERR, message: 'Your IP is banned' });
    socket.end();
  } else {
    console.log('Allowed connection from', remoteAddress);
    socket.buffer = Buffer.alloc(0);
    socket.clientId = uuidV4();
    socket.sequence = 0;

    socket.on('data', onData(socket));
    socket.on('end', onEnd(socket));
    socket.on('error', onError(socket));
  }
};

연결 시 소켓의 IP주소를 확인하고 blockList에 있는지 확인하여 여부에 따라 실행한다.

오케이, 그러면 이제 이걸 어떻게 활용하면 될 것인가?

차단할 아이피 등록하면 소켓 연결 거부하니까 언제 이것을 활용할 것인가..
맨 위에 올려놓은 사진을 보면 receivePacket 인자값 확인하라는 멘트가 보일 것이다.
저 부분에 N번 시도 시 카운트 하여 밴 리스트에 등록하는 과정을 가져보겠다.

writeBanList

function writeBanList(banList) {
  try {
    const data = JSON.stringify(banList, null, 2);
    fs.writeFileSync(filePath, data, 'utf8');
  } catch (error) {
    logger.error('Error write the ban list:', error);
    return null;
  }
}

export const addBanList = (socket) => {
  const remoteAddress = socket.remoteAddress;
  blockList.addAddress(remoteAddress);

  const banList = readBanList() || { banned_ips: [] };

  banList.banned_ips.push({ ip: remoteAddress });
  writeBanList(banList);
};

recvPacket 변경

export const recvPacket = (socket, packet) => {
  if (!packet) {
    ++socket.illegalCount;
    if (socket.illegalCount >= 5) {
      addBanList(socket);
      socket.end();
    }
    throw new Error('receivePacket 인자값 확인하세요.');
  }
  packetManager.enQueueRecv(socket, packet);
};

정상적으로 패킷이 처리되지 않았을 경우 소켓의 illegalCount를 추가시키고, 그 횟수가 5번이 넘었을 경우
addBanList 함수를 실행시키고 종료시킨다.

profile
하루 최소 1시간이라도 공부하자..

0개의 댓글