티스토리 뷰

Sparrow 취약점 점검 

정부에서 진행하는 사업이라 정말 까다롭게 프로젝트가 진행되다보니까 정말 할게 많아졌다.
이번 사업은 감리가 더 빡세서 Sparrow 검출 결과서까지 제출해야했다.
몇천개의 에러를 계속 수정하고, war파일 뽑아서 다시 에러 있나 확인하고 반복 작업이었다.

Sparrow에 에러검출 되었던 항목을 정리해 보았다.

 

1. Math.random()  vs getRandomValues()

 

Math.random()은 암호학적으로 안전한 난수를제공하지 않음
따라서 getRandomValues() 사용 권장

Ref.
https://velog.io/@two_jay/%EB%82%9C%EC%88%98-%EC%83%9D%EC%84%B1-%ED%95%A8%EC%88%98-%EC%95%BC%EB%B0%94%EC%9C%84-%EC%9E%98-%EC%84%9E%EB%8A%94-%EB%B2%95-0vbqtb3r

*Math.random
var s = Math.floor(Math.random() * 4);  // 0 ~ 3

*getRandomValues

- Uint32Array(1)
0부터 2^32까지의 수를 배열에 저장 
(1)은 배열 하나 생성한것

- array[0]%4
2의 32승 랜덤값을 나눈값의 나머지 네자리 뽑아옴

1)
var array = new Uint32Array(1);
window.crypto.getRandomValues(array);
var s = (array[0]%4);  // 0 ~ 3

2)
var array = new Uint32Array(1);
window.crypto.getRandomValues(array);
var secureNumber = (array[0] % 100) + 1;  // 1 ~ 100

 

 

 

 

2. 동등 비교 연산자 ===  VS  == 차이


자바스크립트에서는 타입변환이 유연하다.
특히 ==은 타입을 비교하지 않는다.

10 == '10'   // true
10 === '10'  // false

null == undefined  //true
null === undefined //false

