728x90

Environment

Server: Node.js

NPM: mysql2

Example

Node.js에서 Sequelize ORM같은 것을 사용하지 않고 raw query를 사용하고자 할 때 mysql2 package를 받아 사용할 수 있다. 이 경우 패키지에서 제공하는 prepare/unprepare를 통해 prepared statement injection attack를 방지할 수 있는 방법이 있다.

Prepared statement가 Injection attack을 당했을 경우 예시

// PHP예시를 JavaScript로 변경하여 작성하였으므로 오류가 있을 수 있습니다.

const originData = 1;
const query = `SELECT * FROM user WHERE id = ${originData}`;
// 위처럼 의도하고자 하는 데이터가 올바르게 들어갈 경우 문제는 없다

const spoiledData = '1; DROP TABLE user;';
const query = `SELECT * FROM user WHERE id = ${spoiledData}`;
// 위처럼 데이터가 변조당해 쿼리가 실행될 경우 user 테이블이 drop되는 공격을 받을 수 있다

mysql2 package를 이용해 injection attack을 방지하여 사용하는 예시

const mysql = require('mysql2/promise'); // mysql2의 promise를 사용
const mysqlPool = mysql.createPool(
	host: 'localhost',
  user: 'root',
  database: 'test'
);

const connection = await pool.getConnection();

const data = ['david', 25];
const query = 'SELECT * FROM user WHERE name = ? AND age = ?';
const [rows, fields] = await connection.execute(query, data);
connection.unprepare(query); // unprepare로 만들고 싶을 경우(단 한 번만 쓰는 구문일 경우)

// If you execute same statement again, it will be picked from a LRU cache
// which will save query preparation time and give better performance

mysql2 패키지를 이용하면 데이터와 쿼리문을 분리해서 적용하기 때문에 injection을 방지할 수 있다. 예상되는 로직으로는 mysql2에서 query를 먼저 받아 자체적으로 sanitize? 또는 분류? 작업을 통해 하나의 프로그램이 아닌 것으로 나눈다. 그리고 ?로 해당하는 곳에 data로 넘겨받은 파라미터를 하나씩 적용해준다. 이 과정에서 injection을 검증하는 과정(?)을 거치기 때문에 우리는 안전하게 prepare statement를 사용할 수 있으며 추가적으로 cache효과까지 얻을 수 있다.

이 글에서 하고싶은 말은, 우리는 DB와 통신할 때 prepared statement injection attack을 대비해야 한다는 것이며 만약 Node.js를 사용하고 있다면 mysql2 packge를 이용해 injection attack을 방지함과 동시에 cache효과를 얻을 것을 제안한다는 것이다.

Refs.

mysql2

How can prepared statements protect from SQL injection attacks?

https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html

+ Recent posts