늘모자란, 개발

늘모자란, 개발


2015년

Gossip 2015/12/31 23:59
다시 돌아오지 않을 이시간. 2015년의 12월 31일.
작년에 똑같은 글을 썼는데 아마 잘 안지켜졌을것 같다. 딱봐도 그렇다...
작년의 다짐을 복사해와 인용해본다.

2015 회고

논문

논문은 두세편 정도 쓰려고 한다. 그 이상은 쓸 주제가 있을진 잘 모르겠는데, 아무래도 웹 관련해서 주제선정하고 쓰는데 많은 시간을 보내게 될 것 같은 해이다. 2014년 한학기 동안 논문을 안써서.... 논문에는 많은 시간을 투자해야될 것 같다. 괜찮다. 1년은 기니까.주제는 웹으로 꾸준히 밀 생각인데, 글쎄 웹 관련 논문부터 많이 읽어봐야겠지도서는 딱 오십권을 읽는걸 목표로 하자.
50권.. 정말 지랄염병이다.  열권정도는 읽은 것 같다.
논문은 정확히 세편썼다. 사실 학술대회에 낸거라 질이 아주 저질이지만, 한학기에 하나씩은 써낸것 같다.
제주도에도 다녀오고, 필리핀에도 다녀올 수 있었던건 이 논문같지도 않은 논문들 덕을 봤던것 같다.

2015.04. 한국 정보 처리 학회(KIPS) SDN을 활용한 네트워크 검역시 패킷캡쳐 기능 개선 방안
2015.06. 한국 정보 통신 학회(KICS) SQN에 적용 가능한 XSS 우회공격 필터링 방법 제안
2015.12. CUTE 2015 Design and implementation of packet reassemble module using a multi-queue for network quarantine in SDN

웹으로 쓰고 싶다고 했지만 XSS 정도가 웹이라고 할 수 있으려나. 결국 과제따라가게 되는건 별 수 없는 것 같다.
하지만 그 시간이 헛되었다고 생각하진 않는다. 많은 공부를 했고 논문도 꽤(?) 읽었다

개발

역시 개발이 주제에서 빠질 수 없는데, 조금 장황한 계획이 있다.웹은 계속해서 할 생각이고, 생각나는데로 적어보자면python, ruby, scala node.js + framework - meteor나 express 등PHP - framework 체험도 체험이고, 가을에 나올 7.0도 개발해보고 싶다. 또한, 싴갤러스 클래스화를 진행하고..c# - c#은 정말 한다한다 하는데 하질 못하는 그런녀석이다. 꼭 해보고 싶다.java - android 개발자 등록하고, 푸시앱을 왕창 만들어보고 싶다. 다 개인용도로 쓰는거지....그리고 git와 github을 이용해서 버저닝을 할 계획이고, 전체적인 언어의 깊이를 주고 싶은 한해가 될 것 같다.너무 많은 계획이 아닌가 할 수 있겠지만 어느정도는 다 해본 녀석들이라... 새로운 도전은 없어뵌다.파이썬 루비 스칼라는 마스터는 못해도 개념은 반드시 알고 넘어갈 계획이고 python같은경우는 flask나 django를 이용해서 서비스 하나를 만들어보는게 목표다. 크던 작던 일단 만들어야 뭘 할 것 같다.
개발은 python 외에 손댄것이 하나도 없다.
다만 git을 적용해 체계적으로 개발을 하게 되었다(고 생각한다)
스칼라는 계획에서 빠졌고, ruby 도 ror열풍에 잠깐 흘끔거려봤으나 결국은 손도대지 않았다.
2016년의 목표라면 C와 C#을 다시 할 계획이고(이제 취업을 생각해야되니까?), 싴갤러스의 고질병이던 DB ERROR를 잡아내는데 성공한 한해였다(고 생각한다)
하지만, 역시 다짐과는 달리 언어의 깊이를 더했다고 하기엔 무리가 있을 것 같다. 나는 여전히 얕고, 더 노력해야만한다.

운동

그리고 운동을 할려고 한다. 반 강제로 시작하게 될 것 같지만, 건강이 적신호인건 내가 누구보다 잘 알고 있다몸뚱아리도.... 좀 깔끔하게 살기 위해서라도 운동을 해서 움직이는 습관을 들여야할 것 같다.학교 헬스장을 이용하면 좋을 것 같고, 구체적인 계획을 세워서 강제로라도 시간 맞춰 운동해볼 계획이다. 한시간만이라도..

운동이라는 항목이 있는것도 웃긴다. 맞다. 헬스장 3개월 등록하고 3일 가서 운동했다.
온몸이 너무 아파서 딱 하기 싫던데 정말 파오후들은 이걸 이겨내지 못하기때문에 돼지가 되는 느낌이다. 다만, 밤마다 먹는 맥주를 자제하고 건강도시락을 먹는등 나름의 노력은 하고 있다.....


공모전
2015.06. SKKU ICC BugBounty 특별상
2015.12. SKKU Security Idea Contest 최우수상

2015년엔 뜻밖의 소득이 있었다. 1학기에는 학교에서 열린 버그바운티, 2학기엔 아이디어 공모전에서 수상하는 쾌거가 있었다.
사실, 온전히 나만의 능력으로 해낸건 아니라서 조금 찝찝하다. 버그바운티는 Jake에게서, Idea Contetst는 수행하고 있는 과제에서 영감을 많이 얻었다. 그치만, 분명 축하해야 될 일이다. 잘했다 나!


2016



역시, 다짐의 순간이다. 바쁘게 살아야할 해이다.
이제 절벽에 몰렸다. 나는 나를 믿는다.

졸업

좋든 싫든 반년뒤 학교를 떠난다. 벌써 2년인가 싶은데 시간이 참 빠르다는 생각이 스쳐지나간다. 세미나를 준비하면서 밤을 새고 핫식스도 자기 최면 건다고 하루에 서너캔씩 먹던 날도 있었는데, 벌써 연구실의 최고 선배(박사를 제외하고)가 되고 연구실을 떠날날을 손꼽아 세어야 되는걸 보니 기쁘기도하고 벌써 두렵기도 하다. 하지만, 나는 나를 믿는다

논문

이 칼럼이 여전히 존재하는 이유는 석사학위를 위한 논문을 써야되기때문. 약 40페이지 정도의 논문을 작성해야하나, 예심정도의 길이라면 LNCS 폼으로 15장정도 작성하면 될것 같다. 하지만, 단순히 페이지 채우는게 목적이 아닌 앞으로 계속 따라다닐 논문이니 깊이 있는 논문을 작성하고 싶다. 글쎄, 잘 될지는 모르겠지만 차근차근 준비해보려고 한다.

개발

C를 공부할 생각이다. 아울러 c#까지.
나는 웹을 계속 공부하고 싶지만, 넥슨과 NC등 모집요강을 보면 여전히 C 개발자를 모집하고 있고, 선택하고 있다.
C는 언어중 가장 기본이 되는 언어이며, 앞으로도 가장 중요한 언어라고 생각한다. 그래서 조금 뜬금없지만, C를 다시 공부하려고 한다. 최종적으로는 프로그램을 개발해 서비스해보는것이 목표이다.

건강

최근 술만 먹으면 헛구역질도 하고 좀 상태가 이상하다. 생체리듬은 27세에 깨진다는 말이 있는데, 각별히 조심하고 내년엔 폭식하는 습관을 고쳐보고자 한다. 잘 될지는 모르겠지만, 2015년 말에 시작한 도시락 업체로부터 도시락 받아먹는걸로 시작(... 하지만 잘 지켜지진않는다...ㅠㅠ)

내 원래 좌우명이자 좋아하는 말은
위험에 처했을때 굳이 너와 내가 같이 도망칠 필요는 없다. 내가 너보다 더 빨리 뛰면 되니까.
였다.

하지만, 나는 남들보다 빨리 뛰지 못했고, 이제 절벽끝에 몰려 위험한 달리기를 하고 있다.
나는. 나를 믿는다. 나는 잘할 것이고, 잘해야만 한다. 그리고 그렇게 할 것이다.
2016년, 도약의 해가 되줄 것이라고 굳게 믿는다.
2015/12/31 23:59 2015/12/31 23:59
우선 실시간으로 공격당해서 너무 어이가 없다.
왜냐면, 너무 당연한건데 처리하지 않은 나한테 어이가 없는게 젤 큰것 같다.

