Node.js + MySQL 연동 / SQL injection 공격 실습

2023. 1. 30. 15:33Web 개발

실습 환경

 

- Visual Studio Code 
- Node.js - (18.14.0)
- MySQL - (8.0.32)

 


 

Node.js란?

 

Node.js는 확장성 있는 네트워크 애플리케이션(특히 서버 사이드) 개발에 사용되는 소프트웨어 플랫폼

( Chrome V8 Javascript 엔진으로 빌드된 Javascript 런타임)

 

 

 

 

< 장점 >

 

  • 고성능 네트워크 서버
  • 단일 쓰레드(Single Thread) 이벤트 루프(Event Loop) 기반           // 높은 처리 성능
  • 비동기 I/O 처리(Non-Blocking I/O)                                                 // 서버 무리 적음
  • 자바스크립트                                                                                  // 서버단 로직 처리 가능
  • 개발 생산성 향상
  • 방대한 모듈 제공(NPM)

 

 

 

< 단점 >

 

  • 이벤트 기반 비동기방식이라 서버단 로직이 복잡한 경우 콜백함수의 늪에 빠질 수 있음

 

  • 순차적 코드 실행이 아닌 비동기 방식으로 이벤트를 보내고, 응답이 오면 처리하는 방식                                              따라서, java 개발을 했던 방식으로 설계, 프로그래밍하면 큰 문제가 발생

 

  • 단일 쓰레드이기 때문에 하나의 작업 자체가 많이 걸리는 웹서비스에는 어울리지 않음 
    게시판형태와 같이 가벼운 I/O가 많은 웹서비스에 어울림

 

  • 코드가 수행시 코드 에러를 알 수 있으며, 에러 있을 경우 프로세스가 내려가기 때문에 테스트가 엄청 중요                반드시 모든 케이스에 대해 소스코드를 검증해야 한다.

 

 

 

Node.js 가 어울리는 웹서비스

  • 간단한 로직. 
  • 대용량(동시에 여러 request를 처리)
  • 빠른 응답시간 요구
  • 빠른 개발 요구
  • 비동기방식에 어울리는 서비스(네트워크 스트리밍 서비스, 채팅 서비스 등)

 

Node.js 가 어울리지 않는 웹서비스

  • 단일 처리가 오래 걸리는 경우 : 싱글 쓰레드이기 때문
  • 서버 체크로직이 많은 경우 : 비동기방식이기 때문에 CallBack Hell에 빠지지 않기 위해
  • 업무 복잡도/난이도가 높은 경우 : 에러가 나면 서버가 죽기 때문에 코드 품질 중요

 


 

MySQL 설치

 

실습을 진행하기전에 먼저, PC에 mysql을 설치하고 시작해야 한다

 


 
VSCode / Node.js / MySQL / Express 모듈 설치

 

에디터 프로그램인 VSCode에서 코드를 작성하기에 앞서, 웹 서버 제작에 필요한 모듈을 설치해야한다

 

 init 를 설치하기 위해 express-mysql-example이름으로 폴더를 만들고

폴더 안으로 들어가 npm init 로 설치를 진행하였다

 

mkdir express-mysql-example
cd express-mysql-example
npm init --yes

 

이때, npm은 Node Package Manager의 약자로 node.js에서 사용하는 모듈들을 패키지로 만들어 관리하고 배포하는 역할을 합니다 (npm을 통해서 모듈도 설치)

 

 

npm init를 하는 이유는?

 

package.json을 생성하여 Node 패키지에 관한 정보와 의존중인 버전에 관한 정보로 

node.js에서 사용하는 모듈들을 패키지로 만들어 관리하고 배포하기 위해서 

 

npm install express mysql

- >  express, mysql 모듈을 npm으로 설치

 

- Express

  : Node.js에서 웹 어플리케이션 혹은 API 서버를구축하는데 가장 많이 사용되는 대표적인 프레임워크이며

   서버를 만들고, 데이터베이스와 연결하고 어플리케이션을 구동하는 등의 작업을 진행하도록 돕는 모듈

 

