GIGO MIND DEV BLOG

User Online Status

디스코드나 스카이프 같은 웹 애플리케이션 심지어는 게임 클라이언트 (요새는 다 html5 써서, electron, react native같은 프레임워크 등등으로 제작되더라..)에서도 유저 online state를 제공한다.

구현하는 개념이 어떨까?


먼저 유저가 로그인했는지 안했는지 알려면, 우리는 보통 쿠키나 세션에 유저가 로그인했다는 정보를 넣어서 브라우저가 들고 있게 한다.

브라우저는 꽤나 잘만들어져서 유저가 아무것도 안하고 일정 시간이 지나면 세션을 새로 리셋 시켜준다.

비슷하게도 유저가 온라인인지 아닌지 다른 사람이 볼 수 있게 하려면, 서버는 다음과 같은 두가지 개념이 필요하다.

  • 유저 한명한명 마다의 “세션”
  • “세션”을 다른 사람의 시점에서 언제 업데이트해주나

유저 한명한명마다 정보를 담고 있다.. 이거 어디서 들어봤는데?

그냥 데이터베이스에 테이블을 하나 만들어주면 된다.

Primary Key/foreign key는 유저, 그리고 유저의 “세션”.
세션은 가상의 개념으로 우리가 구상해볼 수 있다.
세션은 유저가 웹사이트에 들어와서 로그인을 했을 때, 그 떄 우리에게 보내는 웹 관련 request들을 토대로 그 시점의 시간으로 정할 수 있다.
만약 마지막에 유저가 보낸 request 시간이 어떤 time interval 안이라면, 유저는 온라인이다. 그 반대는 오프라인이다.

만약 다른 유저가 어떤 유저의 온라인 상태를 봤다 쳤을때 언제마다 업데이트 해주면 되나? 이건 자바스크립트의 time interval을 써서 몇초마다 계속 업데이트 해주면 된다.
이것이 어느정도의 overhead는 있지만, 제일 간단한 방식이다.


디스코드같이 오프라인 모드는 어떻게 하나?
만약 유저가 숨김 status(모드)를 정해놨을시, 이 시간 부분을 훨씬 전으로 바꿔주면 된다. 그리고, status가 숨김일 시, request에 online update를 안해주면 된다.

idle은 아직 잘 모르겠다. 이 부분은 더 찾아봐야겠다.

Notion of Password Hashing

패스워드를 웹사이트에 넣어 회원가입을 한다는 시나리오에서 어떻게 안전하게 패스워드를 저장할 수 있을까?

클라이언트 사이드

만약 패스워드를 웹 브라우저 한 부분에 넣어 서버에 보낸다고 했을때, 해커가 MITM 공격으로 가로챈다고 해도, 이미 HTTP request는 SSL/TLS로 인해 암호화가 완료된 상태이다.
이 부분은 우리가 원리적으로 더 보안을 강하게 하고 싶어도 딱히 할 방법이 없다.

클라이언트 사이드에서 자바스크립트같은 언어로 암호화를 진행한다 해도, 그 symmetric public key encryption과 같은 복잡한 암호체계가 아니고 단순히 암호화를 시키는 거라면, 클라이언트 사이드의 코드를 가지고 있는 유저쪽에서 쉽게 풀 수 있기 때문이다.

즉, 클라이언트 쪽의 패스워드, 그리고 그 패스워드의 암호화는 굳이 신경쓰지 않는다.

서버 사이드

반대로, 만약 해커가 서버 쪽의 컴퓨터를 해킹했다고 쳤을 때, 어떻게 유저들의 암호들을 알 수 없게 하느냐가 매우 중요하다.

여기서 요점은 암호 자체를 해커들이 알 수 있느냐 없느냐다.
다시 말하자면, 해커가 이미 서버사이드를 장악했다고 했을 시에, 유저의 아이디를 통해 우리의 서비스를 이용하는 것은 막을 수 없다.
우리가 암호화를 통해 달성할 수 있는 것은 유저의 암호자체를 해커가 알 수 없게 하여 그 암호를 이용해서 다른 서비스라던가 다른 해킹으로 이어지는 걸 막는 것 뿐이다.