우선, 어제 노드 스크립트를 좀 수정하고 redis 키를 갱신하도록 코드를 짰는데, 낮에 갑자기 뜬금없이 모든 키들이 증발하기 시작했다. 나는 코드에서 이슈가 나는줄 알고 얼른 복구해봤지만 그대로 날아가긴 마찬가지..
답답한마음에 redis 키를 살펴보던 와중에 정말 재수가 좋았던건지 이걸 발견하게 된다.

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCcuHEVMRqY/Co/RJ5o5RTZmpl6sZ7U6w39WAvM7Scl7nGvr5mS4MRRIDaoAZpw7sPjmBHz2HwvAPYGCekcIVk8Xzc3p31v79fWeLXXyxts0jFZ8YZhYMZiugOgCKvRIs63DFf1gFoM/OHUyDHosi8E6BOi7ANqupScN8cIxDGsXMFr4EbQn4DoFeRTKLg5fHL9qGamaXXZRECkWHmjFYUZGjgeAiSYdZR49X36jQ6nuFBM18cEZe5ZkxbbtubnbAOMrB52tQX4RrOqmuWVE/Z0uCOBlbbG+9sKyY9wyp/aHLnRiyC8GBvbrZqQmyn9Yu1zBp3tY8Tt6DWmo6BLZV4/ crack@redis.io


RSA 키인데, 키 이름은 crackit 이었다. 뭔가 깨림칙해서 구글링해보니...

http://antirez.com/news/96

이 링크부터 시작이다.
결론은 대부분의 redis 사용자들이 bind를 0.0.0.0 이나 로컬인 127..... 로 하기때문에, 텔넷을 붙여보면 쉽게 돌파할 수 있다는 내용이었다. 그거에 라이브로 털리고 있고 -_-

단순한 장난같아보였는데, 키는 키대로 다 날아가고 있고.. 특히 나는 세션을 레디스로 관리하기떄문에 사용자 세션이 정말 승천하는 중이었다. bind 세팅을 바꿔보니 제대로 붙질 못하고, 호스팅 업체에서 제공하는 파이어월을 세팅하니 노드가 못붙는다... 왠진 모르겠다.

결국 아이피테이블로 모든 접근을 제한하고, 웹서버에서 요청하는것만 붙을 수 있도록 정책을 수정했다.

iptables -A INPUT -p tcp -s IP  --dport PORT -j ACCEPT;
iptables -A INPUT -p tcp --dport PORT -j DROP
iptables -nvL


redis자체에서도 AUTH를 해줄 수 있으니,
소스코드를 수정해서 보안적으로 접근할 수 있도록 해야할 것이다. 당연히 오픈소스인 MySQL 도 그렇고...
보이는것 외에 보이지 않는것에도 신경을 제대로 써야겠단 생각이 든다
2015/11/10 16:13 2015/11/10 16:13
이 글은 HSV(HSL) for DB query - Sort color by lightness 에서 이어진 글입니다.

HSL은 밝기 기준으로 색을 정렬하는 기준이다.
하지만, 무작위 배열을 선택해 정렬했을 때는 전혀 정렬한것 같게 보이지 않는다는 단점이 존재한다.
복습해보자면, 간단한 쿼리로 HSL을 계산해서 색을 정렬했었고 그 결과는 다음 이미지와 같다.



사실 표본이 작기 때문에 이정도로도 충분히 정렬되었다고 판단할 수 있다.
하지만 모든 표본을 나열해보면 그렇지가 않다.
다소 길지만 비교를 위해서 그냥 올려본다. 양해를 구한다.



모든 표본을 나열했을때 밝음에서 어두움으로 넘어오곤 있지만, 사실 밝기 diff는 몰라도 사람의 시각으로는 이 데이터는 '정렬' 되었다고 말할 수가 없다.
물론, HSL이 쓸모가 없다는것은 아니다. 단지, 무작위 데이터를 처리할때 한가지 더 처리를 해줘야된다는것을 얘기하고 싶은것이다.

color Difference를 구하는 방법은 여러가지가 있으며 위키피디아에서 다수의 방법을 확인할 수도 있으며,
휘도(Luminance)로 정렬하는 방법도 존재한다.

하지만 휘도는 결국 Brightness 로서 HSL과 별 다를바가 없으며, 정렬햇을때도 결과가 비슷했다.
여러 방향으로 접근을 해봤으나 결론은 그라데이션(gradient)를 구현하려면 한가지 처리를 더 해야겠다고 생각했는데 (말은 쉽게하지만 나름 고민 하느라 많은 시간을 보냈다 ㅠㅠ) 그러다가 이 글을 보게 되었다.

내가 얻은 결론은 한가지 색을 뽑고 그 색의 color DIff를 계산해서 근접한 값을 모아줘야된다것이다.

데이터가 200개도 안되는 작은 숫자였기때문에, 선택 정렬(selection sort)을 이용해 정렬을 해볼까 했다.
2단 루프로 o(n^2)로 엄청나게 느리지만 표본이 적고, 먼저 HSL로 정렬된 값을 뽑기때문에 가능할거라 생각했지만..
중복처리를 하려면 답이 안나와서 결국 멋대로 작업해보기로 했다.
루프를 하나 돌고, 어레이를 삭제하고 초기화하는 좀 극단적인 방법이지만 별 다른 수가 떠오르지 않았다.....ㅠㅠ

색 판별은 delta E(CIE 2000) 알고리즘을 사용하기로 했다. color diff를 계산하는 방법은 여러가지가 있었으나, 색사이의 distance를 계산해야했기때문에 이 알고리즘을 선택했다.
라이브러리르는 직접 구현하지 않고, github에서 찾아다 썼다.

$sArray = Array();
$sArray[0] = $array[0];
$count = 0;

$i = 0;
while ( count($array) ) {

    $diff = "";
    $pointer = "";

    for ($j = 0; $j < count($array); $j++ ) {
        $cDiff = (new color_difference())->deltaECIE2000([ $sArray[$i]['colorR'], $sArray[$i]['colorG'], $sArray[$i]['colorB'] ], [ $array[$j]['colorR'], $array[$j]['colorG'], $array[$j]['colorB'] ]);
            if ( $diff == "" ){
                $diff = $cDiff;
                $pointer = $j;
            } else {
            if ( $cDiff < $diff )  {
                $diff = $cDiff;
                $pointer = $j;
            }
        }
    }

    $sArray[] = $array[$pointer];
    if ( $i == 0 ) {
        unset($array[0]);
    }
    unset($array[$pointer]);
    $array = array_filter($array);
    $array = array_values($array);
    $i = count($sArray)-1;

}


그 결과이다.


아래 그림은 최종본은 아니고, 실제로는 정렬되어있다.
재밌는 점은 분명히 이렇게 엮일거같다고 생각한 색들도 알고리즘에 의해 계산된 숫자 수치로는 다르다는점이었다
사람의 시각으로 정렬하는게 맞겠지만.. 일단은, 여기까지!



색상 정렬 (Color sort) 맺으며.. 에서 이어집니다.


Reference
https://en.wikipedia.org/wiki/HSL_and_HSV
https://en.wikipedia.org/wiki/Color_difference
https://github.com/renasboy/php-color-difference
2015/10/20 03:16 2015/10/20 03:16
PHP 갖고 논지는 오래됐다.
문득 이런생각이 든게, 프로젝트도 커지고 했는데, 언제까지고 나만 이 프로젝트를 관리하려나 싶어졌다.
(대부분의 한국인들은 editplus나, sftp, ftp를 지원하는 에디터를 이용해 소스를 바로 수정해서 올릴거라 생각한다.
왜냐면 PHP는 별도의 빌드가 필요없기 때문이다. 아니면 별도의 개발서버를 둬서 한번에 다 덮어쓴다던가..)

누군가와 협업하게 된다면 버전관리와 diff 체크를 해야할텐데, 지금까지의 형태론 도저히 답이 안나왔다.

그래서, delpoy를 하는 방법에 대해 찾아보기로 했다.
우선 phpschool 과 생활코딩에 물어보니, php는 빌드가 필요없이 즉각 적용되기때문에 어떻게든 올리기만하면 된단다.
그래서... 다들 따로 관리는 안하는구나 생각했다.

나는 우선 버전관리는 별도의 git 서버를 구축하지 않을것이라 굳게 다짐했기때문에,
github를 이용하기로 했다. private repo를 다섯개를 사용하기때문에 비공개적으로 관리할 수 있을것이라 생각했다.
깃헙을 사용하지 않겠다면 gitlab이나 bitbucket 과 같은 서비스도 있으니 고려할 수 있겠다.

우선 ftp 로 파일을 싹 받고 .gitignore를 설정했다. 자주 바뀌고 추가되는 이미지들은 버전관리할 필요가 없기 때문이다.
http://dolfalf.tistory.com/58
http://trend21c.tistory.com/1471

두곳을 참조해 적당한 룰을 만들면된다.
로컬 세팅은 귀찮음에 의거해 따로 하지 않기로하고, push 관리같은것도 github desktop을 사용하기로 한다.
즉, IDE에서 수정한 내용을 compare하고 push 하는 git의 기능을 그냥 프로그램에 전적으로 (-_-) 의존하기로 해본다.