- Mysql

  :  전세계적으로 가장 널리 사용되고 있는 오픈 소스 데이터베이스

 


 

Web 서버 작동

 

웹 서버를 작동하기 위해 express 모듈이 필요하다

 

Node.js에서는 require 매서드를 통해 외부 모듈을 가져 올 수 있기에 이를 이용하였다

 

const express    = require('express');        //express 모듈 가져옴
const app = express();                        

app.set('port', process.env.PORT || 3000);         // 웹 서버 포트 지정

app.get('/', (req, res) => {                       // 웹 서버의 메인 페이지에 html파일 연동
  res.sendFile(__dirname + '/web.html');
});

app.listen(app.get('port'), () => {                 
  console.log('Express server listening on port ' + app.get('port'));
});                                               
                                                   // 웹 서버 실행

 

터미널에 node index.js 입력 후

 

http://localhost:3000 주소로 접속하면 Node.js 서버가 실행됨을 확인 가능

 

'/'가 localhost를 의미

 


 

Web 서버 HTML 작성
 
Web 서버 화면에 보여질 페이지의 html을 만들어야한다

 

간단하게, 아이디와 패스워드를 받아 DB로 넘기는 기능을 가진 웹서버를 구현할 것이기에
 
아이디와 패스워드를 받는 폼을 만들어 보자
<body>
    <div class="login_box">
        <div class="Login">
            <h1>Login</h1>
        </div>
        <div class="id">
            <h3>
            <form action="/" id="idpw" method="POST">
                아이디 : <input type="text" name="id" ><br>
                패스워드 : <input type="text" name="pw" ><br>
                <button type="submit" id="send">ok</button>
            </form>
            </h3>
        </div>
    </div>
</body>
 
아이디와 패스워드를 텍스트로 입력받는 공간을 만들었고,
 
sumbit 타입의 버튼을 넣어
텍스트 입력 후 버튼을 누르면 정보가 전달되는 형식으로 작성하였다
 

[웹 서버 화면]

 

위와 같이 웹서버를 열었을 때 작성한대로 잘 보이는것을 확인하였다

 

 

Test DB 생성

 

웹 서버와 연결할 Test DB 생성을위해 MySQL에 접속하고

 

web_db라는 이름의 데이터베이스를 만들어준다

 

[DB 생성]

 

show databases; 로 db가 만들어진것을 확인하고,

 

[DB 조회]

 

방금 만든 web_db를 사용한다고 명령한 다음

 

[DB 변경]

 

web_db안에 web이라는 테이블을 생성해준다

 

칼럼은 id와 pwd로 각각 최대 20 byte크기의 문자를 받을 수 있게 설정해주었다 

 

[테이블 생성]

 

테이블에 값이 잘 들어가는지 확인을 위해, web테이블의 id, pwd칼럼에 각각 test문자를 insert 해주었고

 

[테이블 값 추가]

 

테이블을 조회 하였을 때 다음과 같이 잘 저장된 것을 확인하였다

 

[테이블 조회]

 


 

Node.js 와 MySQL 연동

 

 

const mysql      = require('mysql');                 // mysql 모듈 가져옴

const connection = mysql.createConnection({          // DB정보 connection함수로 선언
  host     : 'localhost',                       // 호스트 이름
  port     : '3306',                            // mysql 사용 포트
  user     : 'root',                            // mysql user 이름
  password : 'root',                            // mysql user의 패스워드
  database : 'web_db'                           // 연동 할 DB 이름
});

 

require 매서드로 mysql 모듈을 가져왔으며

 

Node.js에서 mysql에 접속하기 위해 
내가 사용하려는 db와 mysql 정보를 입력하여 이를 connection함수로 선언해주었다

 


 
DB 연동 확인

 

이제 DB와 잘 연동 되었는지 확인해야한다

 

 