그러면 우리에게는 유저에게서 받아온 패스워드를 토대로 다음 시간에 로그인을 한다고 쳤을 때, 그 두 패스워드가 동일한지 알아볼 수는 있지만, 서버 쪽에서 원래 암호는 알 수 없는.. 그런 구조가 필요하다.

그 one way encryption을 hashing 이라고 한다.

hashing을 가장 쉽게 이해하는 방법은 modular과 같은 기존의 값의 어느 부분을 아예 제거해버리는 수학적 함수이다.

아무 숫자에 %3을 해준다 했을 시에 예를 들어 10%3 이나 1%3 같은 1이라는 결과를 반환하므로 우리는 그 아무 숫자가 10인지 1인지 13인지 알 방도가 없다.

그렇기에 modular 자체도 hash method 라고 볼 수 있다.

어쨋든, 패스워드를 hashing의 함수의 넣어주어 서버 쪽에서는 알아볼 수 없는 형태로 비밀번호를 저장하면 안전하다는 것이다. 그리고 그것이 바로 패스워드 저장의 제일 기초 개념이다.


hash 자체의 문제점

hash function 하나로는 꽤 문제점이 많다.

  • Same password, different users

우연찮게 유저 A와 유저 B가 같은 패스워드를 쓴다고 치자. 그럼 그 둘의 패스워드를 hash한 값도 같아져 버린다.
사실 이 정보는 중요하게 느껴지지 않을지 몰라도 엄청나게 큰 것이다.
그 정보만으로도 조합해야할 패스워드의 수가 적어지기 때문이다.
이를 어쩌나..

  • Dictionary attack

해커들은 유저들이 할 만한 패스워드를 엄청나게 많이 적어두어 하나하나 매우 빠른 속도로 hash시켜서, 저장되어있는 값과 비교할 수 있다.

그렇다면, 같은 값이 나오면 바로 패스워드가 들켜버리는데, 그럼 이를 어찌할까?

  • I don’t guess password, I guess hash codes(Brute Force)

해커들이 패스워드를 예상하고, 그 패스워드를 hash해서 값을 비교하는 방법도 있겠지만, 반대로 서버 쪽 장악은 포기했다 치고 그냥 아무 웹사이트나 가서 유저 아이디 하나 정하고 배 벅벅 긁으면서 가능한 모든 경우의 수를 대입해보는 경우도 있다.

참고로 해시라는 method는 아무 길이의 패스워드를 넣는다 해도 정해진 길이의 값만 반환한다.. 그럼 이를 어쩔까?

  • GPU processing speed

최근 GPU들이 cryptocurrency mining등을 위해 성능이 좋아지면서 엄청난 속도로 복호화를 진행 할 수 있다. 만약 유저가 패스워드를 평범한 길이로 평범한 단어를 넣어서 조합했다면 이는 생각보다 빠르게 해킹당한다. 하.. 이를 어쩌나?


해결 방안

첫번째 문제점을 고치는 법은 주어진 패스워드가 유저마다 다르게 암호화되어야 한다는 것이다.
이는 생각보다 쉽게 해결할 수 있는데, 바로 랜덤한 값들을 패스워드 앞이나 뒤에 붙여주는 것이다.
이를 “salt”라 한다.
그 앞이나 뒤에 붙여주는 salt 값은 같은 유저테이블에 넣어둔다 해도 상관없다.

이미 해커가 서버사이드를 장악했기 때문에 어딘가에 숨기는가는 중요하지않다. 그저 해커가 봤을 때 두 유저가 똑같은 해시를 가지고 있지만 않으면 된다.

같은 논리로 두번째 문제점도 salt를 붙여줌으로서 dictionary attack 자체에 hash하는 값이 다르게 나올 확률을 높여 줄 수가 있다.

그리고 salt를 여러개 저장하고 hash를 여러번 적용하는것도 하나의 방법이다.

세번째 문제점은 매우 간단하다. Hash가 긴 값을 반환하는 것을 사용하면 된다.

예를 들어, 지금도 쓰이고 있는 SHA 해시를 봤을때, SHA1이라는 해시 함수는 128비트를 반환한다.
그리고 SHA512라는 해시 함수는 512비트를 반환한다.

비트당 2의 지수만큼 경우의 수가 증가하므로, 패스워드같이 중요한 정보는 적당히 긴 걸 쓰면 좋다.