git사용을 극대화하기 위해서, github에서 만든 atom 에디터를 사용하기로 했다.
atom은 현재 .git와 비교해 달라진 파일, 생성된 파일을 별도로 표시해준다.



내용을 변경했다면 github desktop에서도 확인할 수 있다.



여기서 pull request나 commit을 관리할 수 있다.
이후 sync 버튼을 클릭해 파일을 싱크하고, 해당 작업이 완료되면 서버의 ssh로 들어가 git 명령어를 친다.
서버에서 git 명령어를 사용할 수 있도록 설치되어 있어야한다. (apt-get install git)

SSH등 여러 방법이 있지만 나는 아주 귀찮았기 때문에, 별다른 등록이나 인증과정없는 HTTPS를 사용하기로한다.
사용법은 이렇다.

git clone https://{user}:{password}@github.com/{user}/repository.git {direcotry}
git pull https://{user}:{password}@github.com/{user}/repository.git {direcotry}


git clone과정은 현재 버전을 받기 위해서 하는 과정이고, 실제 가장 많이 쓰는 deploy는 git pull이다.

사실 git에 대해 자세히 알고 싶을수도 있는데, 그런 사람들을 위해 쉽고 빠르게 이해할 수 있는 여러 링크를 준비했다.
http://classic.scottr.org/presentations/git-in-5-minutes/?hc_location=ufi
http://blog.outsider.ne.kr/865
http://blog.outsider.ne.kr/866

특히 outsider 님의 글은 이 프로세스를 거의 모두 설명하다시피 하고 있으니 참고하면 좋겠다.

git pull을 하면 다음과 같은 화면을 보게되고, 정상적으로 deploy되게 된다.
# git pull https://{user}:{password}@github.com/{user}/repository.git {direcotry}
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 7 (delta 6), reused 7 (delta 6), pack-reused 0
Unpacking objects: 100% (7/7), done.
From https://github.com/{user}/repository
 * branch            HEAD       -> FETCH_HEAD
Updating ...
Fast-forward
 *.php  |    2 +-
 *.php  |    2 +-
 *.php |    2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)


나처럼 버전관리는 하고 싶은데, 귀찮다면 이렇게 하는 방법도 있다고 소개해본다.
2015/09/29 16:56 2015/09/29 16:56

1년

Gossip 2015/08/24 20:38
1년전 오늘 8월 30일.

수원. 그리고 성균관대학교 대학원에 입학하게 되었다.
그동안 나는 무얼했나 되새겨보면 정말한게 없는것 같다.
누군가 석사과정은 글쓰기를 연습하는 시간이라고 하던데, 글쓰기 능력도 그렇게 나아진것 같진 않고 오히려 퇴화한 기분이다.

연구실에서는 많은것을 배웠고 수업은 글쎄. 썩 만족스럽진 않고 별로 중요하게 생각되지 않는것이 사실이다.
연구실 과제들중 도움되는 과제들도 있었다만 대부분 문서 작업이었으며 문서는 대부분 그냥 보여주기식이 대부분이다.
정보 검색 대회하는 느낌이며 이만큼 찾아봤다 하고 과시하는 느낌이 강하게 들지만. 리서치라는게 그런것인가 하고 넘어가고..

시간 흐름대로 한 일을 짚어보자면,

4월, 6월에 논문 한편씩 발표를 하고,
교내에서 열린 버그바운티 대회에 참여해 3위 입상. 이게 큼지막한 이벤트고 나머진 공부..? 글쎄.

이론적으론 좀 강해진 기분이 들지만,
1년전의 아무 선택지가 없어서 이 곳에 올수밖에 없었던 내가. 거의 시궁창급이었던 내가.
지금의 나에게 얼마나 더 나아졌냐 하고 물어본다면 할말이 있겠나 싶다.

앞으로 1년 남았는데
예심/본심을 쓰는 기간이다.
처음에는 단순히 석사라는 타이틀만 달러 왔는데
어느 누구도 내 앞길을 책임져주진 않는다.

쌓여만 가는 학자금대출 빚을 보고 있으면 마음이 무겁다.
출세해야지. 그럴려면 공부해야지.
2015/08/24 20:38 2015/08/24 20:38
디비 쿼리 최적화는 정말 끝나지 않는 싸움같다.
싴갤러스에서 사용하고 있는 쿼리도 여러번 바뀌었는데 다 기억은 못하고..

글 내용을 말하기에 앞서 테이블 구조부터 소개를 하자면,
아래와 같이 생겼다. 아주 심플한 테이블이라 할 수 있다.

CREATE TABLE `TABLE` (   
`index` int(11) NOT NULL AUTO_INCREMENT,   
`date` varchar(10) NOT NULL,   
`time` varchar(10) NOT NULL,   
`nick` varchar(30) NOT NULL,   
`data` varchar(200) NOT NULL,  
 PRIMARY KEY (`index`),   
 KEY `indx_date` (`date`),   
 KEY `indx_nick` (`nick`) ) 
 ENGINE=InnoDB AUTO_INCREMENT=4016741 DEFAULT CHARSET=utf8


사실 처음에 몇만건 몇십만건 됐을때는 아무렇게나 짜도 무리가 없었는데 백만단위로 들어가면서부터 검색 범위를 제한하게 됐다.
유쾌하지 않은건 사실이다. 성능 때문이라는 변명에 괜히 숨는 느낌이라고 해야하나...
그래서 항상 고민이 많다. 이 글을 작성하는 시점에는 멀쩡한데 저녁에 서비스가 몰리는 시간엔 어찌될지...

검색 쿼리는 다음 조건을 가진다.
1. 날짜 범위 지정
2. 닉네임은 매칭 / 내용은 와일드카드 검색
3. 최근 순서대로 정렬

이를 간단히 생각하면 다음과 같이 작성해볼 수 있을 것 같다.
(실제로 생활코딩에 질문을 올렸을때 어떤 누가 이런식으로 쿼리를 짜서 주었음)

select `index`, `date`,`time`, `nick`, `data` from TABLE 
where `date` BETWEEN '20150709' AND '20150716'  and `data` like '%검색어%' OR `nick` = '검색어' 
order by date desc


별 다른 특이사항이 보이지 않는 쿼리이다. 과연 성능은 어떨까?

Explain extended 를 사용하여 쿼리가 어떻게 수행되는지 확인을 해보자.
SIMPLE    TABLE    ALL    indx_date,indx_nick                387809    100.00    Using where; Using filesort


검색은 SIMPLE하고, possible한 key도 기재가 되지만, 실제로는 인덱스를 타지 않는다.
왜냐면 컬럼중에 인덱싱 범주에 포함되지 않는것들이 다수 있기때문이다.
또한 검색에 소요되는 Row의 수도 거의 테이블 전체를 seeking 해야하는 수준이며, 검색 조건중 반드시 배제되어야 된다는 filesort를 통해 order by를 수행한다.

따라서, 위 쿼리로 서비스를 했다간 그냥 db가 뻗어버릴것이다

그래서 두번째로 생각한것이 서브쿼리를 이용해보는것이었다.
내 착안은 이렇다.

1. 날짜 범위별로 검색범위를 제한한다.
2. 서브쿼리에서 배달된 (delivered) 쿼리중에서만 추가검색을 진행한다.

결론부터 말하자면 근접한 방법이긴한데 정답은 아니다.
쿼리를 보면서 계속하자
SELECT `index`, `date`,`time`, `nick`, `data` 
FROM     (         
                 select `index`, `date`,`time`, `nick`, `data` from TABLE
                 WHERE `date` BETWEEN '20150700' AND '20150716'
              ) as origindata
WHERE (    `data` like '%검색어%' OR `nick` = '검색어' )
ORDER BY `index` DESC


서브쿼리를 이용해서 인덱싱이 되어있는 Date 컬럼을 이용해 범위를 제한하고,
나온 결과에서만 검색을 수행하겠다는 계획이고, 실제로 위 쿼리로 서비스를 오래 했다.

이 쿼리의 explain은 다음과 같다.
1    PRIMARY    <derived2>    ALL                    177838    100.00    Using where; Using filesort
2    DERIVED    TABLE    ALL    indx_date                386966    45.96    Using where


읽는 순서는 역순으로..
인덱스를 타라고 일부러 서브쿼리로 나눠놨는데 위에서 언급한것과 마찬가지로 인덱싱이 되지 않는 컬럼을 select하기때문에 결국 전체 테이블을 seeking하게 된다.

결과적으로 놓고보면, 첫번째 쿼리와 다를게 없다....
filesort도 그대로 사용하고 있고, 인덱스도 여전히 이용하지 않고 있다.

그럼 어떻게 해야할까? 우선 인덱스를 타게 해야겠다고 생각했다.
그래서 쿼리를 좀 고쳐봤다. 요렇게
select b.`index`, `date`,`time`, `nick`, `data` from TABLE as b
left join (
   select `index` from TABLE where `date` BETWEEN '20150709' AND '20150716' ) a
