리셋 되지 말자

[NodeJS] SQL Injection 본문

NodeJS/생활코딩

[NodeJS] SQL Injection

kyeongjun-dev 2020. 9. 19. 16:31

nodejs에서의 SQL 코드

	db.query(`update author set name=?, profile=? where id=?`, [post.name, post.profile, post.id], function (error, result) {
            if (error) throw error;
            response.writeHead(302, { Location: `/author` });
            response.end();
        });

코드를 보면 query문 안에 '?'표시를 두고, 그 다음 인자인 리스트에 '?'표시 각각에 대입되는 값을 나열하는걸 확인할 수 있는데, 이같은 방식이 injection 공격을 방지하기 위한 것이라고 한다.

 

게시물 접근

exports.page = function(request, response){
    var _url = request.url;
    var queryData = url.parse(_url, true).query;
    var sql_result = db.query(`select topic.id, title, description, created, name, profile from topic left join author on topic.author_id=author.id where topic.id=?`, [queryData.id], function (error, topic) {
        //db.query(`SELECT * FROM topic WHERE id=?`,[queryData.id], function (error, topic) {
            if (error) throw error;
            db.query('SELECT * FROM topic', function (error2, topics) {
                if (error2) throw error2;
                
                var title = topic[0].title;
                var description = topic[0].description;
                var list = template.list(topics);
                var html = template.html(title, list, `<h2>${title}</h2>${description}
                <p>by ${topic[0].name}</p>`, 
                `<a href="/create">create</a>
                <a href="/update?id=${topic[0].id}">update</a>
                <form action="/delete_process" method="POST" onsubmit="y?">
                    <input type="hidden" name="id" value="${queryData.id}">
                    <input type="submit" value="delete">
                </form>`);

                response.writeHead(200);
                response.end(html);
            });
        });
    console.log(sql_result.sql);
}

위와같은 코드로 확인을 해보자. 게시물에 접근할 때 localhost:3000/?id=3 이런 방식으로 세 번째 게시물에 접근할 때, 주소를 'localhost:3000/?id=3;DROP TABLE topic;' 이런 방식으로 주소를 적게 되면, 아래와 같은 결과가 출력된다.(실제로 mysql 서버로 전송되는 쿼리문)

select topic.id, title, description, created, name, profile from topic left join author on topic.author_id=author.id where topic.id='15;DROP TABLE topic;'

id 값으로 'DROP TABLE topic;'이 그대로 들어가게 된다.

결과적으로는 DROP TABLE topic; 이 문자열에 묶여 있어서 DROP 쿼리문이 실행되지는 않지만, nodejs 쿼리에서 id값을 '?'로 지정하고, 리스트 인자로 id값을 전달하지 않았으면 'topci'테이블이 삭제되었을 것이다.

이런걸 sql 인젝션 공격이라고 한다.

하지만 nodejs의 mysql 모듈은 기본적으로, .query('쿼리문')을 사용할 때, 여러 쿼리문이 동시에 동작하지 않도록 방지한다.

여러 쿼리문을 동작하게 하려면 아래와 같이 createConnection을 수정한다.

var mysql = require('mysql');
var db = mysql.createConnection({
    host : '172.30.1.53',
    user : 'Open',
    password : 'Piano11823!',
    database : 'opentutorials',
    multipleStatements:true
});
db.connect();

module.exports=db;

 

Injection 상황 구현

  • mysql table
mysql> show tables;
+-------------------------+
| Tables_in_opentutorials |
+-------------------------+
| author                  |
| topic                   |
+-------------------------+
2 rows in set (0.00 sec)

위와같이 두 개의 테이블이 있는 상태에서, 상세보기 페이지 코드를 아래와 같이 수정한다.(DROP 쿼리문 포함)

exports.page = function(request, response){
    var _url = request.url;
    var queryData = url.parse(_url, true).query;
    var sql_query = `select topic.id, title, description, created, name, profile from topic left join author on topic.author_id=author.id where topic.id=${queryData.id};DROP TABLE topic`;
    console.log(sql_query);
    //var sql_result = db.query(`select topic.id, title, description, created, name, profile from topic left join author on topic.author_id=author.id where topic.id=?`, [queryData.id], function (error, topic) {
    var sql_result = db.query(sql_query, function (error, topic) {
        //db.query(`SELECT * FROM topic WHERE id=?`,[queryData.id], function (error, topic) {
            if (error) throw error;
            db.query('SELECT * FROM topic', function (error2, topics) {
                if (error2) throw error2;
                
                var title = topic[0].title;
                var description = topic[0].description;
                var list = template.list(topics);
                var html = template.html(title, list, `<h2>${title}</h2>${description}
                <p>by ${topic[0].name}</p>`, 
                `<a href="/create">create</a>
                <a href="/update?id=${topic[0].id}">update</a>
                <form action="/delete_process" method="POST" onsubmit="y?">
                    <input type="hidden" name="id" value="${queryData.id}">
                    <input type="submit" value="delete">
                </form>`);

                response.writeHead(200);
                response.end(html);
            });
        });
    console.log(sql_result.sql);
}

그리고 실행하면,

  • 테이블 확인
mysql> show tables;
+-------------------------+
| Tables_in_opentutorials |
+-------------------------+
| author                  |
+-------------------------+
1 row in set (0.00 sec)

topic 테이블이 사라진 것을 확인할 수 있다.

 

'?' 대신 사용하는 방법

db.query(`update author set name=${db.escape(post.name)}, profile=${db.escape(post.profile)} where id=${db.escape(post.id)}`, function (error, result) {

db.escape를 사용하면, '?'를 사용할 때처럼 쿼리문이 문자열로 묶인다. 결과는 '?'를 사용했을 때랑 똑같다.

'NodeJS > 생활코딩' 카테고리의 다른 글

[express] 미들웨어 - body parser  (0) 2020.09.23
[express] 루트 페이지 작성, pretty url  (0) 2020.09.21
[NodeJS] 출력에 대한 보안  (0) 2020.09.14
path 보안  (0) 2020.09.14
글 삭제-삭제 기능 완성  (0) 2020.09.11
Comments