네번째 문제점은 세번째 문제점에도 해당되는 내용이지만 시대(computational power)에 맞춰 우리가 계속 생각해보며 바꿔나가야 할 부분이다.

이 부분을 해결하는 원리적인 개념은 Hash 함수의 길이는 늘리는 것도 중요하지만, hash 자체를 생성하는 시간을 늦추면 되는 것이다.

만약 슈퍼컴퓨터를 사용해 엄청난 수의 경우의 수를 빠르게 비교 할 수 있는 능력이 있다고 해도 한번한번의 경우를 위해 자원을 만드는 시간이 오래걸린다면, 전체 수행의 시간이 늘어지기에, 이 속도를 늦추는 느린 hash를 이용하는 것이 trade off로서 굉장이 좋다는 것이다.

유저가 회원가입할때, 로그인할때 0.1초의 시간을 더 들여 한번의 hash를 더 해야 하는 반면, 해커는 엄청난 수를 0.1초를 더 들여야 하는 것이다.


자, 이로서 비밀번호 해시의 개념, 정리가 끝났다.

세줄 요약하겠다.

  • hash(password + salt), apply more hash methods if wanted
  • use slower hash methods like PBKDF2withHmacs for strong GPU
  • our premise is when the server is already compromised, so we don’t care whether the hacker has access to the user authentication or not, we care about the actual password being exposed to the hackers.

Javascript Specific DOM

앞에서 다룬 기본적인 내용들은 다 자바의 그것들과 비슷했다.
하지만 앞으로 다룰 내용들은 자바스크립트의 특수성이라 할 수 있는 웹 개발 프론트엔드에 관한 내용이다.


타이머

사실 혹자는 왜 바로 DOM manipulation을 다루지 않느냐고 물을 수 있다.
이는 타이머, 이벤트 개념이 동적인 엘러먼트를 담당하는 하나의 축이기 때문이다.
시간축이 없이 어찌 변화를 논할 수가 있는가?

자바스크립트에서는 몇초의 시간이 지나면 기능을 수행하는 SetTimeout과 몇초가 지날 때마다 같은 행동을 반복하는 SetInterval 두가지가 있다.

setTimeout(<function>, <time in miliseconds>);

ex) 3초뒤 alert를 띄운다.
var timer = setTimeout(function() {
	console.log('콘솔 예시');
	alert('alert 예시');
}, 3000); 

// to remove timeout timer
clearTimeout(timer);

setInterval(<function>, <interval in miliseconds>);


ex) 매 3초마다 alert를 띄운다
var timer = setInterval(function() {
	console.log('콘솔 예시');
	alert('alert 예시');
}, 3000);

//to remove interval timer
clearInterval(timer);

이벤트

이벤트란 다른 시스템 구조에서의 signal과 동일하다.

OS 프로그래밍에서의 시그널은 시그널 핸들러가 보내진 시그널 종류에 따라 return memory address를 저장하고, stack의 다른 메모리 주소로 보내져 주어진 기능을 수행한 뒤, 원래의 user-space로 돌아오는 signal trampoline을 수행한다.

자바 스크립트에서의 이벤트는 이벤트 리스너가 보내진 이벤트 종류에 따라 주어진 기능을 수행하는 것을 뜻한다. 사실 이런 이벤트 자체는 AHK같은 언어에서도 자주 볼 수 있다. key/mouse listener/hotkey, etc.

AutoHotKey나 Python의 이벤트 리스너처럼 JS의 이벤트리스너도 element와 같이 쓰인다. 이 말이 무슨 말이고 하니..

<element>.addEventListener(<event-type>, <function>);
의 식으로 쓰인다.

ex) 
var element = document.getElementsById('submit');
element.addEventListener('click', function() {

	// element has a style property which is just like css, the following syntax will be explained in a minute. Just keep reading

	element.style.color = 'red';
});

이벤트를 다뤘으니 이제는 HTML DOM을 JS에서 access하는 법을 알면 된다. 이 것만 알면 JQUERY로 넘어가도 나쁘지않다.

DOM

DOM element 중 몇몇 중요한 요소들은 document의 properties로 가져올 수 있다.

var element = document.body;
var element = document.cookie;
var element = document.images;
etc..