on a.`index` = b.`index` 
where a.`index` = b.`index` and  `data` like '%검색어%' OR `nick` = '검색어'
ORDER BY b.`index` DESC


위 쿼리는 인덱싱이 되어있는 `index`컬럼과 `date`컬럼을 사용해보자고 생각을 했다.
그래서 서브쿼리 조인을 사용해서, 날짜에 해당하는 index들만 뽑아낸 후 바깥 테이블에서 해당 primary index에 해당하는 녀석들에서만 검색을 해보자고 시도를 해봤다. 결과는 실패였지만 과정은 괜찮았다.
1    PRIMARY    b    index    PRIMARY,indx_nick    PRIMARY    4        387047    100.00    Using where
1    PRIMARY    <derived2>    ref    <auto_key0>    <auto_key0>    4    lute.b.index    10    100.00    Using where; Using index
2    DERIVED    TABLE    range    indx_date    indx_date    32        179508    100.00    Using where; Using index


드디어 Extra에 using index가 등장하기 시작했다.
하지만 결국 b 테이블은 전체 테이블을 seek해서 배달되는 index랑 비교를 해야되기때문에 컬럼수가 무자비했고, 간혹 돌리다보니 using filesort뿐 아니라 using temporary까지 .....
오히려 더 안좋은 결과가 되었다. 실제 쿼리타임도 쿼리를 두번 돌려야되는 꼴이기때문에 더 느린건 두말할것도 없다

하지만 아이디어 자체가 나쁘다고 생각하지 않았기때문에 using temporary라는 키워드로 검색해보다가 이곳을 발견하게 되었다.
이제 내가 해결해야될 문제는 명확했다.

1. using index를 무조건 띄워야된다.
2. filesort 및 temporary가 뜨면 안된다
3. 최소한의 Row를 사용해야한다

링크한 블로그의 커버링 인덱스(covering index)를 참조하여 다시 작성한 쿼리는 다음과 같다.
select a.`index`, a.`date`,a.`time`, a.`nick`, a.`data` 
from (      
       select `index`  from TABLE
       where  `date` BETWEEN '20150700' AND '20150716'
       order by `date` desc
       ) b 
join TABLE a
on b.`index` = a.`index`
where a.`data` like '%검색어%' OR a.`nick` = '검색어'


사실 뭐가 다른지도 잘 구분이 안된다만 엄연히 다르니 잘 보자
따로 말로 설명하면 길어질것 같으니 성능부터 보자
1    PRIMARY    <derived2>    ALL                    193547    100.00    
1    PRIMARY    a    eq_ref    PRIMARY,indx_nick    PRIMARY    4    b.index    1    100.00    Using where
2    DERIVED    TABLE    range    indx_date    indx_date    32        193547    100.00    Using where; Using index


확연한 차를 보이고 있고, 제시한 모든 문제가 다 해결되었다.
최소한의 Row를 사용하고, index를 이용해 order by 까지 처리하는걸 볼 수 있다.

이번글에서는 엄청난 내용을 소개한것은 아니다.
하지만 쿼리 한개라도 아무렇게나 작성하면 안되고 충분한 테스트 및 성능 검증이 필요하다고 말하고 싶었다.
그래서 일일히 삽질한 쿼리도 공개를 해보면서 풀어나가듯이 작성해봤다.
어쨌든 디비쿼리와의 전쟁은 계속되겠고 최적화를 위해서 계속 삽질을 하자.



Reference
http://gywn.net/2012/04/mysql-covering-index/




2015/07/16 15:43 2015/07/16 15:43
색을 정렬하는 방법은 무엇인가.

운영하는 사이트 컨텐츠중 지정 염색 앰플 도서관에 들어갈 색상을 정렬해야 하는 문제가 생겼다.

db set은 대강 이렇게 생겼다.

빕분홍 255 170 170 #ffaaaa
실버분홍 164 129 140 #A4818C
그레이체리 142 128 138 #8E808A
탁분홍 197 141 141 #c58d8d
진달래 172 72 126 #ac487e
진한딸기우유(진딸우) 255 111 189 #ff6fbd
딸기우유(딸우) 255 129 211 #ff81d3
딥핑크 160 19 64 #a01340
핫핑크 199 25 103 #c71967


처음에는 대수롭지 않게 hex (web color code)대로 정렬해봤다.
간단히 webcode를 desc대로 정렬해봤다.


정렬은 되는데 뭔가 이상한..?
정렬이 안된다는 느낌이라고 해야하나..
다른 결과를 좀 내려다보니 아니나 다를까, 정렬이 되고 있지 않았다.
아니 정확히 말하자면 이렇게 정렬이 되어선 안됐다



딱봐도 이상하잖아..

그래서 이것도 그냥 정렬해서 안되겠구나 하고 찾아보니 색은 HSV나 HSL 이라는 하이라이트(밝기) 기준으로 정렬해야된단다.
관련 문서 같은건 위키나 이곳의 샘플코드를 보면 되겠다.

이렇게 한창 만들고 있다가, 잘 생각해보니 정렬을 위해선 모든 dataset에 배열안에 다들어가 있어야되는데,
read해서 바로 뿌려주는 내 페이지에는 비적합하다는 생각이 들었다. dbset이 몇개가 될줄알고 그걸 정렬을 위해 다 들고 있어야되나 이 생각이 들었다. (물론 이건 케이스마다 다른얘기)

그래서, 애초에 쿼리로 HSL을 적용해서 불러오면 어떨까 생각이 들었다.

결론부터 말하자면 웹컬러코드는 필요 없다. RGB색상이 필요하다.

select * from TABLE order by SQRT(colorR * colorR * 0.241 + colorG * colorG * 0.691 + colorB * colorB * 0.068) desc


HSV나 HSL은 결국 채도, 밝기에 의해 정렬되는것이기때문에 색상별로 가중치를 줘서 더한값을 정렬해야한다.
그 값은 쿼리에 나와 있듯이 R*R*0.241 + G*G*0.691 + B*B*0.068 값으로 구성된다.
즉, 그라데이션 같은 값으로 나오진 않는다. 하지만 색상정렬이 밝기별로 되고 있다는 느낌은 확실히 받을 수 있다.

다음은 위 쿼리를 설정해서 검색해본 결과.



1) Sort color using delta E(CIE 2000) - Similar Color gradient 에서 이어집니다
2) 색상 정렬 (Color sort) 맺으며.. 에서 이어집니다

2015/06/23 15:15 2015/06/23 15:15
Assignment #3
주어진 평문, 암호문을 이용하여 사용된 두개의 키를 알아내는 과제

1. 암호문은 DES(ECB), AES-128(CBC)를 이용하여 암호화됨
2. 계산시간의 축약을 위해, password list가 주어진다. 리스트에는 md5 hash 값과, word가 한라인에 공백으로 구분되어 기재됨
2-1. 리스트는 약 18만 5천여개의 hash로 이루어져있다.
3. DES CBC이므로 8바이트 key를 사용해 암호화를 해야하므로, 주어진 hash에서 가장 앞글자 8자리를 사용할것
4. AES-128은 주어진 hash를 모두 사용해 암호화할것




외국사람들이 내 블로그 볼일이 있겠나 싶어 그냥 한글로 정리한다.

처음에 이 과제를 받고 어떻게 해야하나 생각을 해봤다.
평문과 암호화된 텍스트, 그리고 심지어 암호화 방법도 주어졌으니
암호화를 두번하고, 나오는 cipher text를 주어진 암호문과 비교 하는방법을 생각했다.

당연히, NP-hard Problem 이라고 생각했다. 그 외의 방법이 있나.. 싶었는데 내가 멍청이란걸 깨닫기전엔...
그래도 모르니 일단 코드 공유를 한다.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os,sys,timeit,string,base64
from Crypto.Cipher import AES,DES

__author__ = "Song Myeong-Uk"
__date__ = "2015.05.27"

#global value
md5_keys = {}
target_plaintext = ""
encrypted_plaintext = ""

start_time = timeit.default_timer() #when program start

#@func: download_data
#@desc: If there is no md5 hash passwords, download it
def download_data():
  print "[-] passwords.txt not found! downloading now..."
  url = "http://www.ece.ubc.ca/~hyoung/passwords.txt"
  urllib.urlretrieve(url, 'passwords.txt')
  print "[-] passwords.txt download complete"

#@func: save_input_data
#@desc: Insert input data to global variable
def save_input_data():
  global target_plaintext
  global encrypted_plaintext

  i = 0
  with open('PlaintextCiphertext.txt', 'r') as f:
    i = 0
    for line in f:
      line = line.replace("\r\n","").replace("\n","")
      if i is 0:
        target_plaintext = line
      elif i is 1:
        encrypted_plaintext = line
      i = i + 1
  print "[-] Done!"