따라서 프로젝트 전체 ==을 ===으로 바꿔주는 작업을 진행하였다.
ex1) if(dreYear == ""){       ->    if(dreYear === ""){
ex2) if (document.getElementById("layerGroupManagerList1").childNodes[i].lastChild == null) {
      ->
 if (document.getElementById("layerGroupManagerList1").childNodes[i].lastChild === null || "layerGroupManagerList1").childNodes[i].lastChild === undefined) {
ex3) if (document.getElementById("layerGroupManagerList1").childNodes[i].lastChild != null) {
      ->
 if (document.getElementById("layerGroupManagerList1").childNodes[i].lastChild !== null && "layerGroupManagerList1").childNodes[i].lastChild !== undefined) {

 

 

3. innerHTML  vs  textContent

 

innerHTML을 사용하면 내부 HTML 코드를 JavsScript 코드에서 새 내용을 쉽게 변경 가능함
textContent는 Node 속성으로 태그와 상관없이 해당 노드가 가지고 있는 텍스트 값을 그대로 읽음

보안때문에 textContent 사용을 권장하기 때문에 전부 textContent로 바꾸었다.



//수정전
measureTooltipElement.innerHTML = output;
measureTooltip.setPosition(tooltipCoord);

//수정후
var dom = new DOMParser().parseFromString(output, "text/html").body.childNodes[0];
measureTooltipElement.appendChild(dom);
measureTooltip.setPosition(tooltipCoord);


4. 크로스 사이트 스크립트

 

외부 입력값에 스크립트가 삽입되지 않도록 문자 변환 함수 또는 메소드를 사용하여
<, >, &, "  를 &lt;, &gt;, &amp;, &quot;로 대체한다.

//수정전
<%
<!- Receive name from external source -->
  String name = request.getAttribute("name");
%>
<!?Print name received from outer source -->
<p>NAME:<%=name%></p>

//수정후
<%
String name = request.getAttribute("name");
if ( name != null ) {
  name = name.replaceAll("<","&lt;");
  name = name.replaceAll(">","&gt;");
} else {
  return;
}
%>

우리 프로젝트 적용 모습
<%
//sparrow
HttpSession sessionUser = request.getSession();
UserVO user = (UserVO) session.getAttribute("user");

String user_id = "";
String nam = "";
String orgzid = "";
String sys_man = "";
String rank = "";
String auth = "";
String rsapublicKeyModulus = "";
String rsapublicKeyExponent = "";
String orgzid_nam = "";

if(user != null) {
request.setAttribute("user_id", user.getUser_id());
request.setAttribute("nam", user.getNam());
request.setAttribute("orgzid", user.getOrgzid());
request.setAttribute("sys_man", user.getSys_man());
request.setAttribute("rank", user.getRank());
request.setAttribute("auth", user.getAuth());
request.setAttribute("rsapublicKeyModulus", user.getRsaPublicKeyModulus());
request.setAttribute("rsapublicKeyExponent", user.getRsaPublicKeyExponent());
request.setAttribute("orgzid_nam", user.getOrgzid_nam());

user_id = String.valueOf(request.getAttribute("user_id"));
nam = String.valueOf(request.getAttribute("nam"));
orgzid = String.valueOf(request.getAttribute("orgzid"));
sys_man = String.valueOf(request.getAttribute("sys_man"));
rank = String.valueOf(request.getAttribute("rank"));
auth = String.valueOf(request.getAttribute("auth"));
rsapublicKeyModulus = String.valueOf(request.getAttribute("rsapublicKeyModulus"));
rsapublicKeyExponent = String.valueOf(request.getAttribute("rsapublicKeyExponent"));
orgzid_nam = String.valueOf(request.getAttribute("orgzid_nam"));

if ( user_id != null ) {
user_id = user_id.replaceAll("<","&lt;");
user_id = user_id.replaceAll(">","&gt;");
}
if ( nam != null ) {
nam = nam.replaceAll("<","&lt;");
nam = nam.replaceAll(">","&gt;");
}
if ( orgzid != null ) {
orgzid = orgzid.replaceAll("<","&lt;");
orgzid = orgzid.replaceAll(">","&gt;");
}
if ( sys_man != null ) {
sys_man = sys_man.replaceAll("<","&lt;");
sys_man = sys_man.replaceAll(">","&gt;");
}
if ( rank != null ) {
rank = rank.replaceAll("<","&lt;");
rank = rank.replaceAll(">","&gt;");
}
if ( auth != null ) {
auth = auth.replaceAll("<","&lt;");
auth = auth.replaceAll(">","&gt;");
}
if ( rsapublicKeyModulus != null ) {
rsapublicKeyModulus = rsapublicKeyModulus.replaceAll("<","&lt;");
rsapublicKeyModulus = rsapublicKeyModulus.replaceAll(">","&gt;");
}
if ( rsapublicKeyExponent != null ) {
rsapublicKeyExponent = rsapublicKeyExponent.replaceAll("<","&lt;");
rsapublicKeyExponent = rsapublicKeyExponent.replaceAll(">","&gt;");
}
if ( orgzid_nam != null ) {
orgzid_nam = orgzid_nam.replaceAll("<","&lt;");
orgzid_nam = orgzid_nam.replaceAll(">","&gt;");
}
}
%>
<script>
   var user_info = new Object();
    user_info.USER_ID = "<%= user_id %>";
    user_info.NAM = "<%= nam %>";
    user_info.ORGZID = "<%= orgzid %>";
    user_info.SYS_MAN = "<%= sys_man %>";
    user_info.RANK = "<%= rank %>";
    user_info.AUTH = "<%= auth %>";
    user_info.publicKeyModulus = "<%= rsapublicKeyModulus %>";
    user_info.publicKeyExponent = "<%= rsapublicKeyExponent %>";
    user_info.orgzid_nam = "<%= orgzid_nam %>";
</script>



사실 이걸 하면서 말이 좀 많았다. 저렇게 하게 되면 < > 이런 값을 실제로 사용할시 값이 바뀌어버리니까 좀 말이 안되니깐...
근데 일단 감리는 넘겨야하고 로그인할때 저런 특수한 문자가 들어갈 일은 없기 때문에 저렇게 진행하였음..


5. for문 안에서 var 선언 하지 않은 경우


//수정전
    for (i = 0; i < rows; i++) {

//수정후
    for (var i = 0; i < rows; i++) {

 

 

 

 

댓글