JS Selector로 DOM element를 select 할 수 있다.

셀렉터로는 tag, id, class, css selector 등이 있다.

var element = document.getElementById(id);
var element = document.getElementsByClassName(class);
var element = document.getElementsByTagName(tag);
var element = document.querySelector(css-selector);

ex)
css-selector : .list .btn

var element = document.querySelector('.list .bin');

DOM manipulation

위에서 가져온 element에게는 여러가지 properties 들이있다.
이 중에는 css의 속성을 바꾸는 style property도 존재한다.

document.getElementById('temp').style.color='red';

Javascript Array

이번 포스트는 쉬어가는 정리 글이다.
Javascript의 배열(Array)은 Python의 배열(List)과 그렇게 크게 다른점은 없다.
그저 다른 배열 관련 함수를 정리하듯 정리하는 것이다.

Array


var arr = [];
var arr = ['apple', 'banana', 'cherry'];
var arr = new Array();

var len = arr.length;
var str = arr.join('<delimiter>'); -> a-b-c
var arr = arr.reverse();
// sort with comparator
var arr = arr.sort(function(a,b) { return b - a }); 
var arr = arr.concat(arr2);
var arr = arr.slice(n,m); -> [n,m)번째까지 자르기, 원본 유지
arr.splice(n,m); n에서 m개 빼내기, 원본 손상

slice와 splice의 특이한 점은 python과 비슷하게 negative indexing을 쓸 수 있다는 점.
i.e) -1은 length-1 와 같은 index.
또한, length 보다 높은 인덱스는 index out of bounds 에러를 던지는 것이 아니라, 제일 큰 index를 쓴다.


arr.push(x, y, z, ..); <-> arr.pop(); 
arr.unshift(x,y,z,..); <-> arr.shift();

n번째 자리에 item 을 넣기위해서 Python에서는 insert()라는 메소드를 쓴다.
Python : arr.insert(n, item);

자바스크립트에서는 splice를 쓰면 된다.
arr.splice(n,0,item);

String


문자열은 문자의 배열이니 이 범주에 넣겠다. 자바스크립트의 문자열(String)메소드들은 자바의 문자열(String)메소드들과 거의 같다.
이 말이 무슨 말이고 하니..

자바에서는 문자열을 효과적으로 수정하기 위해 StringBuilder라는 클래스를 사용한다.
StringBuffer for thread-safety;
String s = a + b; 라는 짧은 assignment statement조차도 사실은 StringBuilder로 convert 되어 컴파일되는 경우도 있다. 디벨로퍼가 따로 정하지 않아도 말이다.

String s = a + b;
Stringbuilder s = new StringBuilder(a);
s.append(b);
s.insert(a.length()-1, b);
s = s.reverse();

자바스크립트는 이와 다르게 String 한 가지의 오브젝트를 사용한다. 그렇기에 StringBuilder에서 제공하는 여러가지 유용한 built in method와 benefit은 없다고 보면 된다. 그렇다 해도 reverse 정도가 유용하지만 말이다. 메모리 부분에서의 garbage collection 전 heap 이 꽉 차버리는 부분도 포함한다. 즉, 자바의 String과 비슷하다는 말이다.

이렇기에 String 자체를 바로 reverse 하는 static method 또한 존재하지 않는다.

// string을 array로 바꿔준다 -> 자바의 .toCharArray(), split()과 동일
var array = str.split("");
// Python의 구조를 가진 어레이를 reverse 해주는 method 사용.
var reverseArr = array.reverse();
// Python의 구조를 가진 어레이를 합쳐주는 join 사용.
var joinArray = reverseArr.join("");

이 과정은 자바의 그것과 거의 동일하다.

또 다른 예시로는
var ind = str.indexOf('x'); //자바와 동일
var str = str.substring(n,m); //자바와 동일
var up = str.toUpperCase()/toLowerCase(); // 자바와 동일
var text = "        x ".trim();      // 자바와 동일
var chr = str.charAt(0); // 자바와 동일
마지막으로 조금 다른 부분인 length가 있다.
자바스크립트의 length는 property로서 존재한다.
반대로 자바의 length는 method로서 존재한다.
var len = str.length vs int len = str.length();
하지만 이것조차도 파이썬의 len()메소드와는 확연히 다르다.