#@func: download_data
#@desc: To faster calculate. insert file data in memory instead IO system
def md5_to_memory():
  global md5_keys
  with open('passwords.txt', 'r') as f:
    for line in f:
      key_hash = line.split(" ")[0]
      password = line.split(" ")[1].replace("\r\n","").replace("\n","")
      md5_keys[key_hash] = password
  print "[-] Done!"

#@func: DES_encrypt
#@param hash: first selected key of text file
#@desc: Simple DES(ECB) encryptor. return encrypted text
def DES_encrypt(hash):
  global target_plaintext

  des = DES.new(hash[:8], DES.MODE_ECB)
  text = pad(target_plaintext,8)
  cipher_text = des.encrypt(text)

  return cipher_text

#@func: AES_encrypt
#@param DESed: DES encrypted text
#@param hash: Second selected key of text file
#@desc: Simple AES(CBC) Encryptor. return encoded base64 text 
def AES_encrypt(DESed,hash):
  global target_plaintext
  
  #128 bits = 16bytes

  mode = AES.MODE_CBC
  encryptor = AES.new(hash, mode, IV='\x00'*16)
  text = pad(DESed,16)

  ciphertext = encryptor.encrypt(text)
  return ciphertext.encode('base64')

def s2b(x):
  return ''.join(c.encode('hex') for c in x)

#@func: decrypt
#@param enc: encrpyted text
#@param k1: First selected key of text file
#@param k2: First selected key of text file
#@desc: just test case
def decrypt(enc,k1,k2):
  # enc = "W7n515icc+1dW5+82CYgPOGyFqgaFLA0FTCgB/jw1DZBeKUUF2h4z7yRWb03ZIeE"
  # k1 = "0001245350b5eb0a1548fc6d27d3b4d1"
  # k2 = "00009965"
  text = enc
  dec = AES.new(k1, AES.MODE_CBC, IV='\x00'*16)
  des = DES.new(k2[:8], DES.MODE_ECB)
  return unpad(des.decrypt(pad(unpad(dec.decrypt(text.decode('base64'))),8)))

#@func: pad
#@param x: text
#@param b: limit
#@desc: add null character to text untill b
def pad(x,b):
  pad1 = lambda s: s + (b - len(s) % b) * chr(b - len(s) % b) 
  return pad1(x)

def unpad(x):
  try:
    unpad1 = lambda s : s[0:-ord(s[-1])]
    return unpad1(x)
  except Exception,err:
    return x
  

#@func: progress
#@desc: execute calc when it ready
def progress():
  global md5_keys
  try:
    for hash in md5_keys:
     for hash2 in md5_keys:
       DESed_text = DES_encrypt(hash)
       if encrypted_plaintext is AES_encrypt(DESed_text,hash2):
         print "[-] find keys ... !!"
         print md5_keys[hash]
         print md5_keys[hash2]
         raise BreakAllTheLoops()
       else:
        #debug to print. you can pass it
        #pass
        print "[-] Trying key : {} - {},{}".format(AES_encrypt(DESed_text,hash2),md5_keys[hash],md5_keys[hash2]).replace("\r\n","").replace("\n","")
    print "[-] Can not find keys ... "
  except BreakAllTheLoops:
    pass

  # for hash in md5_keys:
  #   for hash2 in md5_keys:
  #     if target_plaintext is decrypt(encrypted_plaintext,hash2,hash):
  #        print hash
  #        print hash2

# main execution
if __name__ == "__main__":
  print "[+] starting {}...".format(os.path.basename(__file__))
  # check if data file exists

  #check input file exists
  cipher_exists = os.path.isfile('PlaintextCiphertext.txt')
  print "[+] checking input file: PlaintextCiphertext.txt"
  
  if not cipher_exists:
    print "[+] PlaintextCiphertext.txt not found!"
    sys.exit()
  else:
    print "[-] Done!"

  print "[+] checking password data: passwords.txt"
  txt_exists = os.path.isfile('passwords.txt')
  if not txt_exists:
    download_data()
  else:
    print "[-] Done!"

  print "[+] Process init ... [1/2]"
  save_input_data()

  print "[+] Process init ... [2/2]"
  md5_to_memory()

  print "[+] Start processing ... "
  print "[-] Target plaintext : {}".format(target_plaintext)
  print "[-] Target Encrpyted key : {}".format(encrypted_plaintext)
  progress()

  stop_time = timeit.default_timer() #when program end

  print "Done!!"
  print "Program Run Time : " + str(stop_time - start_time)


코드는 단순하다. 먼저 DES를 하고 AES를 해서 최종
총 키가 18만개니까, 최악의 경우를 고려하면 O(n^2) 가 되는 방법이다.
1초에 30개씩 처리한다고 해도 며칠은 돌려야된다.. 잘될리가 없었다. 속도문제도 그렇고 퍼포먼스가 나오지 않았다.
이대로는 안되겠다 생각했다. 문제를 다시 읽어서 교수님이 원하는게 뭔가 생각을 했다.

왜 하필이면 두번만했을까, 왜 더블 AES도 아니고 다른 암호화를 썼을까
처음에 접근했어야 하는 문제를 개삽질 한번하고 다시 생각해보게 되었다.

그 과정에 Double DES를 생각하게 되었다.
Double DES 에 대한건 영상을 하나 첨부한다.

요점만 말하자면, 첫번째 암호화 한 결과와 암호화된 값을 복호화함으로서 생기는 중간값이 매치가 되야 정상 프로세스가 진행되게 된다. 즉, 중간값을 알아내기 위한 MITM(Meet in the middle) 공격이 가능해진다. (기존의 MITM이랑은 좀 다르다)

따라서, AES는 decrpyt 하고 DES는 encrpyt 하면 반드시 매칭되는 중간 값이 나오게 된다.
속도도 O(nx2)가 맥시멈으로서 제곱과는 비교가 되지 않는건 물론이고...
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os, sys, time, base64, string, urllib
from Crypto.Cipher import AES, DES
from Crypto.Hash import MD5
 
# author information
__author__ = "Song Myeong-Uk"
__email__ = "mwsong@imtl.skku.ac.kr"
__date__ = "2015.05.27"
 
# constants
DATA_FILE_URL = "http://www.ece.ubc.ca/~hyoung/passwords.txt"
DATA_FILE_NAME = "passwords.txt"
INPUT_FILE_NAME = "PlaintextCiphertext.txt"
OUTPUT_FILE_NAME = "keys.txt"
 
# global variables
md5_keys = {}
target_plaintext = ""
encrypted_plaintext = ""
DES_keys = {}
AES_keys = {}
 
