-
[Node.js & MySQL] 보안: SQL injection, Escaping생활코딩/WEBn 2021. 1. 13. 20:41
외부로부터 유입되는 정보는 필터링이 필요하다.
유저가 입력 데이터로 악성 SQL을 주입해 공격하는 경우인 SQL injection 예방법과
해당 데이터를 웹브라우저로 실행할 때 발생할 수 있는 Cross site scripting (XSS) 예방법을 살펴본다.
[강의 출처] opentutorials.org/course/3347/21299
SQL injection 예방법
페이지를 아래처럼 로드한다고 할 때,
var mysql = require('mysql'); var db = mysql.createConnection({ host : 'localhost', user : 'root', password : 'password', database : 'data' }) db.connect(); var app = http.createServer(function(request, response){ var _url = request.url; var id = url.parse(_url, true).query.id; // 여기까지 설정 db.query('SELECT * FROM author WHERE id=?',[id], function(err, data){ // 실제 실행, id는 위에서 URL의 쿼리부분을 읽어온 값이다 }
id='1'인 경우 실제 SQL문은 아래처럼 생성된다.
SELECT * FROM author WHERE id = '1'
공격자가 URL에 입력된 쿼리 부분이 그대로 SQL문에 전달된다는 사실을 이용해서, 아래와 같은 URL을 요청했다고 하면,
https://testpage.com/?id=1;DROP TABLE tablename;
이런 SQL문이 생성된다. SQL은 세미콜론으로 명령을 구분하지만, 전체 인풋이 텍스트로 묶여있기 때문에 해당 공격은 실행되지 않는다.
SELECT * FROM author WHERE id = '1;DROP TABLE tablename;'
그 이유는 db.query를 호출할 때 1번째 인자로 쿼리문('SELECT * FROM author WHERE id=?')을,
2번째 인자로 입력데이터(id)를 주어서 mysql '?'를 변수로 치환 후 처리했기 때문이다.
따라서 가능하면 ? 치환형태로 쿼리문을 실행하는 것이 좋다.
이것이 가능한 이유는 mysql 모듈에서 기본적으로 SQL Injection(입력 데이터로 악성 SQL을 주입해 공격하는 방법)을 예방하기 위한 처리를 해두었기 때문이다.
*도큐먼트의 해당 부분에 아래와 같이 설명되어 있다.
In order to avoid SQL Injection attacks, you should always escape any user provided data before using it inside a SQL query. You can do so using the mysql.escape(), connection.escape() or pool.escape() methods:
SQL문 삽입을 통한 공격을 피하기 위해서는, 유저가 입력한 데이터를 SQL쿼리에 사용하기 전에 항상 치환해야한다. mysql.escape(), connection.escape(), pool.escape() 함수를 이용할 수 있다.
var userId = 'some user provided value'; var sql = 'SELECT * FROM users WHERE id = ' + connection.escape(userId); // 이스케이프가 적용되었다 connection.query(sql, function (error, results, fields) { if (error) throw error; // ... });
Alternatively, you can use ? characters as placeholders for values you would like to have escaped like this:
대안으로 ? 문자를 써서 치환하고자 하는 값의 플레이스홀더로 사용할 수 있다:
connection.query('SELECT * FROM users WHERE id = ?', [userId], function (error, results, fields) { if (error) throw error; // ... });
그리고 데이터타입에 따라서 어떻게 치환되는지에 대해서도 설명이 나와 있는데,
숫자는 그대로, 불리언값은 true/false, 데이터는 'YYYY-mm-dd HH:ii:ss' 형태, undefined, null은 NULL 등등이 있다.
그 중 유용할 듯한 부분은 객체는 key = 'val'로 치환되는 부분.
var post = {id: 1, title: 'Hello MySQL'}; var query = connection.query('INSERT INTO posts SET ?', post, function (error, results, fields) { if (error) throw error; // Neat! }); console.log(query.sql); // INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL'
객체를 넣으면 알아서 변환해준다! 정말이지 좋은 모듈이다.
혹시 이 방법을 몰랐다고 하더라도, 다행히 mysql 모듈측에서 connection.query()로 실행될 수 있는 구문 수를 제한해두었기에 파국으로 치닫지는 않는다.
마찬가지로 도큐먼트에 보면 Connection option에 아래와 같은 옵션이 있다.
multipleStatements: Allow multiple mysql statements per query. Be careful with this, it could increase the scope of SQL injection attacks. (Default: false)
한 쿼리에서 여러 mysql 구문을 실행할 수 있도록 허용한다. SQL 주입 공격 유효범위가 늘어날 수 있음에 유의. (기본값은 false)
multipleStatements가 true인 경우에는 테이블이 DROP 될 수 있으니 조심하고, 기본값은 이유 없이 변경하지 말자. 모듈 사용 전에 도큐멘트만 잘 살펴봐도 대부분의 파국은 막을 수 있을 듯하다.
Cross site scripting (XSS) 예방법
기본에 다루었던 sanitize-html와 같은 방법으로 예방한다.
유저 입력 데이터를 처리하는 부분을 sanitizeHTML()로 감싸주면 된다.
유저 입력 데이터를 다루는 모든 부분에 처리가 필요하기 때문에 종종 빠뜨리는 부분이 나올 수 있다. 코딩할 때부터 미리미리 해두자.
'생활코딩 > WEBn' 카테고리의 다른 글
[Node.js & MySQL] 도전과제: 검색/페이징/정렬 (2) 2021.01.24 [Node.js & MySQL] 도전과제: 검색 - 색인기능 살펴보기 (0) 2021.01.20 [Node.js & MySQL] 테이블 생성 (0) 2021.01.13 [Node.js & MySQL] main.js 정리 - DB접속 정보/쿼리 분리 (0) 2021.01.12 [Node.js & MySQL] 목록 콤보박스 생성 / 디폴트값 설정 (2) 2021.01.05