주요 보안 취약점
입력데이터 검증 및 표현
: 입력값 검증 누락 또는 부적절한 검증, 데이터의 잘못된 형식 지정으로 인해 발생.
- 크로스 사이트 스크립트 공격(XSS): 검증되지 않은 외부입력데이터나 부적절한 스크립트가 포함된 동적 웹페이지가 생성,전송되고 웹페이지에 포함된 부적절한 스크립트가 실행됨
- 사용자 정보유출, 세션도용, 악성코드 유포, 브라우저 무한반복 등의 공격
- request.getParameter("name")하는 코드에서
<script>alert(document.cookies)</script>
가 입력되면 공격자에게 피해자의 쿠키정보가 전송될 수 있음. =>replaceAll()
을 사용해<
,>
,&
,"
와 같이 스크립트 생성에 사용되는 모든 문자열을 <, > 등으로 변경해야 실행하지 못하도록 해야함
sql injection
: 입력 데이터 유효성 점검하지 않아서 db 쿼리 로직이 변경되어 발생.
- 입력 값이 바로 sql 함수의 파라미터로 전달되지 않게 해야함. 파라미터화된 쿼리를 사용해야함.
자바의 경우
preparedStatement
클래스를 사용해setString()
해서 외부의 입력이 쿼리의 구조를 바꾸는 것을 방지해야함
HTTP 응답 분할
: http 요청 파라미터에 \r\n
같은 걸 넣어서 http 응답이 분리된다. 첫번째 응답은 개행문자(\r, \n)로 종료시키고 두번째 응답페이지는 공격자가 맘대로 수정 가능하다. replaceAll을 넣어 개행문자를 제거해 응답헤더가 나눠지는 것을 방지해야함
- 버퍼 오버플로우: 프로그램이 버퍼에 저장할 수 있는 것보다 큰 데이터가 들어오지 않도록 입력값과 버퍼의 크기를 비교할 것
- 디렉터리 경로 조작: 파일명을 입력받을 때 상대경로(
\
,\\
)등을 입력하지 못하도록replaceAll()
로 특수문자 제거할 것 - os 명령어 injection:
cmd.exe
명령어와 다른 명령어 삽입하지 못하도록 입력값에 따른 선택지(white list)를 미리 만들어둘것. - 자원 injection: service입력값을 소켓번호로 그대로 사용하는 경우 "-2920"과 같은 값이 들어오면 기존에 80 포트에서 구동되는 서비스와 충돌되어 에러 발생. 검사를 하고, 정해진 선택지 중 정해진 값을 할당하여 포트를 부여할 것.
- 동적으로 생성되어 수행되는 명령어 injection: eval 사용할때도 입력값 그대로 쓸 것이 아니라
&
,"
등으로replaceAll()
하고 써야함 - 프로세스 제어: 외부 라이브러리 사용시 절대경로 사용할 것.
- 무제한 파일 업로드: 업로드하는 파일 타입과 크기 제한. 업로드 디렉터리를 웹서버의 다큐먼트 외부에 설정. 화이트리스트 방식으로 허용된 확장자만 업로드, 확장자도 대소문자 구분없이 처리.
- URL Redirection 시 url과 도메인의 화이트리스트를 이용하고, 사용자로부터 입력받은 url을 그대로 사용하지 말 것
- 시스템의 상태 정보나 중요한 정보는 서버에만 저장한다. 평문으로 사용자의 유저네임, authenticated값 등을 쿠키에 저장하지 말것.대신 세션에 저장해라. 중요한 정보를 쿠키에 저장할 땐 암호화한다. 외부입력 검사가 js를 통해 브라우저에서 이루어져도 서버 측에서 재검사를 한다.
API 악용
- 필요한 파라미터가 null인지 검사할 것
- Java에서 동일 객체는 동일한 해시코드를 가져야한다. 따라서 한 클래스내에서
equals()
와hashcode()
는 둘다 구현하거나 둘다 구현하지 않아야한다. ?????????????
보안특성
- 하드코드된 패스워드
- 사이트간 요청 위조: CSRF(Cross-site Request Forgery) / 교차접속 요청 위조: 공격자가 웹서버에 CSRF 공격 코드를 등록, 사용자가 웹 서버에 CSRF script 포함된 페이지를 요청, 웹 서버는 CSRF script를 포함하여 사용자에게 응답, 사용자는 CSRF에 사용자 권한으로 사용가능한 서비스를 요청. get을 쓰면 form 전달값이 노출되므로 CSRF 공격에 쉽게 노출될 수 있다. 인풋 폼 작성시 post 사용할 것. 또는 폼과 입력을 처리하는 프로그램 사이에 토큰을 사용해 공격자의 직접적인 url 사용을 막을 것
- 부적절한 세션 만료: 세션이 영원히 끝나지않거나 다른 사용자가 세션을 가로챌 수도 있음.
일정시간이 흐르면 세션이 종료되도록
session.setMaxInactivaInterval(12000)
등 양의 정수값 설정. - 취약한 암호화 알고리즘 사용
- 예측 가능한 난수 사용. Java에서
Math.random()
은 seed를 재설정할 수 없어 위험하다.java.util.Random
의setSeed(new Date().getTime())
을 사용해 r을 예측 불가능한 long타입으로 설정해라. - 기밀정보의 단순한 텍스트 전송
- https로만 서비스하는 경우 브라우저 쿠키에 데이터를 저장할때
반드시 Cookie 객체의
setSecure(true)
메서드를 호출해야함. 보안 속성 설정하지 않으면 단순한 텍스트의 형태로 노출될 수 있다.
시간 및 상태
: 프로그램 동작과정에서 시간적 개념을 포함한 개념(프로세스 등)이나 시스템 상태에 대한 정보와 관련된 취약점
- 파일 등의 공유 자원을 여러 스레드가 접근해 사용할 경우, 동기화를 사용해 한번에 하나의 스레드만 접근가능하도록 할 것. 다중 스레드와 공유 변수 사용시 thread safe 함수만 사용, java의 경우 synchronized 사용
- 재귀 종료 잘 되도록 할 것
에러 처리
- 패스워드는 강하게 요구할 것. 패스워드 복잡도를 검증할 것
- 오류 메시지를 통한 정보 노출.
e.printStackStrace()
를 출력하지 말것. 에러 메시지는 정해진 사용자에게 유용한 최소한의 정보만 포함하도록 할 것.
코드 품질: 코드가 복잡하면 안전성을 위협할 취약점들이 있을 수도 있다
- signed int를 unsigned int로 변환하면 음수가 큰 양수가 되는데
이 값이 배열의 인덱스로 사용되면 정수 오버플로우가 일어나 프로그램이 오동작할 수도 있음.
e.g) C의
len()
함수에서 문자열이 null일 경우 -1를 반환하는데 이걸 unsigned int로 변환하면 10억단위가 된다. 버퍼 오버플로우 취약점을 가질수도 있다. null이면 -1대신 0을 반환하게 한다. - int를 char로 변환할 때 4바이트->1바이트니 크기 확인하고 변환
- database connection 중 예외가 발생되어도
finally
를 사용해 할당받은 자원(Jdbc 커넥션)을 반환(close)할 것
캡슐화
- 제거되지 않고 남은 디버거 코드.
System.err.print("디버그코드")
- 민감한 데이터를 가진 내부클래스 사용: 내부 클래스는 컴파일 과정에서 패키지 수준의 접근성으로 바뀐다. 내부클래스를 static 선언해 외부클래스의 private 필드에 접근하지 못하게 한다.
- 변경하면 안되는 public 멤버 변수는 반드시 final 키워드로 선언한다.
- private으로 선언된 배열을 public 메서드로 반환하면 외부에서 배열의 reference를 알 수 있다. public 메서드 안에서 private 배열의 카피를 만들어서 그것을 반환하도록 작성하면 private 배열의 수정을 막을 수 있음
- 시스템 데이터 정보 누출: 예외 발생시
e.getMessage()
같은 것 출력하지말고 IOE exception occured 와 같은 대략적인 정보만 제공.
그 외 사용 가능한 방법들
- 동적 SQL: 조건에 따라 쿼리가 다를 경우 프로그램 실행시 전체 쿼리가 만들여서 db에 요청하는 sql
- 상호배제(mutex): 동시 프로그래밍에서 공유 불가능한 자원의 동시 사용을 막기위해 사용되는 알고리즘
- DAP(Lightweight Directory Access Protocol): TCP/IP 위에서 디렉터리 서비스를 조회하고 수정하는 응용 프로토콜