secure coding

js 유효성 검사 템플릿 (+SQL injection)

Digital Audio Player 2024. 5. 8. 11:05

$('#submitBtn').click(function() {

var frm = document.frm2;

 

/* 유효성 체크 start */

if (!validationChk()) return;

/* 유효성 체크 end */

 

frm.action = "";

frm.method = "POST";

frm.submit();

});

 

var lengthFlag = true;

 

function validationChk(){

var frm = document.frm2;

var title = frm.title;

var phone1 = frm.phone1;

var phone2 = frm.phone2;

var phone3 = frm.phone3;

var email = frm.email;

var problem = frm.problem;

var improvement = frm.improvement;

var benefit = frm.benefit;

var detail = frm.detail;

var agree = frm.agree;

var email_pattern = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+/; //(알파벳,숫자)@(알파벳,숫자).(알파벳,숫자)

 

if (title.value.trim().length == "") {

alert("제목을 입력하세요.");

title.focus();

return false;

}

if (phone1.value.trim().length == "") {

alert("휴대폰 번호를 입력하세요.");

phone1.focus();

return false;

}

if (phone2.value.trim().length == "") {

alert("휴대폰 번호를 입력하세요.");

phone2.focus();

return false;

}

if (phone3.value.trim().length == "") {

alert("휴대폰 번호를 입력하세요.");

phone3.focus();

return false;

}

if (email.value.trim().length == "") {

alert("이메일을 입력하세요.");

email.focus();

return false;

}

if(email_pattern.test(email.value)==false){

alert("이메일 주소가 올바르지 않습니다.");

email.focus();

return false;

}

if (problem.value.trim().length == "") {

alert("현황 및 문제점을 입력하세요.");

problem.focus();

return false;

}

if (improvement.value.trim().length == "") {

alert("개선방안을 입력하세요.");

improvement.focus();

return false;

}

if (benefit.value.trim().length == "") {

alert("기대효과를 입력하세요.");

benefit.focus();

return false;

}

if (detail.value.trim().length == "") {

alert("세부사항을 입력하세요.");

detail.focus();

return false;

}

if (!lengthFlag) {

alert("글자수가 허용범위를 넘었습니다.");

return false;

}

if (!agree.checked) { //체크박스 미체크시

alert("약관 동의를 체크하세요.");

agree.focus();

return false;

}

 

/* check or convert value for security */

if (!securityChk(title, "제목", 1)) {

title.focus();

return false;

}

 

if (!securityChk(problem, "현황 및 문제점",1)) {

problem.focus();

return false;

}

 

if (!securityChk(improvement, "개선방안",1)) {

improvement.focus();

return false;

}

 

if (!securityChk(benefit, "기대효과",1)) {

benefit.focus();

return false;

}

 

if (!securityChk(detail, "세부사항",1)) {

detail.focus();

return false;

}

/* check or convert value for security */

 

 

/* input and textarea value LTRIM RTRIM */

var inputLength = document.querySelectorAll('#frm2 input[type="text"]');

var textareaLength = document.querySelectorAll('#frm2 textarea');

 

for (var i = 0; i < inputLength.length; i++) {

inputLength[i].value = inputLength[i].value.replace(/^\s+/,"");

inputLength[i].value = inputLength[i].value.replace(/\s+$/,"");

}

 

for (var i = 0; i < textareaLength.length; i++) {

textareaLength[i].value = textareaLength[i].value.replace(/^\s+/,"");

textareaLength[i].value = textareaLength[i].value.replace(/\s+$/,"");

}

 

return true;

}

 

//textarea 바이트 수 체크하는 함수

function fn_checkByte(obj){

// textarea 에 onkeyup="fn_checkByte(this)" 추가 

// textarea 태그 아래에 <sup>(<span class="nowByte">0</span>/3500bytes)</sup> 추가 

 

 

const maxByte = 3500; //최대 3500바이트

const text_val = obj.value; //입력한 문자

const text_len = text_val.length; //입력한 문자수

 

var totalByte=0;

 

for(let i=0; i<text_len; i++){

const each_char = text_val.charAt(i);

const uni_char = escape(each_char); //유니코드 형식으로 변환

if(uni_char.length>4){

// 한글 : 3Byte

totalByte += 3;

}else{

// 영문,숫자,특수문자 : 1Byte

totalByte += 1;

}

}

 

var nowByteClass = document.getElementsByClassName('nowByte');

var textarea = document.getElementsByTagName('textarea');

var index = 0;

 

for (var i = 0; i < textarea.length; i++) {

if (textarea[i] == obj) index = i;

}

 

if(totalByte > maxByte){

alert('최대 3500Byte까지만 입력가능합니다.');

nowByteClass[index].innerText = totalByte;

nowByteClass[index].style.color = "red";

lengthFlag = false;

}else{

nowByteClass[index].innerText = totalByte;

nowByteClass[index].style.color = "green";

lengthFlag = true;

}

}

 

