보안 공부/소스코드 보안약점 진단

소스코드 보안약점 진단 - Null Pointer 역참조

H.J.World 2022. 1. 31. 10:10
728x90
반응형

소스코드 보안약점 진단 // 소프트웨어 보안약점 진단 // SW 보안약점 진단과 같이 다양한 이름으로 불리는 진단 과업 중 하나이다.

SW개발보안은 해킹 등 사이버공격의 원인인 보안약점을 SW개발단계에서 사전에 제거하고 SW 개발 생명주기의 각 단계별로 수행하는 일련의 보안활동을 통하여 안전한 SW를 개발·운영하기 위한 목적으로 적용하는 개발체계이다.

해당 내용은 KISA에서 발간하는 취약점 진단 가이드 항목을 기준으로 작성한다.

 

제 5절 코드오류

타입 변환 오류, 자원(메모리 등)의 부적절한 반환 등과 같이 개발자가 범할 수 있는 코딩오류로 인해 유발되는 보안약점이다.


1. Null Pointer 역참조

가. 개요

널 포인터(Null Pointer) 역참조는 ‘일반적으로 그 객체가 널(Null)이 될 수 없다’라고 하는 가정을 위반했을 때 발생한다. 공격자가 의도적으로 널 포인터 역참조를 발생시키는 경우, 그 결과 발생하는 예외 상황을 이용하여 추후의 공격을 계획하는 데 사용될 수 있다.

나. 보안대책

널이 될 수 있는 레퍼런스(Reference)는 참조하기 전에 널 값인지를 검사하여 안전한 경우에만 사용 한다.

다. 코드예제

다음의 예제의 경우 obj가 null이고, elt가 null이 아닌 경우 널(Null) 포인터 역참조가 발생한다.

- 안전하지 않은 코드의 예 JAVA -

1: public static int cardinality (Object obj, final Collection col) {
2: int count = 0;
3: if (col == null) {
4: return count;
5: }
6: Iterator it = col.iterator();
7: while (it.hasNext()) {
8: Object elt = it.next();
//obj가 null이고 elt가 null이 아닐 경우, Null.equals 가 되어 널(Null) 포인터 역참조가
발생한다.
9: if ((null == obj && null == elt) || obj.equals(elt)) {
10: count++;
11: }
12: }
13: return count;
14: }

 

- 안전한 코드의 예 JAVA -

obj가 null인지 검사 후 참조해야 한다. 

1: public static int cardinality (Object obj, final Collection col) {
2: int count = 0;
3: if (col == null) {
4: return count;
5: }
6: Iterator it = col.iterator();
7: while (it.hasNext()) {
8: Object elt = it.next();
//obj가 null이 아닌 경우에만 obj.equal를 실행한다.
9: if ((null == obj && null == elt) || (null != obj && obj.equals(elt))) {
10: count++;
11: }
12: }
13: return count;
14: }

 

라. 진단방법

표현된 객체가 널(Null) 값이 될 수 있는지 확인한다. 만약 널(Null) 값이 될 수 있는지 체크하여 예외 처리를 한 경우 안전으로 판단하고 널(Null) 체크를 하지 않은 경우 취약한 것으로 판단한다. (①)

1: …
2: public void f() {
3: String cmd = System.getProperty(“cmd”);
4: // cmd가 널(null)인지 체크하지 않았다.
5: cmd = cmd.trim();
6: System.out.println(cmd);--------------------------------①
7: …

※(참고) 널(Null) 값이 될 가능성은 다음의 기준으로 확인

 - 초기값이 널(Null)로 할당된 값은 널(Null)이 될 수 있음

 - 선언이 된 후 객체가 생성되지 않은 값은 널(Null)이 될 수 있음

 - 널(Null)인 객체로 필드 접근을 하거나/널(Null)인 객체에 메소드 호출을 할 경우 결과 값은 널(Null)이 될 수 있음

 - 문자열 등 ‘널(Null)이 될 수 있는(Nullable)’ 객체들의 연산 결과 값은 널(Null)이 될 수 있음

 

- 정탐코드의 예 -

다음의 예제는 3번째 라인에서 널(Null)로 초기화 한 이후에 14번째 라인에서 널(Null)값인 vmrs 값을 참조하고 있다. 이 경우는 조건문 등에 의해 분기가 이루어지면서 널(Null)로 초기화된 변수가 초기화 되지 않는 경우이다. 

1: public Hashtable deleteAll(Connection con, GenericModel model) throws Exception {
2: Hashtable<String, Object> m = new Hashtable<String, Object>();
3: VMResultSet vmrs = null;
4: int rValue = 0;
5: try {
6: ArrayList grid =null ;
7: ….
8: con.commit();
9: con.setAutoCommit(true);
10: if(rValue > 0) {
11: vmrs = dao.listGoodKnowQuestion(con, model);
12: vmrs.setMessage(UtilMsg.getInstance().getMsg(“SUC003”));
13: } else {
14: vmrs.setMessage(UtilMsg.getInstance().getMsg(“ERR000”));
15: }
16: m.put(“result”, vmrs); 17: } catch (Exception e) { 18: ….
19: } finally {
20: ….
21: }
22: return m;
23: }

 

- 정탐코드의 예 -

다음의 예제에서는 4번째 라인에서 Statement를 널(Null)로 초기화하고 있고 85번째 라인에서 예외가 발생하면 10번 라인의 Statement의 값을 대입하지 못하고 20번의 finally 문으로 분기한 후 25번 라인이 실행되면서 널(Null) 값을 참조하게 된다. 

1: private static void makeList() {
2: Connection con = null;
3: StringBuffer sql = new StringBuffer();
4: PreparedStatement statement = null;
5: ResultSet rs = null;
6: list.clear();
7: try {
8: con = GenericDAO.getDataSource().getConnection();
9: ……..
10: statement = con.prepareStatement(sql.toString());
11: rs = statement.executeQuery();
12: while (rs.next()) {
13: String authGrp = rs.getString(“AUTHORITY_GROUP”);
14: …….
15: }
16: } catch (Exception e) {
17: if (verbose) {
18: ………..
19: }
20: } finally {
21: try {
22: rs.close();
23: } catch (Exception e1) {}
24: try {
25: statement.close();
26: } catch (Exception e1) {}
27: if (con != null) {
28: try {
29: con.close();
30: } catch (SQLException e) {
31: e.printStackTrace();
32: }
33: }
34: }
35: }

 

- 오탐코드의 예 -

try ~ catch 문에서 throw를 하는 경우 catch 절 이후는 실행되지 않음으로 널(Null) 포인터 역참조 가 발생하지 않는다. 다음의 예제에서 System.out.println(“After Throw 1”);은 실행되지 않는다.

1: public static void testFunction(String dateStr1) throws Exception {
2: SimpleDateFormat sdf = new SimpleDateFormat(“yyyyMMdd”,Locale.getDefault());
3: Date date1 = null;
4: try {
5: date1 = sdf.parse(dateStr1);
6: } catch (ParseException e) {
7: System.err.println(“Inner Function”);
8: e.printStackTrace();
9: thrownew Exception(e);
10: }
11: System.out.println(“After Throw 1”);
12: int days1 = (int)((date1.getTime()/100000)/24);
13: System.out.println(“After Throw 2”);
14: }
728x90
반응형