로그인 폼(body의 input 부분)에서 값을 입력하면 js에서 그 값을 받아와야한다

 

 

http모듈로만 body값을 가져오려면 

req.on('data', function(chunk) { body += chunk; });와 같은 이벤트를 등록해야한다

 

이는 번거롭기때문에 

body-parser모듈을 사용하여 간단하게 값을 가져올 것이다

 

const bodyParser = require('body-parser');              // bodyParser 모듈 가져옴

app.use("/", express.static(__dirname + "/css"));       // css 파일 연동
app.use(bodyParser.urlencoded({ extends: true }))       
app.get('/', (req, res) => {                            // html 파일 연동
  res.sendFile(__dirname + '/web.html');
});

extends : true 는 중첩된 객체표현을 허용

                false 는 허용 x 

 

 

 

 

다음으로는,

 

connection.query를 통해 db에 접속하여 해당 쿼리문을 실행시켜 

 

테이블에 값을 넣고 db를 출력하는 명령을 보내는 코드를 작성하였다

 

app.post('/', (req, res) => {
  
  let a = req.body.id;   //html의 name인 id, pw값을 js로 가져와서 선언 
  let b = req.body.pw;

  connection.query('insert into web(id, pwd) values (' + connection.escape(a)+ ','+ connection.escape(b) +');', function(err){
    if(err){
     console.log(err);
    }
   })                             //web 테이블의 id, pwd칼럼에 a,b의 값을 INSERT하는 쿼리문 실행
   
   
  testQuery = "SELECT * FROM Web";
  
  connection.query(testQuery, function (err, results, fields) { // testQuery 실행
    if (err) {
      console.log(err);
     }
    console.log(results);
  });                                      //콘솔로그에서 DB 조회 (INSERT 확인)
  connection.end();
});

 

그리고 node.js로 웹 서버를 실행한 뒤,

 

웹 서버 페이지에서 id, pw에 각각 1234라는 값을 넣고 보내었을 때

 

[id,pw 값 전송]

 

다음과 같이 콘솔로그에서 DB가 조회되어 값이 잘 들어갔는지 확인 가능하다

 

[콘솔 로그에 DB insert 값 출력]

 

 


 

SQL injection 공격 실습

 

 

DB 조회하였을 때 테이블이 존재하는 것을 확인하였다

 

[web 테이블 데이터 조회]

 

그리고 SQL injection을 하기에 앞서 

SELECT문을 사용하여 데이터를 조회하는 기능을 가진 ID search창을 만들어주었다

 

 

<index.html>

<div class="ms">
    <h1>ID search</h1>

    <h3>
    <form action="/mo" id="movie" method="POST">                   //id검색시 /mo페이지로 넘어가 결과 출력
        아이디 검색 : <input type="text" name="search" ><br>
        <button type="submit" id="msend">검색</button>
    </form>
    </h3>
</div>

 

<index.js>

app.post('/mo', (req, res) => {
  data = req.body.search;
  testQuery = `SELECT * FROM web where id='${data}'`;
 
  connection.query( testQuery, (error, rows) => {
    if (error) throw error;
    console.log('id info is: ', rows);
    res.send(rows);
  });
});

 

[DB에서 id검색하는 창]

 

위의 코드를 통해 만든 웹서버 화면이다 (나는 기존 id, pw 입력 bar 밑에 새로 추가한 것이다)

 

DB에 들어가 있던 id인 aaa를 입력하였을 때

/mo 페이지로 넘어가 내가 입력한 아이디와 그 아이디의 비밀번호를 출력하는 기능을 만들었다 

 

[SQl injection - DROP TABLE]

 

여기에  '; DROP TABLE web; '-- 을 입력하여 테이블 삭제 인젝션 공격을 하였다

 

[web 테이블 데이터 조회]

 

다시 DB 확인을 해보았을 때

테이블이 사라졌으며 DROP TABLE 인젝션 공격이 성공한것을 알 수 있다

 

 

 

 