반대로 Strongly typed인 자바와는 다르게 자바스크립트는 딱히 casting을 해야 할 상황이나 data type conversion같은 일은 일어나지 않는다.

자바에서는 Integer을 String으로 바꿀때 String.ValueOf(num), String을 Integer로 바꿀때 Integer,ValueOf(str) 등의 class들의 static, class methods를 사용하여 바꿔주는 일이 허다하다.

Javascript Characteristics(1)

리액트나 Jquery 관련 글을 포스트 하기전에 JS의 특징에 대해 올려야 한다는 생각이 들었다.


자바스크립트?

나에게 자바스크립트란..

HTML,CSS와 같은 DOM 자체를 수정시키는 FRONT-END와 Node.js등을 통해 RDBMS와 직접적 통신을 통해 데이터를 넣고 받아오는 BACK-END 둘 다 소화할 수 있는 만능 언어이다.


조금 특별한 기초 데이터 타입

또한, Strongly typed가 아닌 언어로서, var이나 let으로 변수(property)를 퉁쳐버린다는 것이 참 편이하다고 볼 수 있다.

함수(method)는 function 이라는 prefix를 이용, strongly typed가 아닌지라 return type같은 것을 나타내지 않는다.
함수 자체를 parameter로 보낼 수도 있으며, call back function이라는 함수 자체를 변수로서 저장할 수 있는 구조도 있다. 이 두 가지는 같은 구조를 가지기 때문에 parameter 로 보내지는 함수 또는 call back 함수의 이름에는 ()가 들어가지 않는다.

Javascript에서의 callback
<script>
//함수 만들기
function isOdd(num){
	if (nun % 2 == 0) return false;
	return true; 
}

function someother(isOddF, num) {
	return isOddF(num);
}
someother(isOdd, 1);

</script>

자바에서는 이와 같은 기능을 구현하기 위해서는 abstraction을 이용해야한다.
쉽게 말해서, function을 하나의 parameter로 보내기 위해서는 그 function의 구조를 어느정도는 abstract 하게 나타내주는 Interface를 하나 만들어야 하며, 그렇게 만들어진 interface의 안에 메소드로서 call back function과 비슷한 기능을 만들어 놓는 것이다.
다른 메소드에 인터페이스를 넣어 주고 싶다면, 따로 인터페이스의 instance를 하나 만들고, 그 instance를 parameter로 넣어 줘야한다.
반대로 다른 메소드에서는 argument로 그 기능을 받아 오기 위해서 인터페이스의 이름을 타입으로 지정하여 변수를 받아오고, 그 변수의 method를 이용하는 방식이 있다.

Java에서의 callback(?)

// 인터페이스 구조 abstraction
interface Example {
	boolean isOdd(num) {
		return num %2 == 0  ? false : true;
	};
}
// 인터페이스 instance
Example e = new Example();

// passing instance as a parameter
public static... main (String args[]).. method(e, 10);

// method that takes the interface instance as an argument.
public boolean method (Example eF, int num) {
	return eF.isOdd(num);
}

자바스크립트에서의 Object는 변수(property)와 함수(method)를 모아 만든 하나의 객체이다. property의 부분은 다른 말로 하면 k-v pair, 즉 다른 언어들의 map과 동일하다. dot notation, key 부분에 quote가 필요없음. 과 bracket notation, key 부분에 quote 가 필요함을 통해 v을 얻어 낼 수 있다. quote는 double-quote와 single-quote가 둘다 쓰일 수 있다. SQL은 문자열을 위해 single-quote 만 쓰고, 어떤 언어들은 double-quote를 쓴다는 점을 생각하면 참.. 답답해진다.
함수 부분은 다른 오브젝트, 즉 클래스의 메소드와 동일하다. 클래스와 동일하기에 python 의 self, java의 this 와 동일한 this 가 존재한다.

	var obj = new Object();
	var obj ={};
	
	obj.age = 10;
	obj.["name"] = "name";	
	
	var obj={
		// property
		age: 10, name = "name"

		// method
		getAge:function() {
			return this.age;
		}

		// abstraction, callback function
		getAge:getAge
	};
	// callback function
	function  getAge() {
		return this.age;
	}