# pad value with given block size: 16
def pad(x):
  BLOCK_SIZE = 16
  if len(x) == BLOCK_SIZE: return x
  c = (len(x) // BLOCK_SIZE) + 1
  c *= BLOCK_SIZE
  return "{:\x00<{l}}".format(x, l=c)
 
# returns value of md5 for given value
def hashMD5 (v):
  m = MD5.new(v)
  return m.digest()
 
# download base md5 hash password list from url
def download_data():
  global DATA_FILE_URL, DATA_FILE_NAME
  print "[-] {} not found! downloading now...".format(DATA_FILE_NAME)
  urllib.urlretrieve(DATA_FILE_URL, DATA_FILE_NAME)
  print "[-] {} download complete".format(DATA_FILE_NAME)
 
# calculate run time
def print_time(t):
  diff = time.time() - t
  print "[*] total run time: {:.4f} seconds".format(diff)
 
# load data from input file
def save_input_data():
  global target_plaintext, encrypted_plaintext
  global INPUT_FILE_NAME
  with open(INPUT_FILE_NAME, 'r') as f:
    data = [ l.strip() for l in f.readlines() ]
    target_plaintext = data[0]
    encrypted_plaintext = data[1]
 
# load md5 data into dictionary
def md5_to_memory():
  global md5_keys
  global DATA_FILE_NAME
  with open(DATA_FILE_NAME, 'r') as f:
    data = [ l.strip() for l in f.readlines() ]
    for line in data:
      line = line.split(' ')
      md5_keys[line[0]] = line[1]
 
# encrypts plaintext with DES using different keys
def plaintext_to_des():
  global md5_keys, DES_keys
  for k in md5_keys:
    KEY = DES_encrypt(hashMD5(md5_keys[k])[:8]).encode('hex')[:64]
    DES_keys[KEY] = md5_keys[k]
 
# decrypts ciphertext with AES128 using different keys
def enc_to_decrypt():
   global encrypted_plaintext, md5_keys, AES_keys, DES_keys
   for k in md5_keys:
    KEY = AES_decrypt(encrypted_plaintext, hashMD5(md5_keys[k])).encode('hex')[:64]
    AES_keys[KEY] = md5_keys[k]
    try:
      if DES_keys[KEY]:
        print "[!] solution key found"
        print "[-] key1: {}".format(DES_keys[KEY])
        print "[-] key2: {}".format(AES_keys[KEY])
        write_key(DES_keys[KEY], AES_keys[KEY])
      else:
        print "[!] solution key not found"
    except Exception, err:
      pass
 
# writes found key to file
def write_key(k1, k2):
  global OUTPUT_FILE_NAME
  print "[+] writing key file...",
  with open(OUTPUT_FILE_NAME, 'wb') as f:
    f.write(k1 + '\n' + k2)
  print "done"
 
# DES-ECB encrypt with given key
def DES_encrypt(key):
  global target_plaintext
  return DES.new(key, DES.MODE_ECB).encrypt(pad(target_plaintext))
 
# AES-CBC decrypt text with given key
def AES_decrypt(text, key):
  return AES.new(key, AES.MODE_CBC, IV='\x00'*16).decrypt(base64.b64decode(text))
 
# main execution block
def main():
  print "[+] starting {}...".format(os.path.basename(__file__))
  # time function execution
  start_time = time.time()
  # check if base data file exists
  txt_exists = os.path.isfile(DATA_FILE_NAME)
  print "[+] checking password data: {}".format(DATA_FILE_NAME)
  if not txt_exists:
    download_data()
  # check if input file exists
  cipher_exists = os.path.isfile(INPUT_FILE_NAME)
  print "[+] checking input file: {}".format(INPUT_FILE_NAME)
  if not cipher_exists:
    print "[!] {} not found! terminating...".format(INPUT_FILE_NAME)
    print_time(start_time)
    sys.exit()
  # load input data
  print "[+] loading input file data..."
  save_input_data()
  # load md5 to memory
  print "[+] preparing md5 dictionary..."
  md5_to_memory()
  # DES encrypt
  print "[+] processing with DES... [1/2]"
  plaintext_to_des()
  # AES decrypt
  print "[+] processing with AES... [2/2]"
  enc_to_decrypt()
  # time execution
  print_time(start_time)
 
if __name__ == "__main__":
  main()


7초만에 프로세스는 끝난게 된다.
여러 다른 어프로치를 했봤으나, 실질적인 소득은 위 두가지 코드가 전부이고, 따로 레퍼런스랄것도 이번에 없다.
Meet-in-the-Middle 공격을 잘 이해만 한다면 쉽게 구현할 수 있다.

최근에는 Single DES 및 Double DES는 아예 쓰이지도 않고, 중간자 공격에 대비해 최소 Triple DES를 사용하고,
이마저도 AES가 표준이 됨으로서 DES가 많이 사용되고 있지는 않지만, 암호화를 하려는 사람들은 이런 Known Ciphertext Attack 을 항상 염두에 둬야할것이다.






Reference
https://www.youtube.com/watch?v=vROZGQ9XLe8
2015/05/28 23:42 2015/05/28 23:42
Assignment #1

Requirements
1. At least 8 characters long
2. Contains both upper and lower-case letters
3. Contains one or more numerical digits and special characters.
4. Must be written in C/C++/Java/Python
5. Time limit for key/100 seconds
6. No salt keys. -> http://www.xorbin.com/tools/sha256-hash-calculator
7. All Program is smaller than 30MB



블로그 글을 너무 대충 작성해서 다시 쓰기로 했다. 구글에 검색하니 나오는게 신기하기도 했고..

먼저 이 과제의 목적은, 일반적으로 알려진 비밀번호를 사용하면 안된다는 것이다.
대부분의 비밀번호로 사용되는 hash 값들은 salt를 이용하여 이중 비밀번호를 진행한다.
따라서 one way 암호화를 진행하는데, 이 과정을 거치지 않을 경우에 비밀번호가 저장된 데이터베이스가 털렸을시 비밀번호 guessing이 가능하다는것을 알려주고자 하는 과정이다

요즘은 비밀번호 자릿수를 무조건 8자리이상에, 특문 하나를 넣어야 된다지만,
예전엔 그렇지 않았다는것을 누구나 안다. 6자리이상에 숫자 안넣어도, 아니, 숫자만 넣어도 비밀번호를 넣을 수 있었다.
이런 취약점들덕에 비밀번호 제도가 바뀌게 되었는데... 사설이 너무 길었고

어쨌든 이 과제에서는 SHA256으로 암호화된 비밀번호를 역으로 알아내야 하는것인데, 공격법은 세가지 정도로 나뉜다.

1. 역상 공격(preimage Attack)
  Secure한 SHA256상대로 할 수 없다. 할 수 있을진 모르겠는데 일단 내 실력으론 어림도 없는건 확실했다.

2. 무차별 대입 공격(Brute force Attack)
  SHA256에 할당된 8자리의 레인보우 테이블만 4테라바이트라고 한다....

3. 사전 공격(Dictionary Attack)
  결국 세가지 방법중에 미리 정의한 잘 알려진 단어와 유출된 사용자들이 자주 쓰던 비밀번호를 사용해서 사전을 만들고, 그를 SHA256 hash로 다시 정의하여 찾는 방법이 합당해보였다


우선 Password Dictionary라고 검색하면 어렵지 않게 정의된 사전들을 찾을 수 있다.
처음에 접근해보길, 모든 단어를 커버할 수 없으니 8자리만을 선택하자. 그런데 대소문자가 반드시 필요되시되고 특문 하나 숫자 하나도 들어가니까, 임의의 글자를 만들어 넣자라고 생각했으나, 프로그램 크기가 30메가 제한이라는 소식을 접하게 되었다.

처음엔 6자리 단어에 임의로 특문도 넣고 그랬는데 그랬더니 사전의 크기가 너무 커지게 되어서 비효율적이게 되었다.
그리고 만들어놓고 보니 Pass1[#2 뭐 이런꼴인데 사실 이렇게 비번 쓰는 사람도 없을 것 같았다..
그래서 딕셔너리를 다음과 같은 조건으로 거르고 해싱을 해보았다.

조건은,
1. 8~20자리의 글자
2. 숫자가 포함되어 있을것
3. 대문자가 포함되어 있을 것
i = 1
with open("file.txt") as f:
 for line in f:
     raw_pw = line.split(":")[0].replace('\r\n','').replace('\n','')
     if len(raw_pw) < 20 and len(raw_pw) >= 8:
          if re.search("[\!\-\~]",raw_pw) > -1:
               if re.search("[0-9]",raw_pw) > -1: 
                  if re.search("[A-Z]",raw_pw) > -1: 
                     hash_text = hashlib.sha256(raw_pw).hexdigest()
                     fp = open("aa.out","a")
                     fp.write(str(i)+":"+raw_pw+":"+hash_text+"\r\n")
                     fp.close()
                     i += 1

더 깔끔하게 코딩할 수 있을것 같은데 일단 이런식으로 해봤다..
이렇게 하면 백몇십메가나 되던 사전의 크기가 엄청나게 압축된다.
실제로 백만줄이 넘어가던 사전이 1메가도 안된다 지금은..
크기는 30메가 제한인데 제출과 현재 블로그 글을 쓰면서도 불안하나 그냥 하늘에 맡기기로 했다..
그 결과는 이곳(Github)에 있다.

나는 사실 여기까지 만들면서 사전만 잘 구성하면 될거라고 생각 했다. 사실 그게 핵심이고..
하지만 문제가 있었는데, 제출당일날 속도 제한이 있다는것을 깨달았다.
테스트로 돌려본 코드는 I/O를 이용해서 1개 키를 매칭해서 찾고 매칭해서 찾고 하는것이었다.
이건 망했으니 github엔 올리지 않고 여기에서 다룬다..
 import timeit
start = timeit.default_timer()

i = 1
with open("hashedPasswords.txt") as target:
      for line in target:
          user = line.split(":")[1].replace("\r\n","")
          target_word = line.split(":")[2].replace("\r\n","")
          with open("out_a.out") as dic:
              for dic_word in dic:
                  if target_word == dic_word.split("|")[1].replace("\r\n",""):
                      findit = dic_word.replace("\r\n","")
                      fp = open("Passwords.txt","a")
                      fp.write(str(i)+":"+user+":"+findit.split("|")[0]+"\r\n")
                      fp.close()
                      i+=1
                      break
                  
stop = timeit.default_timer()
print "Program Run Time : " + str(stop - start)


굳이 이름을 붙여보자면, Find word in large text file using python... zzz...
기가 막히지만, 말그대로다. 파일 포인터를 두개 열고 일대일로 나올때까지 매칭된다.
아마 사전의 크기가 크고 시도되는 키가 많다면 밤새도록 돌려야될지도 모른다.
내가 쓰려고 짰던건 아니고... 뭐 그렇다.

나는 이거랑 다르게 쓰레드를 이용해보기로 했다.
Python은 싱글코어만 사용하기때문에 쓰레드가 좋지 않아 프로세스를 늘리는 방식으로 사용해야 된다고 본적이 있다.
(참고: 파이썬으로 클라우드 하고 싶어요)

그래서 애초에 프로세스를 한개 더 늘려서 만들어보기로 했다.
사실 syncronized variable을 어떻게 만들어야될지 감도 안와서 추잡하게 다 쓰고 다시 읽어서 넘버링하고, 임시파일을 지우는 꽁수를 적어놨는데, 나는 코드의 신에게 분명히 맞아죽을것이다...
어쨌든 시도 자체는 훌륭했다고 생각한다. 프로세스를 물려서 뭔가 처릴 해보려고 했으니....... 남는건 있었겠지.
이렇게 돌린 코드는... 600초가 소모됐다 -_-
#!/usr/bin/env python

# Author     : Myeong-Uk (mwsong@imtl.skku.ac.kr)
# Date       : 2015. 03. 30
# Desc       : Python program to dictionary attack SHA256
# Dependency : out_c.out (Password dictionary), hashedPasswords.txt(input)
# Command    : <python find.py>
# OUTPUT     : Passwords.txt

import timeit,os
from multiprocessing import Process,Lock
  
  
start_time = timeit.default_timer() #when program start

def do_work(type):
    hash_count = 0
    with open('aa.out') as target:
         for line in target:
            findit = ""
            if type == "1" and hash_count%2 == 0:
                findit = search_word(line)
            elif type== "2" and hash_count%2 > 0:
                findit = search_word(line)
            hash_count +=1

            if findit != "" and findit != None:
                 user = findit.split("|")[0]
                 crackedPassword = findit.split("|")[1]

                 fp = open("Passwords_tmp.txt","a")
                 fp.write(user+":"+crackedPassword+"\r\n")
                 fp.close()

  
def search_word(word):
    user = str(word.split(":")[1].replace("\r\n",""))
    target_hash = word.split(":")[2].replace("\r\n","")

    with open("out_c.out") as dic:
        for dic_line in dic:

            dictionary_hash = dic_line.split("|")[1].replace("\r\n","")
            dictionary_word = dic_line.split("|")[0].replace("\r\n","")

            if target_hash == dictionary_hash:
                 return user + "|" + dictionary_word
                 break
 
def numbering():
    i = 0
    with open("Passwords_tmp.txt") as dic:
        for find_word in dic:    
             fp = open("Passwords.txt","a")
             fp.write(str(i)+":"+find_word)
             fp.close()
             i += 1
    print str(i) + " Password(s) found."

 
     
if __name__ == '__main__':

    pr1 = Process(target=do_work,args=(str(1)))
    pr2 = Process(target=do_work,args=(str(2)))
      
    pr1.start()
    pr2.start()
      
    pr1.join()
    pr2.join()


    stop_time = timeit.default_timer() #when program end

    print "Dictionary matching done"

    print "Numbering.."
    numbering()
    os.remove("Passwords_tmp.txt")
    print "Done!!"

    print "Program Run Time : " + str(stop_time - start_time)


사실 이걸 다 짜고 낼려는데 그제서야 시간제한이 있다는걸 알게 됐다
이대로 내면 100초에 끊을 경우 넘버링으로 다시 쓰는것도 안되기때문에 실제로 다 찾더라도 0이 될께 뻔했다
고로 이 코드는 못쓰는 코드라고 생각하고 다시 생각을 하기 시작했다.

다시 생각을 해보니까, 이런 생각이 들었다. 얘네를 왜 굳이 I/O를 이용해야 하는가?에 대한..
애초에 작은 사전을 만들었으니까, 모두 string 화 해서 사용하면 안될까? 생각이 들었다.
이때 stack overflow에서 찾은 이 글에서 영감을 많이 받았다.
요는, 파이썬은 루프는 느리다. 램에 다 올려서 string으로 찾아라. 그건 굉장히 빠르다.
그래서 덤프를 뜨고 메모리에서 비교를 해보기로 했다.
import timeit,re
from multiprocessing import Process
  
  
start_time = timeit.default_timer() #when program start

pass_dump = ""
hash_dump = ""


def init():
    global pass_dump, hash_dump
    f = open("out_c.out","r")
    pass_dump = f.read()

    f = open("aa.out")
    hash_dump = f.read()



def do_work():
   lines = hash_dump.split("\r\n")
   for i in lines:
    search_word(i.split(":")[2])
  
def search_word(word):
    match = re.search(r'.*'+word,pass_dump)
    if match:
        print match.group(0)
     
if __name__ == '__main__':

    init()

    do_work()

    stop_time = timeit.default_timer() #when program end

    print "Dictionary matching done"

    print "Numbering.."
    #numbering()
    #os.remove("Passwords_tmp.txt")
    print "Done!!"

    print "Program Run Time : " + str(stop_time - start_time)


요점은 한번에 싹 읽고, 정규식을 이용해 해당 라인을 다 가져와서 처리해보겠다고 한것이다.
정규식쓰면 당연히 빠르겠거니 하고 생각했는데, 어찌됐던 이전보단 빨라보이긴했는데 아니 끝이 안난다.
이 코드 검증은 안해서 잘 모르겠지만 그냥 망했다는 삘이 왔다. 너무 느렸다..

그래서 먼저 한 후배한테 물어보니까 그냥 딕셔너리 기본 옵션으로 구현했다고 한다.
무슨차이가 있을까.. 정규식보다 더 빠를 수 있을까? 속는 심정으로 코드를 짰다.
import timeit

start_time = timeit.default_timer() #when program start

pass_dump = {}
hash_dump = {}
count = 0

def init():
    global pass_dump, hash_dump
    with open("dictionary.out","r") as dic:
       for a in dic:
        key = a.split("|")[1].replace("\r\n","").replace("\n","") #hash
        value = a.split("|")[0] #original text
        pass_dump[key] = value

    with open("hashedPasswords.out","r") as dic:
       for a in dic:
        key = str(a.split(":")[2].replace("\r\n","").replace("\n","")) #hash
        value = a.split(":")[1] #user
        hash_dump[key] = value

    print "Init finished.."

def do_work():
    global hash_dump,pass_dump,count

    for a in hash_dump:
        try:
            if pass_dump[a]:
                count += 1
                fp = open("Passwords.txt","a")
                fp.write(str(count)+":"+hash_dump[a]+":"+pass_dump[a]+"\r\n")
                fp.close()
        except KeyError:
            pass
  
def search_word(word):
    match = re.search(r'.*'+word,pass_dump)
    if match:
        print match.group(0)
     
if __name__ == '__main__':

    init()
    do_work()

    stop_time = timeit.default_timer() #when program end

    print "Done!!"
    print str(count) + " Password(s) found. Recorded at Passwords.txt"
    print "Program Run Time : " + str(stop_time - start_time)


겉치레 이런거 없이, 이 코드는 그냥 엔터치면 검색이 끝났다...
배열처럼 사용하는 딕셔너리의 키값에 해쉬를 넣고 반환되는 값이 있나 없나 비교해보는것으로 모든 면에서 우수한것을 확인했다.
원리는 잘 모르겠다. 도대체 나는 동기화 이딴건 왜 찾아보고 있었나 이런 의문이 그냥 들었을뿐....
그냥 파이썬이 딕셔너리 키를 매칭해 찾는 속도가 개빠르구나.. 생각만 들었다. 혹시 구성할일 있으면 딕셔너리가 짱이니 잘쓰도록..

어쨌든, 쉽게 접근하면 쉽게 끝낼 수 있었던 과제였는데 너무 꼬아서 생각하는 바람에,
시간은 시간대로 잡아먹고 결과는... 안나왔지만 뻔할거라 생각한다만..

그래도 간만에 재밌는 과제였다 생각한다. 이런 짱구를 굴리는 과제가 재밌고 좋다.
이 개삽질이 누군가에게 도움되길 바라며 github 링크를 조심스럽게 링킹한다..



Reference
https://sites.google.com/site/reusablesec/Home/password-cracking-tools/probablistic_cracker
http://ko.wikipedia.org/wiki/%EC%97%AD%EC%83%81_%EA%B3%B5%EA%B2%A9
http://www.laurentluce.com/posts/python-and-cryptography-with-pycrypto/
http://www.xorbin.com/tools/sha256-hash-calculator
https://wiki.skullsecurity.org/Passwords
http://www.thegeekstuff.com/2014/07/advanced-python-regex/
http://stackoverflow.com/questions/8023306/get-key-by-value-in-dictionary
http://stackoverflow.com/questions/3449384/fastest-text-search-method-in-a-large-text-file

2015/03/26 11:50 2015/03/26 11:50

2014년

Gossip 2014/12/31 04:11
다시 돌아오지 않을 이시간. 2014년의 12월 31일.
네이버 블로그에 글을 쓸 수도 있겠다만 써놔봐야 찾기도 힘들 것 같고 그냥 여기 적어볼까한다.
회고와 다짐을 같이..

직장

2014년 4월에, 직장을 그만 두었다.
병역특례를 기다리고 있다가 법이 바뀌어서인데, 2013년 12월 9일에 발표난 것 치고 4달이나 더 다닌것 같다.
2013년~ 2014년 회사다니면서 작업한 홈페이지로는..

2013. 04?~ surem int'l intranet (내부 홈페이지로 따로 접근할 수 없음)
2013. 09 ~ 10 surem.net (대외 홈페이지로서 특별한 기능은 없다)
2013. 10 ~ 12 discountyourcost.com (이 프로젝트는 중지되었는데 아직 있다;; 친구 스타트업?을 도와준다고 간단히 만든 홈)
2014. 02 ~ 03 lute.fantazm.net (기존 싴갤러스에서 새갤러스로 모든 기능을 이관하고 디자인을 다시했다)
2014. 04 ~ 05 m.surem.com 기존 surem.com 의 모바일 버전으로 jquery-mobile을 사용했다. 풀로 쓰진않았고 맛만..
2014. 05.       harp,mand,wolf

여러 홈페이지를 만든것 같다 꽤.. 생각나는건 일단 저 정도인것 같다.
2014년에만 한건 아니지만, 회사가 classic ASP를 사용해서 이에 대한 코딩도 좀했고(surem.net 이 기존 asp로 짜여진 홈페이지여서, 포팅하기 위해 어떻게 하다보니 알게 되었다), ASP를 더 이상 사용하지 않는다고 해서 JSP를 회사에 도입하기도 했다.

surem.net은 혼자 작업해서 따로 툴을 쓸 필욘 없었지만,
m.surem.com을 작업할때는 정말 제대로 협업을 해야했기때문에 이때 처음 SVN과 Jenkins를 사용해서 형상관리와 배포를 했다.
(회사내에서 협업이라는 개념이 없어서.... 개발자는 각자의 파트만 혼자 개발하기때문에 같이 코딩할일이 잘 없어서 그런것 같다.. 정작 내 프로젝트도 버저닝을 안하고 있다.. 안습..)
또한, 회사 보안 문제로 mod_security와 webknight라는 죽어도 볼일 없을 것 같은 방화벽들을 체험하기도 했다.

그리고 회사에서 사용되는 PHP 모듈도 어느정도 다시 확고히 작성했고 새 기능도 추가했고..

coryjin이라는 넘사벽을 경험하면서 node.js 의 맛을 보게 되었고, 나에게 있어서 공부할 계기와 실제로 공부를 꽤 많이 한 시절이었다.
회사가 솔직히 어렵진 않고 학교 놀러가는 기분이어서 가볍게 다닌것 같다. 일이 좋았다는 건 아니고, 업무 처리 프로세스 같은건 좀 개선되었었으면.... 하는 생각을 많이 했다. 나에게 있어선 좋은 추억이 된 회사로 남았다. 끝마무리를 이렇게 깔끔하고 좋게 하는 사람이 잘 없는데....ㅋㅋ


방학

생각해보면 3학년 방학때부터 따로 방학이란게 없었다.
정확히 말하자면 집에 잘 없었다. 기사였나 산업기사 시험 날짜를 놓쳐서 김재환네 집에 도망가서 일주일 살기도했고(2013년 얘긴아니다), 3학년 겨울방학엔 대전에서, 4학년 여름방학은 현근집에서 몰래 숨어살았고, 겨울방학 시작하기도 전에 취직을 해서 나와 살았다.

직장을 그만두고 나서, 대학원을 준비했는데, 사실 도피의 의미가 좀 많이 컸으나 안되면 말고라고 생각도 많이 했다.
성균관대학교, 시립대학교 대학원을 썼고 전부 다 붙었다.  사실 연세대나 고려대에 쓰고 싶었는데 직장을 4월에 그만두고 알아봤었는데 연,고대는 3월에 원서를 쓴단다... 그래서 5월에 두 학교 면접을 보고 집에 내려갔었다.

집에서 7 8월까지의 시간을 보냈는데 솔직히 말하자면 정말 잉여롭게 보낸것 같다.
몸과 마음이 그렇게 늘어지기도 쉽지가 않은데, 어쨌든 알바를 구해서 알바도 다니고 그랬고, 당연히 군대가야되는 줄 알고 있던 어머니와 아빠에게 대학원 가겠다고 폭탄 선언을 한 기간이다.

이때 별로 특별한 기억은 없고, 나한테 집이나 좀 물려주셨으면 하는 바람을 가지게 되....긴했다.

기술적으로는 이때 mysql replication을 안정화 시켰다.
시작은 슈어엠때 하긴했는데, db 튜닝같은건 이 기간내에 잉여라서 많이했고, redis도 본격적으로 이 기간에 도입해서 사용했다.


대학원

그리고 9월부터 학교에 다니기 시작했고, 정신없는 석달을 보내고 지금이다.
지금은 보안을 공부한다는데.... 솔직히 연구실을 너무 대충알아봤음에도 제대로 된 연구실에 온것 같아 좋다.
뭣보다 사람들이 그렇게 악한 사람들이 없어서.. 그리고 직장-> 학교로 다시 오니 일을 대강대강하는게 눈에 보이기도한다
그래도 회사가 더 까다롭긴한것 같다. 학교는 그래도 학생의 신분이라서....

TCP/IP 및 다른 보안을 메이저로 하여 공부하고 있고, 웹 해킹쪽으로 파고 있는데 솔직히 웹해킹은 인젝션외엔 너무 뻔해서 (아직 선무당일수도있다), 사례 같은걸 보면서 공부하고 있고, python을 익히고 있다.
또 bro라는 ids를 좀 심도있게 파고있고...... 뭐 그렇다. 대학원 와서는 특별히 언어적으로 뭐 익히고 하진 못한 것 같다.
그나마 파이썬을 사용시작하게되고 다뤄보고 있다는게 소득이라면 소득..?

나는 보안도 좋지만 보안을 베이스로 하는 개발자가 되고자 한다.
바쁘다는 핑계가 아니라 정말 너무 바빠서 아직 손을 못대고, 2014년을 보내주려 한다.

2015

논문

논문은 두세편 정도 쓰려고 한다. 그 이상은 쓸 주제가 있을진 잘 모르겠는데, 아무래도 웹 관련해서 주제선정하고 쓰는데 많은 시간을 보내게 될 것 같은 해이다. 2014년 한학기 동안 논문을 안써서.... 논문에는 많은 시간을 투자해야될 것 같다. 괜찮다. 1년은 기니까.
주제는 웹으로 꾸준히 밀 생각인데, 글쎄 웹 관련 논문부터 많이 읽어봐야겠지
도서는 딱 오십권을 읽는걸 목표로 하자.

개발

역시 개발이 주제에서 빠질 수 없는데, 조금 장황한 계획이 있다.
웹은 계속해서 할 생각이고, 생각나는데로 적어보자면

python, ruby, scala
node.js + framework - meteor나 express 등
PHP - framework 체험도 체험이고, 가을에 나올 7.0도 개발해보고 싶다. 또한, 싴갤러스 클래스화를 진행하고..
c# - c#은 정말 한다한다 하는데 하질 못하는 그런녀석이다. 꼭 해보고 싶다.
java - android 개발자 등록하고, 푸시앱을 왕창 만들어보고 싶다. 다 개인용도로 쓰는거지....

그리고 git와 github을 이용해서 버저닝을 할 계획이고, 전체적인 언어의 깊이를 주고 싶은 한해가 될 것 같다.
너무 많은 계획이 아닌가 할 수 있겠지만 어느정도는 다 해본 녀석들이라... 새로운 도전은 없어뵌다.

파이썬 루비 스칼라는 마스터는 못해도 개념은 반드시 알고 넘어갈 계획이고 python같은경우는 flask나 django를 이용해서 서비스 하나를 만들어보는게 목표다. 크던 작던 일단 만들어야 뭘 할 것 같다.

운동

그리고 운동을 할려고 한다. 반 강제로 시작하게 될 것 같지만, 건강이 적신호인건 내가 누구보다 잘 알고 있다
몸뚱아리도.... 좀 깔끔하게 살기 위해서라도 운동을 해서 움직이는 습관을 들여야할 것 같다.
학교 헬스장을 이용하면 좋을 것 같고, 구체적인 계획을 세워서 강제로라도 시간 맞춰 운동해볼 계획이다. 한시간만이라도..

좀 장황한 회고와 앞으로의 계획을, 2014년이 끝나기 1시간 30분전에 마무리하면서,
새로이 다짐을 한번 해본다. 힘든 한해 잘 이겨낸 나에게 용기를 주며, 이 글을 읽는 모두가 행복하길 바란다!
2014/12/31 04:11 2014/12/31 04:11

1 ... 23 24 25 26 27 28