/* 웹 취약점 보완 */

function securityChk(obj, target, level){

let keyword = obj.value.trim();

if (keyword.length > 0) {

 

console.log('before attack defense ' + target + ' keyword transformation : ' + keyword);

 

/* XSS attack defense start*/

let expText = /[&<>"'/()*]/g; //특수문자 제거

 

if (expText.test(keyword)) {

//alert(target + "에 [SQL Injection] 특수문자를 입력 할 수 없습니다.");

//keyword = keyword.split(expText).join("");

 

if (level == undefined || level == 0) {

keyword = keyword.replaceAll(expText,".");

obj.value = keyword;

}else if (level != undefined && level == 1) {

var escapeHtmlMap = {

"&" : "&amp;",

"<" : "&lt;",

">" : "&gt;",

'"' : '&quot;',

"'" : '&#39;',

"/" : '&#x2F;',

"(" : '&#40;',

")" : '&#41;',

"*" : ""

/* "script" : "",

"iframe" : "",

"embed" : "" */

};

 

String.prototype.escapeHTML = function() {

return String(this).replaceAll(expText, function(s) {

return escapeHtmlMap[s];

});

};

 

keyword = keyword.escapeHTML();

}

 

obj.value = keyword;

console.log('after [XSS] attack defense ' + target + ' keyword transformation : ' + keyword);

 

}

/* XSS attack defense end*/

 

 

/* SQL INJECTION attack defense start*/

let sqlArray = new Array( //특정문자열(sql예약어의 앞뒤공백포함) 제거

//sql 예약어

"OR",

"SELECT",

"INSERT",

"DELETE",

"UPDATE",

"CREATE",

"DROP",

"EXEC",

"UNION",

"FETCH",

"DECLARE",

"TRUNCATE"

);

 

let regex;

 

for (let i = 0; i < sqlArray.length; i++) {

regex = new RegExp(sqlArray[i], "gi");

if (regex.test(keyword)) {

keyword = keyword.replace(regex, ".");

obj.value = keyword;

console.log('after [SQL Injection] attack defense ' + target + ' keyword transformation : ' + keyword);

//alert(target + '에 [SQL Injection] "' + sqlArray[i] + '"와(과) 같은 특정문자열로 검색할 수 없습니다.');

//return false;

}

}

/* SQL INJECTION attack defense end */

}

return true;

}

 

 

//날짜 유효성 체크 (윤달 포함)

function dateFormatChk(vDate) {

var vValue = vDate;

var vValue_Num = vValue.replace(/[^0-9]/g, ""); //숫자를 제외한 나머지는 예외처리 합니다.

 

/* if (_fnToNull(vValue_Num) == "") {

alert("날짜를 입력 해 주세요.");

return false;

} */

//8자리가 아닌 경우 false

if (vValue_Num.trim().length != 0 && vValue_Num.trim().length != 8) {

alert("날짜를 20200101 or 2020-01-01 형식으로 입력 해 주세요.");

return false;

}

 

//8자리의 yyyymmdd를 원본 , 4자리 , 2자리 , 2자리로 변경해 주기 위한 패턴생성을 합니다.

var rxDatePattern = /^(\d{4})(\d{1,2})(\d{1,2})$/;

var dtArray = vValue_Num.match(rxDatePattern);

if (dtArray == null) return false;

//0번째는 원본 , 1번째는 yyyy(년) , 2번재는 mm(월) , 3번재는 dd(일) 입니다.

dtYear = dtArray[1];

dtMonth = dtArray[2];

dtDay = dtArray[3];

//yyyymmdd 체크

if (dtMonth < 1 || dtMonth > 12) {

alert("존재하지 않은 월을 입력하셨습니다. 다시 한번 확인 해주세요");

return false;

}

else if (dtDay < 1 || dtDay > 31) {

alert("존재하지 않은 일을 입력하셨습니다. 다시 한번 확인 해주세요");

return false;

}

else if ((dtMonth == 4 || dtMonth == 6 || dtMonth == 9 || dtMonth == 11) && dtDay == 31) {

alert("존재하지 않은 일을 입력하셨습니다. 다시 한번 확인 해주세요");

return false;

}

else if (dtMonth == 2) {

var isleap = (dtYear % 4 == 0 && (dtYear % 100 != 0 || dtYear % 400 == 0));

if (dtDay > 29 || (dtDay == 29 && !isleap)) {

alert("존재하지 않은 일을 입력하셨습니다. 다시 한번 확인 해주세요");

return false;

}

}

return true;

}