'; DROP TABLE web; '-- 을 넣은 이유는? 

 

 

 

내가 실행 시키고 싶은 SQL injection구문은 DROP TABLE web;이다


하지만, 정상적으로 코드를 실행했을 때 SELECT * FROM web where id=' 뒤에 내가 검색한 id가 들어간다

 

 

이때, SELECT 구문 뒤에 id='로 끝나는데 원래 id를 검색하면


서버에서 자동으로 id='내가 검색한 id값';으로 문장을 마무리하고 실행시킨다

 



그래서 어떠한 공격 코드를 넣어도 문자 값으로 받을 뿐이다


따라서 문장을 강제로 마무리시키고 SQL injection코드를 넣어야한다 그래서 '; DROP TABLE web;을 넣었으며

 

 

뒤의 '--은 뒤에 오는 코드들을 모두 주석 처리시키는 것이다 

주석 처리 시키는 이유는 뒷 코드에서 검증로직이 있다면 SQL구문 실행에 영향을 끼칠 수 있기 때문이다 
 

 

 

 

 

 

 

/* 

전체 코드

 

< index.html >

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Min Song Ha</title>
    <link rel="stylesheet"  href="http://localhost:3000/web.css">
</head>
<body>
    <div class="login_box">
        <div class="Login">
            <h1>Login</h1>
        </div>
        <div class="id">
            <h3>
            <form action="/" id="idpw" method="POST">
                아이디 : <input type="text" name="id" ><br>
                패스워드 : <input type="text" name="pw" ><br>
                <button type="submit" id="send">ok</button>
            </form>
            </h3>
        </div>
        <div class="ms">
            <h1>ID search</h1>
            <h3>
            <form action="/mo" id="movie" method="POST">
                아이디 검색 : <input type="text" name="search" ><br>
                <button type="submit" id="msend">검색</button>
            </form>
            </h3>
        </div>
    </div>
</body>
</html>

 

 

< index.js >

const express    = require('express');
const mysql      = require('mysql');

const bodyParser = require('body-parser');
const app = express();

const connection = mysql.createConnection({
  host     : 'localhost',
  port     : '3306',
  user     : 'root',
  password : 'root',
  database : 'web_db',
  multipleStatements: true 
});


// configuration =========================
app.set('port', process.env.PORT || 3000);

app.use("/", express.static(__dirname + "/css"));
app.use(bodyParser.urlencoded({ extends: true }))
app.get('/', (req, res) => {
  res.sendFile(__dirname + '/web.html');
});

app.listen(app.get('port'), () => {
  console.log('Express server listening on port ' + app.get('port'));
});

app.post('/', (req, res) => {
  
  let a = req.body.id;
  let b = req.body.pw;
  var params = [a,b];
  connection.query(`insert into web(id, pwd) values ('${a}','${b}');`, function(err){
    if(err){
     console.log(err);
    }
   })
  testQuery = "SELECT * FROM Web";
 
  connection.query(testQuery, function (err, results, fields) { // testQuery 실행
    if (err) {
      console.log(err);
     }
    console.log(results);
  });

});

app.post('/mo', (req, res) => {
  data = req.body.search;
  testQuery = `SELECT * FROM web where id='${data}'`;
 
  connection.query( testQuery, (error, rows) => {
    if (error) throw error;
    console.log('id info is: ', rows);
    res.send(rows);
  });
});


 
/* app.get('/web', (req, res) => {
  connection.query('SELECT * from web', (error, rows) => {
    if (error) throw error;
    console.log('web info is: ', rows);
    res.send(rows);
  });
});
*/

*/

'Web 개발' 카테고리의 다른 글

로그인 & 회원가입 (Node.js + DB연동)  (0) 2023.07.20
Selenium을 이용한 Web 크롤링 개발  (0) 2023.07.02
CSS  (0) 2023.02.03
JavaScript (자바스크립트)  (0) 2023.01.25
HTML 태그  (0) 2023.01.25