대학교 때 코딩을 할 때와는 다르게 지금은 코딩을 하면서 이게 보안에 적합한가? 라는 생각을 많이 하게 되었다. 그래서 한국인터넷진흥원에서 발행한 보안 코딩가이드를 보면서 회사에서 진행했던 프로젝트와 비교해보았다. 생각보다 안지켜지던 점이 많았다. 앞으로 내가 코딩할때는 위 원칙들은 반드시 지켜야 겠다라고 생각하며 내용을 정리해 본다.
1. SQL 삽입
- DB와 연동된 웹 어플리케이션에서 공격자가 입력 폼 및 URL 입력란에 SQL문 자체를 입력하여 DB로 부터 저옵를 열람하거나 조작할 수 있는 보안약점이다.
public void weakCode(String name)
{
String tableName = "weakCodeTable";
String query =
"SELECT * "
+ "FROM "+ tableName
+ " WHERE Name =" + name;
stmt = con.preparedStatement(query);
rs = stmt.exeuteQuery();
....
}
위 코드는 name을 파라미터로 받아들여 table에 NAME 컬럼의 값이 입력된 파라미터인 'name'과 같다면 1개의 row를 반환하는 형식으로 코드가 짜여져 있다. 하지만 이런 코드는 보안적이지 못하다. 왜냐하면 name에 만약 sql을 입력한 다는 등의 비정상적인 접근을 할때 방어가 불가능 하기 때문이다. 즉, name에 AAA' OR '1=1 이렇게 입력하게 되면 완성된 쿼리는 "SELECT * FROM weakCodeTable WHERE NAME = 'AAA' OR '1=1'"가 되게 된다. 이렇게 되면 모든 쿼리에 의해서 모든 row를 반환하게 된다. multirow 반환으로 오류가 발생할 수도 있고 만약 본능적으로 multirow에 대한 반환을 구현해 두었으면 모든 정보가 노출될 수도 있다. 따라서 위의 코드는 아래처럼 바꿔야 한다.
public void strongCode(String name)
{
String tableName = "weakCodeTable";
String query =
"SELECT * "
+ "FROM ?"
+ " WHERE Name = ?";
stmt = con.preparedStatement(query);
stmt.setString(1, tableName);
stmt.setString(2. name);
rs = stmt.exeuteQuery();
....
}
위 코드는 미리 쿼리의 형식을 정해두고 외부의 입력이 쿼리문의 구조를 바꾸는 것을 방지할 수 있다.
또한 쿼리에 값을 입력할 때는 입력값에 대해서 검사를 진행하며 안전한 문자열로 바꿔야한다.
1. 인자의 길이 제한 - 공격 구문을 작성하게 되면 일반적으로 입력값이 길어지게 된다. 따라서 인자의 길이를 일정 이하 로 제한 하면 인자를 통해 공격 구문을 삽입하는게 힘들어진다.
2. 인자에 예약어 삽입 제한 - 인젝션 공격 구문에는 SQL문에 쓰이는 예약어가 쓰일 가능성이 높다. 따라서 SELECT, UPDATE 등의 예약어를 받아들이지 않으면 인젝션을 방어할 수 있다.
3. 알파벳과 숫자를 제외한 문자 사용 제한 - 공격 구문을 작성할 때, 특수문자가 상당히 많이 쓰이는데, 대표적으로 `가 가장 많이 사용된다. 이런 문자를 강제로 변환하여 공격에 의한 피해를 방지할 수 있다.
한국인터넷 진흥원에서는 JAVA 정규식으로 [^\\p{Alnum}] | select | delete | update | insert | create | alter |drop 를 사용하는것을 권고하고 있다. 알파벳은 사용할 수 있으며, select 와같은 예약어는 사용 불가능하다.
2. 자원(Resource) 삽입
- 외부의 입력을 자원(file, Socket 통신 등)의 정보로 사용하는 경우 적절한 검증을 거치도록 하거나 사전에 정의된 적합한 리스트에서 선택되도록 작성할 필요가 있다. 예를 들어 Socket통신을 한다고 했을때, 통신을 할 PC의 IP와 PORT가 필요로 하게 된다. 그런데 이런 정보를 properties와 같은 외부파일 또는 입력을 받아 사용한다고하면 시스템 자원 사이의 충돌등이 일어날 수 있다.
public void weakCode(int port)
{
try {
ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
....
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
위 코드를보면 int로 받아들인 port를 곧이 곧대로 서버의 port로 사용하여 서버 소켓을 생성한다. 이렇게 한다고 한다면 만약 공격자가 임의의 이상한 포트(65,535(unsigned int)가 넘는 값 또는 기 사용중인 port 값)을 넘긴다면 소켓이 정상적으로 만들어지지 않을 것이다. Client라면 IP, PORT를 넘길 것이고 이는 예상하지 못한곳으로 접속을 시도할 수 있다. 따라서 이러한 코드들은 아래와 같이 바꿀 필요가 있다.
public void strongCode(int portNum)
{
int port = 0; // default Value;
switch(portNum)
{
case 1: port = 3001; break;
case 2: port = 3002; break;
case 3: port = 3003; break;
....
default: port = 3000;
}
try {
ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
....
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
위 코드를 보면 port를 직접 넘기지 않고 number를 넘긴 후 그 번호에 맞는 port를 반환한다. 이렇게 하면 공격자의 임의 포트, 임의 ip에 의한 위협을 막을 수 있다. 위 코드에서는 switch를 사용했지만 switch보다는 DB, enum 등을 추천한다.
위 내요은 자바(java) 보안(SECURE) 코딩과 관련된 내용은 한국인터넷진흥원에서 생산한 코딩가이드(JAVA)_V1.2(수정)-웹용 파일을 참조 정리하였습니다.
'기타 > 보안' 카테고리의 다른 글
[OAuth2] OAuth2 개론 - 개요와 Authorization Code Flow (0) | 2023.03.11 |
---|---|
[JWT+JAVA] JWT(Json Web Token)에 대해서 자세히 알아봅시다 - 실습편 (0) | 2023.02.27 |
[JWT] JWT(Json Web Token)에 대해서 자세히 알아봅시다 - 이론편 (0) | 2023.02.19 |
[기타] 서비스의 일반적인 보안 취약성 - 2편 (0) | 2022.11.12 |
[기타] 서비스의 일반적인 보안 취약성 - 1편 (1) | 2022.11.08 |
댓글