<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="http://iam.namjun.kim/atom.xml" rel="self" type="application/atom+xml" /><link href="http://iam.namjun.kim/" rel="alternate" type="text/html" /><updated>2023-04-04T12:11:48+00:00</updated><id>http://iam.namjun.kim/atom.xml</id><title type="html">austinkim’s blog</title><subtitle>Elegant theme for Jekyll.</subtitle><author><name>Namjun</name></author><entry><title type="html">중단 없는 서비스 제공을 위한 삽질기</title><link href="http://iam.namjun.kim/devops/2023/04/04/pathway-towards-to-the-zero-downtime-service/" rel="alternate" type="text/html" title="중단 없는 서비스 제공을 위한 삽질기" /><published>2023-04-04T00:00:00+00:00</published><updated>2023-04-04T00:00:00+00:00</updated><id>http://iam.namjun.kim/devops/2023/04/04/pathway-towards-to-the-zero-downtime-service</id><content type="html" xml:base="http://iam.namjun.kim/devops/2023/04/04/pathway-towards-to-the-zero-downtime-service/">&lt;h1 id=&quot;중단-없는-서비스-제공을-위한-삽질기&quot;&gt;중단 없는 서비스 제공을 위한 삽질기&lt;/h1&gt;

&lt;p&gt;안녕하세요. 오랜만에 인사 드립니다.&lt;/p&gt;

&lt;p&gt;회사 일로 바빠서 글을 많이 쓰진 못했는데 오늘 제 서버 중 하나에 장애가 발생 (정확히는 내부 사정으로 인해 외부망 연결이 되지 않는 상황) 하여 내부 공유 차 글을 써보려고 합니다.&lt;/p&gt;

&lt;p&gt;내일 일찍 출근해야 되서 사진은 없고, 대부분 글로 채우는 점 양해 부탁 드립니다.&lt;/p&gt;

&lt;h2 id=&quot;서막&quot;&gt;서막&lt;/h2&gt;
&lt;p&gt;회사에 있을 때 였습니다. 한참 일을 하고 있던 중에 제 휴대폰으로 문자가 한통 들어옵니다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[SYNOPTICON-ALERT] &amp;lt;서버명&amp;gt; NODE DOWN, ARK PROTOCOL ACTIVATED
REGION: ap-northeast-1a, SERVICES: &amp;lt;서비스명&amp;gt;, &amp;lt;서비스명&amp;gt;, &amp;lt;서비스명&amp;gt;

[SYNOPTICON-ALERT] ALL SERVICES ARE RECOVERED
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;?? 전혀 죽을 일 없을 것 같던 서버가 죽어서 ARK(방주) 프로토콜이 활성화 되었고, 현재는 정상 운영중이라는 이야기였습니다.&lt;/p&gt;

&lt;h3 id=&quot;왜-죽었는가&quot;&gt;왜 죽었는가?&lt;/h3&gt;

&lt;p&gt;그래서 이상해서 서버 상태를 휴대폰으로 확인해보니 진짜 접속이 안되는 현상이 발생했습니다??&lt;/p&gt;

&lt;p&gt;이유는 알고 보니 서버가 있는 곳 옆에서 공사를 하는데, 공사 과정에서 인터넷 망을 건드렸는지 외부망 단절?이 발생했다는 점?&lt;/p&gt;

&lt;p&gt;이로 인해 일부 서비스가 정상 작동하지 않았던 것으로 추정하고 있습니다. (아직 정확한 원인은 모릅니다)&lt;/p&gt;

&lt;p&gt;이런 장애를 다른 서버에서 탐지해서 상호 합의 하에(뒤에 설명 나옴) 새 AWS 리전에 구축 하였고, 서비스는 정상 작동 하였습니다.&lt;/p&gt;

&lt;h3 id=&quot;어떻게-복구하였는가&quot;&gt;어떻게 복구하였는가?&lt;/h3&gt;

&lt;p&gt;제가 작업한 모든 서비스는 모두 AWS CloudFormation으로 인프라 코드를 작성, 타 서버에서 장애 발생 시 바로 AWS 인프라에 운용 가능하도록 준비 해 두었습니다.&lt;/p&gt;

&lt;p&gt;그리고 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;synopticon&lt;/code&gt; 이라는 프로젝트를 통해 평소 운영중인 서버(서비스)에 대해 상호 감시할 수 있도록 체계를 구축 해 두었습니다.&lt;/p&gt;

&lt;p&gt;이를 통해 특정 서버가 정상 운영되지 않고 있음을 확인 하였고, 상호 교환된 정보에 따라 감시 노드에서 탈락시키고 정상 연결될 때 까지 상태 체크 이외에 전달 값을 무효화 하는 조치를 하였습니다.&lt;/p&gt;

&lt;p&gt;이후 제 AWS에 구축된 람다 함수를 호출하여 해당 서버에서 운영 중이던 서비스에 대해 CloudFormation 실행 명령을 수행 하였습니다.&lt;/p&gt;

&lt;p&gt;수행 이후 기존 도메인에 등록된 A Class IP 주소를 신규 서버 주소로 변경하는 작업까지 완료하여 정상 복구하였습니다.&lt;/p&gt;

&lt;p&gt;근데 이거 어디서 많이 들어 본 내용 아닌가요..?&lt;/p&gt;

&lt;h3 id=&quot;synopticon&quot;&gt;Synopticon?&lt;/h3&gt;
&lt;p&gt;Go 기반의 혼자 쓰는 (비)오픈소스 프로젝트.&lt;/p&gt;

&lt;p&gt;아이디어는 비트코인의 블록 검증에서 따와서 각 서버가 노드가 되어 해당 서버의 서비스 정보를 기반으로 블록을 생성, 검증하는 과정을 통해 “탈중앙화(?)” 된 서버 모니터링을 구축 하는 것이 목표인 프로젝트입니다.&lt;/p&gt;

&lt;p&gt;현재는 Agent가 서버 내에서 돌아가면서 타 서버와 지속적인 통신을 통해 서버 정보를 교환하고 블록(서버 내 운영 중인 서비스 정보와 우선순위)을 생성하여 저장합니다.&lt;/p&gt;

&lt;p&gt;각 서버가 에이전트를 통해 교환하는 블록의 예시 데이터는 다음과 같습니다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
    'nodes': {
        'XXX': [
            'X-SERVICE-1',
            'X-SERVICE-2',
            'X-SERVICE-3',
        ],
        'YYY': [
            'Y-SERVICE-1',
            'Y-SERVICE-2',
            'Y-SERVICE-3',
        ]
    },
    'event': {
        'node-down': {
            'service': 'aws-lambda',
            'attributes': [
                '&amp;lt;람다 함수 주소&amp;gt;, &amp;lt;또 다른 람다 함수 주소&amp;gt;, ...
            ]
        }
    },
    'on-recovery': {
        'wait': 60,
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;여기서 노드 내에 있는 리스트의 값을 해당 서버 내에 우선순위를 뜻합니다.&lt;/p&gt;

&lt;p&gt;즉, X-SERVICE-1은 X-SERVICE-2보다 먼저 살려야 하는 서비스임을 말합니다.&lt;/p&gt;

&lt;p&gt;이걸 만들고 나서 나름대로 서비스 별로 우선순위를 정해서 먼저 살려야 하는 서비스(대외 서비스) 부터 살렸습니다.&lt;/p&gt;

&lt;p&gt;그리고 event의 경우 내가 특정 이벤트가 발생한 경우 호출할 서비스와 그 attribute를 뜻합니다.&lt;/p&gt;

&lt;p&gt;여기서는 node-down 이벤트가 발생 시 aws-lambda 함수를 호출하는데 그 우선순위는 attributes 순서대로 따흡니다 (AWS도 죽을 수 있다는 점을 염두)&lt;/p&gt;

&lt;p&gt;마지막으로, on-recovery의 경우에는 서버가 다시 복구 될 경우 어떻게 할 것인지에 대해 명시 되어 있습니다.&lt;/p&gt;

&lt;p&gt;이 부분은 빡세게 구현할려고 한 것이 아니라서 (수동으로도 가능해서) ‘wait’ (대기하는 시간) 만 구현 했습니다. 이 코드에서는 60분 대기 후 연결 확인되면 복구 완료로 인식합니다.&lt;/p&gt;

&lt;p&gt;TMI지만, 과거 독일의 상호감시 체제인 슈타지로 할까 했지만 잘못했다가 큰일 날 것 같아 그냥 고대 이름을 가져왔습니다. (그게 편하다)&lt;/p&gt;

&lt;p&gt;여튼, 원래는 DR(재해복구) 에 관심이 많아서 만들기 시작했는데 이번에 잘 작동해서 기분이 매우 좋았습니다.&lt;/p&gt;

&lt;h3 id=&quot;말만-들어보면-이것-만으론-부족하지-않나요&quot;&gt;말만 들어보면 이것 만으론 부족하지 않나요?&lt;/h3&gt;
&lt;p&gt;YES. 그래서 저는 AWS를 적극적으로 이용햇습니다.&lt;/p&gt;

&lt;p&gt;미리 일본(도쿄, 오사카), 미국(오리건, 버지니아), 독일(프랑크푸르트) 에 서비스 이미지와 CloudFormation 코드, lambda 함수를 배포해 두었습니다.&lt;/p&gt;

&lt;p&gt;정확히는 서비스 코드 수정 뒤 마스터 머지 완료되면 github action을 이용하여 올리도록 작업 해 두었습니다.&lt;/p&gt;

&lt;p&gt;그리고 만약에 사고 발생 시 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;synopticon&lt;/code&gt; 에서 제공하는 event listener 기능을 이용하여 람다 함수를 다른 함수에서 바로 호출하게 됩니다.&lt;/p&gt;

&lt;p&gt;호출 뒤에는 람다에 정의된 대로 다 알아서 하는 것이라. 제가 신경 쓸 것은 람다에서 CloudFlare API를 이용하여 A Class 주소를 바꾸는 것 뿐?&lt;/p&gt;

&lt;h3 id=&quot;정상-복구-뒤에는&quot;&gt;정상 복구 뒤에는?&lt;/h3&gt;

&lt;p&gt;정상 복구 뒤에는 바로 AWS 인프라를 거두는 것이 아니라 일정 시간동안 다른 서버들이 정상 접근 여부를 확인 (이 정책에서는 60분) 한 뒤 기존 IP 주소로 변경합니다.&lt;/p&gt;

&lt;p&gt;기존 IP 주소 변경 뒤에는 기존에 사용했던 모든 인프라를 삭제하고 사용했던 로그만 S3에 저장합니다.&lt;/p&gt;

&lt;p&gt;끝..!&lt;/p&gt;

&lt;h3 id=&quot;비용은&quot;&gt;비용은?&lt;/h3&gt;

&lt;p&gt;EC2 c5.large 4시간 + EBS 128GB + EIP 등등 해서 300원 정도 들었습니다. 매우 싸죠?&lt;/p&gt;

&lt;p&gt;근데 SMS 발송 비용이 80원… ㅋㅋ 400원 정도로 무중단 서비스를 구현할 수 있었습니다.&lt;/p&gt;

&lt;p&gt;적은 비용으로 중단 없는 서비스를 구현할 수 있습니다.&lt;/p&gt;

&lt;p&gt;참고로 저 때 다행히도 해당 시간대에 스팟 인스턴스가 있어서 스팟 가격으로 이용했습니다.&lt;/p&gt;

&lt;h2 id=&quot;lesson-learned&quot;&gt;Lesson Learned&lt;/h2&gt;

&lt;p&gt;서버는 언제든 죽을 수 있다. 인프라는 언제든 죽을 수 있다. 항상 운영되는건 없다.&lt;/p&gt;

&lt;p&gt;이번에는 데이터베이스 서버가 죽지 않아서 크게 문제될 것이 없었습니다만 하지만 디비 서버가 죽었다면..? (주기적인 백업과 replica 운영중)&lt;/p&gt;

&lt;p&gt;둘 다 죽으면 어떻게 해야 하는가에 대해서도 고민해야 할 것 같습니다.&lt;/p&gt;

&lt;h3 id=&quot;추가&quot;&gt;추가&lt;/h3&gt;
&lt;p&gt;AWS SMS 비용은 아무리 생각해도 너무 비싼 것 같습니다.&lt;/p&gt;

&lt;p&gt;다른 서비스 다 죽었는데 내 서비스만 조금 있다가 바로 복구 되었다고..?&lt;/p&gt;</content><author><name>Namjun</name></author><category term="devops" /><category term="devops" /><summary type="html">중단 없는 서비스 제공을 위한 삽질기 안녕하세요. 오랜만에 인사 드립니다. 회사 일로 바빠서 글을 많이 쓰진 못했는데 오늘 제 서버 중 하나에 장애가 발생 (정확히는 내부 사정으로 인해 외부망 연결이 되지 않는 상황) 하여 내부 공유 차 글을 써보려고 합니다. 내일 일찍 출근해야 되서 사진은 없고, 대부분 글로 채우는 점 양해 부탁 드립니다. 서막 회사에 있을 때 였습니다. 한참 일을 하고 있던 중에 제 휴대폰으로 문자가 한통 들어옵니다. [SYNOPTICON-ALERT] &amp;lt;서버명&amp;gt; NODE DOWN, ARK PROTOCOL ACTIVATED REGION: ap-northeast-1a, SERVICES: &amp;lt;서비스명&amp;gt;, &amp;lt;서비스명&amp;gt;, &amp;lt;서비스명&amp;gt; [SYNOPTICON-ALERT] ALL SERVICES ARE RECOVERED ?? 전혀 죽을 일 없을 것 같던 서버가 죽어서 ARK(방주) 프로토콜이 활성화 되었고, 현재는 정상 운영중이라는 이야기였습니다. 왜 죽었는가? 그래서 이상해서 서버 상태를 휴대폰으로 확인해보니 진짜 접속이 안되는 현상이 발생했습니다?? 이유는 알고 보니 서버가 있는 곳 옆에서 공사를 하는데, 공사 과정에서 인터넷 망을 건드렸는지 외부망 단절?이 발생했다는 점? 이로 인해 일부 서비스가 정상 작동하지 않았던 것으로 추정하고 있습니다. (아직 정확한 원인은 모릅니다) 이런 장애를 다른 서버에서 탐지해서 상호 합의 하에(뒤에 설명 나옴) 새 AWS 리전에 구축 하였고, 서비스는 정상 작동 하였습니다. 어떻게 복구하였는가? 제가 작업한 모든 서비스는 모두 AWS CloudFormation으로 인프라 코드를 작성, 타 서버에서 장애 발생 시 바로 AWS 인프라에 운용 가능하도록 준비 해 두었습니다. 그리고 synopticon 이라는 프로젝트를 통해 평소 운영중인 서버(서비스)에 대해 상호 감시할 수 있도록 체계를 구축 해 두었습니다. 이를 통해 특정 서버가 정상 운영되지 않고 있음을 확인 하였고, 상호 교환된 정보에 따라 감시 노드에서 탈락시키고 정상 연결될 때 까지 상태 체크 이외에 전달 값을 무효화 하는 조치를 하였습니다. 이후 제 AWS에 구축된 람다 함수를 호출하여 해당 서버에서 운영 중이던 서비스에 대해 CloudFormation 실행 명령을 수행 하였습니다. 수행 이후 기존 도메인에 등록된 A Class IP 주소를 신규 서버 주소로 변경하는 작업까지 완료하여 정상 복구하였습니다. 근데 이거 어디서 많이 들어 본 내용 아닌가요..? Synopticon? Go 기반의 혼자 쓰는 (비)오픈소스 프로젝트. 아이디어는 비트코인의 블록 검증에서 따와서 각 서버가 노드가 되어 해당 서버의 서비스 정보를 기반으로 블록을 생성, 검증하는 과정을 통해 “탈중앙화(?)” 된 서버 모니터링을 구축 하는 것이 목표인 프로젝트입니다. 현재는 Agent가 서버 내에서 돌아가면서 타 서버와 지속적인 통신을 통해 서버 정보를 교환하고 블록(서버 내 운영 중인 서비스 정보와 우선순위)을 생성하여 저장합니다. 각 서버가 에이전트를 통해 교환하는 블록의 예시 데이터는 다음과 같습니다. { 'nodes': { 'XXX': [ 'X-SERVICE-1', 'X-SERVICE-2', 'X-SERVICE-3', ], 'YYY': [ 'Y-SERVICE-1', 'Y-SERVICE-2', 'Y-SERVICE-3', ] }, 'event': { 'node-down': { 'service': 'aws-lambda', 'attributes': [ '&amp;lt;람다 함수 주소&amp;gt;, &amp;lt;또 다른 람다 함수 주소&amp;gt;, ... ] } }, 'on-recovery': { 'wait': 60, } } 여기서 노드 내에 있는 리스트의 값을 해당 서버 내에 우선순위를 뜻합니다. 즉, X-SERVICE-1은 X-SERVICE-2보다 먼저 살려야 하는 서비스임을 말합니다. 이걸 만들고 나서 나름대로 서비스 별로 우선순위를 정해서 먼저 살려야 하는 서비스(대외 서비스) 부터 살렸습니다. 그리고 event의 경우 내가 특정 이벤트가 발생한 경우 호출할 서비스와 그 attribute를 뜻합니다. 여기서는 node-down 이벤트가 발생 시 aws-lambda 함수를 호출하는데 그 우선순위는 attributes 순서대로 따흡니다 (AWS도 죽을 수 있다는 점을 염두) 마지막으로, on-recovery의 경우에는 서버가 다시 복구 될 경우 어떻게 할 것인지에 대해 명시 되어 있습니다. 이 부분은 빡세게 구현할려고 한 것이 아니라서 (수동으로도 가능해서) ‘wait’ (대기하는 시간) 만 구현 했습니다. 이 코드에서는 60분 대기 후 연결 확인되면 복구 완료로 인식합니다. TMI지만, 과거 독일의 상호감시 체제인 슈타지로 할까 했지만 잘못했다가 큰일 날 것 같아 그냥 고대 이름을 가져왔습니다. (그게 편하다) 여튼, 원래는 DR(재해복구) 에 관심이 많아서 만들기 시작했는데 이번에 잘 작동해서 기분이 매우 좋았습니다. 말만 들어보면 이것 만으론 부족하지 않나요? YES. 그래서 저는 AWS를 적극적으로 이용햇습니다. 미리 일본(도쿄, 오사카), 미국(오리건, 버지니아), 독일(프랑크푸르트) 에 서비스 이미지와 CloudFormation 코드, lambda 함수를 배포해 두었습니다. 정확히는 서비스 코드 수정 뒤 마스터 머지 완료되면 github action을 이용하여 올리도록 작업 해 두었습니다. 그리고 만약에 사고 발생 시 synopticon 에서 제공하는 event listener 기능을 이용하여 람다 함수를 다른 함수에서 바로 호출하게 됩니다. 호출 뒤에는 람다에 정의된 대로 다 알아서 하는 것이라. 제가 신경 쓸 것은 람다에서 CloudFlare API를 이용하여 A Class 주소를 바꾸는 것 뿐? 정상 복구 뒤에는? 정상 복구 뒤에는 바로 AWS 인프라를 거두는 것이 아니라 일정 시간동안 다른 서버들이 정상 접근 여부를 확인 (이 정책에서는 60분) 한 뒤 기존 IP 주소로 변경합니다. 기존 IP 주소 변경 뒤에는 기존에 사용했던 모든 인프라를 삭제하고 사용했던 로그만 S3에 저장합니다. 끝..! 비용은? EC2 c5.large 4시간 + EBS 128GB + EIP 등등 해서 300원 정도 들었습니다. 매우 싸죠? 근데 SMS 발송 비용이 80원… ㅋㅋ 400원 정도로 무중단 서비스를 구현할 수 있었습니다. 적은 비용으로 중단 없는 서비스를 구현할 수 있습니다. 참고로 저 때 다행히도 해당 시간대에 스팟 인스턴스가 있어서 스팟 가격으로 이용했습니다. Lesson Learned 서버는 언제든 죽을 수 있다. 인프라는 언제든 죽을 수 있다. 항상 운영되는건 없다. 이번에는 데이터베이스 서버가 죽지 않아서 크게 문제될 것이 없었습니다만 하지만 디비 서버가 죽었다면..? (주기적인 백업과 replica 운영중) 둘 다 죽으면 어떻게 해야 하는가에 대해서도 고민해야 할 것 같습니다. 추가 AWS SMS 비용은 아무리 생각해도 너무 비싼 것 같습니다. 다른 서비스 다 죽었는데 내 서비스만 조금 있다가 바로 복구 되었다고..?</summary></entry><entry><title type="html">Review 2022</title><link href="http://iam.namjun.kim/retrospective/2022/12/31/2022-retrospective/" rel="alternate" type="text/html" title="Review 2022" /><published>2022-12-31T00:00:00+00:00</published><updated>2022-12-31T00:00:00+00:00</updated><id>http://iam.namjun.kim/retrospective/2022/12/31/2022-retrospective</id><content type="html" xml:base="http://iam.namjun.kim/retrospective/2022/12/31/2022-retrospective/">&lt;h1 id=&quot;review-2022&quot;&gt;Review 2022&lt;/h1&gt;

&lt;p&gt;이미 2023년이고, 이번에는 안쓸려고 했는데 정리해 두면 좋을 것 같아서 해보았습니다.&lt;/p&gt;

&lt;p&gt;작성 시점이 2022년 12월 31일이라고 감안해 주시고 읽어주시면 감사하겠습니다.&lt;/p&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;대학을 졸업했습니다. (2015년 3월 - 2022년 8월, 7년 5개월)&lt;/li&gt;
  &lt;li&gt;지금 있는 회사에서 1년 넘게 다녔습니다.&lt;/li&gt;
  &lt;li&gt;10월에 캐나다 여행을 다녀왔습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;회사&quot;&gt;회사&lt;/h2&gt;

&lt;p&gt;지금 있는 회사에 입사한지 만 1년이 넘었습니다.&lt;/p&gt;

&lt;p&gt;좋은 사람들과 재밌게 일 할 수 있다는 사실에 즐겁습니다. 제가 빌런이 되지 않도록 계속 공부하고 있습니다.&lt;/p&gt;

&lt;p&gt;요즘은 게임 개발을 공부하고 있습니다. 원래는 게임을 만들고 싶다는 생각은 없었는데, 배워보니 나름 재밌어서 계속 하고 있습니다.&lt;/p&gt;

&lt;p&gt;내년에는 (올해는) 더 도움이 되는 사람이 되도록 노력해야 겠다는 생각을 했습니다.&lt;/p&gt;

&lt;h2 id=&quot;학교&quot;&gt;학교&lt;/h2&gt;

&lt;p&gt;학교를 졸업했습니다. 20대의 70%를 차지하는 세종대학교를 졸업했습니다.&lt;/p&gt;

&lt;p&gt;졸업이 불투명할 때 다행히(?) 코로나 비대면을 만나 상대적으로 괜찮은 성적으로 졸업할 수 있게 되었습니다.&lt;/p&gt;

&lt;p&gt;졸업했다고 동아리에서 사준 키보드 받침대는 아직도 잘 쓰고 있습니다.&lt;/p&gt;

&lt;p&gt;암튼 졸업했습니다. 감사했습니다 다들.&lt;/p&gt;

&lt;h2 id=&quot;개인&quot;&gt;개인&lt;/h2&gt;

&lt;p&gt;올해는 정말 개인적으로 딱히 한게 별로 없었습니다. 블로그 글도 정기적으로 안올리고 그냥 쉴땐 쉬는 삶을 살아 보았습니다.&lt;/p&gt;

&lt;p&gt;작년처럼 PL 활동도 해볼까 생각해 보긴 했는데, 작년에 제가 딱히 도움이 되지 못한 것 같아 더 좋은 사람에게 가는 것이 맞다고 생각해서 지원하지 않았습니다.&lt;/p&gt;

&lt;h2 id=&quot;캐나다&quot;&gt;캐나다&lt;/h2&gt;

&lt;p&gt;항상 여행가는 팟과 함께 캐나다 여행을 다녀왔습니다. (퀘벡, 밴쿠버)&lt;/p&gt;

&lt;p&gt;캐나다.. 멀더라구요.. 그리고 너무 추워요..&lt;/p&gt;

&lt;p&gt;간 동안에 2022 사이버보안챌린지 본선 서류를 작성해야 해서 낮에는 여행하고 밤에는 열심히 서류를 또 썼습니다.&lt;/p&gt;

&lt;p&gt;결론) 사이버보안챌린지 4등&lt;/p&gt;

&lt;h2 id=&quot;마치면서&quot;&gt;마치면서&lt;/h2&gt;

&lt;p&gt;다들 새해 복 많이 받으시고, 내년에도 이루고 싶은 것들을 이루시기 바랍니다.&lt;/p&gt;

&lt;p&gt;감사합니다.&lt;/p&gt;

&lt;p&gt;김남준 드림.&lt;/p&gt;</content><author><name>Namjun</name></author><category term="retrospective" /><category term="retrospective" /><summary type="html">Review 2022</summary></entry><entry><title type="html">파이썬 오픈소스 라이브러리 변경사항 모니터링 시스템, 그 이후 두달간의 이야기</title><link href="http://iam.namjun.kim/security/python/development/2022/08/06/python-open-source-library-monitoring-system-after/" rel="alternate" type="text/html" title="파이썬 오픈소스 라이브러리 변경사항 모니터링 시스템, 그 이후 두달간의 이야기" /><published>2022-08-06T00:00:00+00:00</published><updated>2022-08-06T00:00:00+00:00</updated><id>http://iam.namjun.kim/security/python/development/2022/08/06/python-open-source-library-monitoring-system-after</id><content type="html" xml:base="http://iam.namjun.kim/security/python/development/2022/08/06/python-open-source-library-monitoring-system-after/">&lt;h1 id=&quot;파이썬-오픈소스-라이브러리-변경사항-모니터링-시스템-그-이후-두달의-이야기&quot;&gt;파이썬 오픈소스 라이브러리 변경사항 모니터링 시스템: 그 이후 두달의 이야기&lt;/h1&gt;

&lt;p&gt;파이썬 오픈소스 라이브러리 변경사항 모니터링 시스템을 운영한지 오늘로서 두달이 되었다.&lt;/p&gt;

&lt;p&gt;생각보다 오픈 후에 여러 일들이 있어서 정리 해 보고자 한다.&lt;/p&gt;

&lt;h2 id=&quot;운영&quot;&gt;운영&lt;/h2&gt;

&lt;p&gt;웹 서비스의 경우 오픈 후 스캔성 공격이 계속 들어와서 부하가 발생, 결국 외부 서비스를 닫아버렸다.&lt;/p&gt;

&lt;p&gt;그래도 수집기와 분석기는 정상적으로 돌고 있으니 걱정하지 말자. (아무도 걱정 안한다)&lt;/p&gt;

&lt;h2 id=&quot;바뀐-것&quot;&gt;바뀐 것&lt;/h2&gt;

&lt;p&gt;오픈 이후에 많은 부분을 바꿔서 나름의 고도화(유사) 작업을 진행했다.&lt;/p&gt;

&lt;h3 id=&quot;소스코드-분석&quot;&gt;소스코드 분석&lt;/h3&gt;

&lt;p&gt;처음에는 단순히 IP, Domain을 탐지하기 위한 방법으로 소스코드 전역 스캔을 정규표현식을 이용해 스캔했다.&lt;/p&gt;

&lt;p&gt;근데 이걸 실제로 쓰는 IP인지 아니면 주석으로 써둔 것인지 구분하기 힘들었다.&lt;/p&gt;

&lt;p&gt;이를 해결하기 위해 AST (Abstract Syntax Tree) 패키지를 이용해서 파이썬 코드를 정적 분석하고, 실제 할당된 변수의 값에 IP, Domain이 있는지에 대해 검사하도록 수정했다.&lt;/p&gt;

&lt;h3 id=&quot;부분-동적-분석-도입&quot;&gt;부분 동적 분석 도입&lt;/h3&gt;

&lt;p&gt;소스코드 분석에서 IP / Domain이 있는 경우 동적 분석 엔진에 보내 그 결과를 받을 수 있도록 했다.&lt;/p&gt;

&lt;p&gt;특별한 정보는 아니고 그냥 네트워크 통신, 파일 IO, 프로세스 정보, 그리고 리소스 사용량 정보 정도이다.&lt;/p&gt;

&lt;p&gt;처음에는 파이썬 라이브러리 전체를 스캔하고 모든 함수를 호출 해줘야 하나 고민했는데, 그냥 개발자가 작성한 테스트 코드를 구동하는 방식으로 구현했다.&lt;/p&gt;

&lt;p&gt;완벽하게 모든 코드를 스캔할 순 없지만 (code coverage라는게 있기 때문에) 그래도 나름 구현하면서 재밌던 경험들이나, Docker 내부를 2020년 이후로 뜯어보면서 변경된 사항들을 찾아 볼 수 있었다.&lt;/p&gt;

&lt;p&gt;그리고 네트워크 통신 정보를 수집하는 과정에서 이전에 개발한 &lt;a href=&quot;https://github.com/bunseokbot/Honey-V&quot;&gt;Honey-V&lt;/a&gt; 프로젝트를 많이 참고 하였다.&lt;/p&gt;

&lt;p&gt;단점: 개발자가 테스트 코드를 작성하지 않는 경우 검사가 안됨 ㅠㅠ, 그리고 공격 목적으로 작성된 라이브러리의 경우 (당연히) 테스트 코드가 없다. 그딴걸 개발할 시간에 공격을 하나 더 하는게 유효하기 때문에&lt;/p&gt;

&lt;h3 id=&quot;contributor-이메일-등록-도메인-정보-추가&quot;&gt;Contributor 이메일 등록 도메인 정보 추가&lt;/h3&gt;

&lt;p&gt;파이썬 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctx&lt;/code&gt; 라이브러리와 같이 contributor / maintainer의 이메일에 등록된 도메인이 만료되어 공격자한테 탈취되는 일이 없도록 whois 정보를 추가했다.&lt;/p&gt;

&lt;p&gt;이 정보를 이용해서 한달이 남지 않은 도메인에 대해 위험하다는 경고를 해 주고 싶었다. (사실 도메인의 경우 만료가 되도 바로 시장에 풀리는게 아니라 일정 기간을 두고 풀리기 때문에 실제로 이 공격이 먹힐 일은 없겠지만 그래도 사례가 있으니)&lt;/p&gt;

&lt;p&gt;실제로 만료일이 얼마 안되는 author(한달 기준, 대부분 도메인 관리 업체에서 한달 전에는 notice를 주긴 하므로) 가 생각보다 많진 않았지만 무려 오늘 기준 &lt;strong&gt;하루 남은 패키지&lt;/strong&gt;를 찾긴 했다.&lt;/p&gt;

&lt;p&gt;물론 내일 이후에 이 도메인이 바로 탈취되진 않겠지만.. 그래도 이런건 보고 있긴 해야된다.&lt;/p&gt;

&lt;h3 id=&quot;typosquatting-attack-scoring--추가&quot;&gt;Typosquatting Attack Scoring  추가&lt;/h3&gt;

&lt;p&gt;최근 늘어난 공격 방법 중 하나인 Typosquatting 공격에 대해 탐지할 수 있는 자체 scoring 공식을 만들었다.&lt;/p&gt;

&lt;p&gt;이 방법에 대해서는 아직 다 다듬어진 것은 아니여서 상세한 내용은 밝힐 수 없지만,&lt;/p&gt;

&lt;p&gt;110개의 Typosquatting 공격 의심 라이브러리를 탐지, PyPI 측에서 실제로 &lt;strong&gt;5개의 라이브러리를 차단 조치&lt;/strong&gt;했다. (중요: 내가 알려줘서 차단 한 것은 아니고.. 탐지 된 것 확인해서 퇴근하고 확인 해 보니 이미 차단 되었다 - 생각보다 조치가 매우 빠르다..)&lt;/p&gt;

&lt;p&gt;생각보다 재밌는 방법으로 scoring을 했는데 이를 실제로 구현하면서 오랜만에 재밌었다. :)&lt;/p&gt;

&lt;h2 id=&quot;유효한-데이터는-나왔는가&quot;&gt;유효한 데이터는 나왔는가?&lt;/h2&gt;

&lt;p&gt;수치 값으로 표현하면 다음과 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;파이썬 리포지토리에 업데이트 된 내역 140,269건 스캔&lt;/li&gt;
  &lt;li&gt;정적/동적 분석을 통해 수집된 악성 파일은 아직 없음&lt;/li&gt;
  &lt;li&gt;7,508건의 유효한 Contributor E-mail domain 수집&lt;/li&gt;
  &lt;li&gt;그 중 만료기한이 오늘 기준으로 무려 &lt;strong&gt;하루&lt;/strong&gt; 남은 패키지 1개 발견 (프로젝트가 관리가 되고 있음에도)&lt;/li&gt;
  &lt;li&gt;만료기일 수집 하다가 whois query 더 안되서 못하는 중&lt;/li&gt;
  &lt;li&gt;110건의 Typosquatting 의심 업데이트 탐지, 5건만 실제로 차단 (정책 때문에 단순히 이름이 비슷하면 안되는듯)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;앞으로-뭘-할껀가&quot;&gt;앞으로 뭘 할껀가&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Typosquatting 공격 탐지 방법에 대해 아이디어가 많은데, 그 아이디어 하나씩 구현해 볼 예정&lt;/li&gt;
  &lt;li&gt;부분 동적 분석하는데 사용한 기술 일부 나중에 기회 되면 공개&lt;/li&gt;
  &lt;li&gt;수집 / 탐지 엔진 성능 개선 (요즘 최적화 공부중이라 응용)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;졸업합니다&lt;/p&gt;</content><author><name>Namjun</name></author><category term="security" /><category term="python" /><category term="development" /><category term="security" /><summary type="html">파이썬 오픈소스 라이브러리 변경사항 모니터링 시스템: 그 이후 두달의 이야기</summary></entry><entry><title type="html">파이썬 오픈소스 라이브러리 변경사항 모니터링 시스템</title><link href="http://iam.namjun.kim/security/python/development/2022/06/06/python-open-source-library-monitoring-system/" rel="alternate" type="text/html" title="파이썬 오픈소스 라이브러리 변경사항 모니터링 시스템" /><published>2022-06-06T00:00:00+00:00</published><updated>2022-06-06T00:00:00+00:00</updated><id>http://iam.namjun.kim/security/python/development/2022/06/06/python-open-source-library-monitoring-system</id><content type="html" xml:base="http://iam.namjun.kim/security/python/development/2022/06/06/python-open-source-library-monitoring-system/">&lt;h1 id=&quot;pyclone-파이썬-오픈소스-라이브러리-변경사항-모니터링-시스템&quot;&gt;PyClone: 파이썬 오픈소스 라이브러리 변경사항 모니터링 시스템&lt;/h1&gt;

&lt;h2 id=&quot;서론&quot;&gt;서론&lt;/h2&gt;

&lt;p&gt;우리는 생각보다 많은 사항을 알아보고 오픈소스 라이브러리를 사용하지 않는다.&lt;/p&gt;

&lt;p&gt;그냥 stackoverflow가 시키니까, 다른 라이브러리에서 dependency로 걸려 있으니까 쓴다.&lt;/p&gt;

&lt;p&gt;근데.. 이쪽을 노리고 많은 공격자들이 오픈소스 라이브러리를 타깃으로 삼기 시작했다.&lt;/p&gt;

&lt;p&gt;https://www.codingworldnews.com/news/articleView.html?idxno=10171&lt;/p&gt;

&lt;h2 id=&quot;관련-연구&quot;&gt;관련 연구&lt;/h2&gt;

&lt;p&gt;항상 이런 일이 있을 때 마다, 관련 연구를 찾는 습관을 들이고 있다. 여튼,&lt;/p&gt;

&lt;p&gt;PyPI나 GitHub에서는 특정 라이브러리의 dependency에서 CVE 정보가 올라오는 경우 개발자에게 알려 패치할 수 있도록 하는 기능은 이미 많이 개발되어 있다.&lt;/p&gt;

&lt;p&gt;그러나 이러한 코드 자체에 악성 스크립트가 들어간 경우에는 개발자가 repository 들어가서 분석하는 방법 말곤아직 마땅한 방법이 없다.&lt;/p&gt;

&lt;p&gt;서비스까진 바라지 않고 그냥 이런 정보를 정리해 둔 사이트가 있으면 좋은데 논문이나 자료들이 많이 없어서 어떻게 해야 할지 잘 몰랐다.&lt;/p&gt;

&lt;h2 id=&quot;pyclone&quot;&gt;PyClone&lt;/h2&gt;

&lt;p&gt;그래서 그냥 만들기로 했다.&lt;/p&gt;

&lt;p&gt;우선 Python 라이브러리만 대상으로 하고, 나중에 더 커질 수 있으면 다른 라이브러리도 할 수 있으니 확장성을 고려했다.&lt;/p&gt;

&lt;p&gt;생각보다 만들기로 마음을 먹은 뒤에는 일이 쉽게 풀렸다. 역시 인간은 만들기로 마음을 먹는 것이 제일 중요하다.&lt;/p&gt;

&lt;h3 id=&quot;탐지해야-할-것&quot;&gt;탐지해야 할 것&lt;/h3&gt;

&lt;p&gt;원래는 소스코드 실행을 통해 변경 사항이 있는 경우 그 사항을 모니터링 할려고 했는데, 생각보다 리소스가 너무 많이 들 것 같아 두 가지 요소만을 고려하여 우선 저장 하도록 했다.&lt;/p&gt;

&lt;p&gt;단, 나중에 이 항목은 추가될 수 있다는 가정 하에 작성했다.&lt;/p&gt;

&lt;h4 id=&quot;도메인--ip-주소&quot;&gt;도메인 / IP 주소&lt;/h4&gt;

&lt;p&gt;소스코드 정적 스캔을 통해 도메인, IP 주소가 있는 경우 그 정보를 저장한다.&lt;/p&gt;

&lt;h4 id=&quot;실행-파일&quot;&gt;실행 파일&lt;/h4&gt;

&lt;p&gt;실행 파일이 있는 경우 파일명과 SHA256 해시 정보를 저장한다.&lt;/p&gt;

&lt;h3 id=&quot;작명&quot;&gt;작명&lt;/h3&gt;

&lt;p&gt;원래부터 작명은 즉흥으로 했었어서 그냥 Aviation Hazards 보다가 Cyclone이 보여서 Python + Cyclone = PyClone 으로 지었다.&lt;/p&gt;

&lt;h3 id=&quot;개발&quot;&gt;개발&lt;/h3&gt;

&lt;p&gt;PyPI의 경우 다행(?) 스럽게도 RSS를 통한 패키지 업데이트 정보를 제공한다.&lt;/p&gt;

&lt;p&gt;그래서 일정 시간마다 패키지 정보를 스캔하고, 그 정보를 저장한 뒤 Detection Pipeline(탐지해야 할 것) 을 거치도록 했다.&lt;/p&gt;

&lt;p&gt;그리고 라이브러리 정보 중 메타데이터를 이용한 상관관계 분석이 가능하도록 필요한 것으로 판단되는 정보 중 최대한 많은 정보를 데이터베이스에 저장했다.&lt;/p&gt;

&lt;p&gt;처음에는 그냥 데이터베이스에 저장하고 그 데이터를 구경했는데, 생각보다 많은 데이터가 저장되서 시각화가 필요하였다. 그래서 요즘 공부하고 있는 Next.js (+ Typescript) 를 이용하였다.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://pyclone.namjun.kim&quot;&gt;서비스 바로가기&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/posts/pyclone/dashboard.jpg&quot; alt=&quot;dashboard&quot; /&gt;&lt;/p&gt;

&lt;p&gt;바로 접속하면 다음과 같은 화면이 뜨고, 내가 원하는 패키지를 검색할 수 있도록 하였다.&lt;/p&gt;

&lt;h3 id=&quot;내가-쓰는-곳&quot;&gt;내가 쓰는 곳&lt;/h3&gt;

&lt;p&gt;내가 이걸 활용해서 요즘 쓰는 곳은&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;stackoverflow나 다른 dependency / github issue에서 추천하는 라이브러리 안전성 검색&lt;/li&gt;
  &lt;li&gt;탐지 동향 구경하기&lt;/li&gt;
  &lt;li&gt;새로운 패키지 검색하기&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;정도다.&lt;/p&gt;

&lt;p&gt;사실 내가 쓸려고 만든 수준이라서 많은 디자인적 요소가 없다. 직관적이기만 하면 되서 그냥 그렇게 했다.&lt;/p&gt;

&lt;p&gt;혹시.. 디자인 하기 귀찮아서 그런거 아니냐고 하면 사실 맞다.&lt;/p&gt;

&lt;h3 id=&quot;개발-스택&quot;&gt;개발 스택&lt;/h3&gt;

&lt;p&gt;PyClone을 개발하는데 사용한 개발 스택은 다음과 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Backend
    &lt;ul&gt;
      &lt;li&gt;FastAPI&lt;/li&gt;
      &lt;li&gt;Nginx&lt;/li&gt;
      &lt;li&gt;MySQL&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Frontend
    &lt;ul&gt;
      &lt;li&gt;Typescript + Next.js&lt;/li&gt;
      &lt;li&gt;Material UI&lt;/li&gt;
      &lt;li&gt;Ant Design&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Infrastructure
    &lt;ul&gt;
      &lt;li&gt;2 CPU Core / 4 GB RAM&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;여담이지만, 로컬 개발 환경에서는 검색 엔진으로 Elasticsearch로 운영하려 했으나 서버 사양 문제로 OOM(Out of Memory) 문제가 계속 발생하여 그냥 MySQL에서 쿼리 + 인덱싱 최적화 하는 방향으로 잡았다.&lt;/p&gt;

&lt;p&gt;그리고 서버 리소스 자체가 한계가 있는지라 최대한 최적화 하는 방향으록 가야 했다.&lt;/p&gt;

&lt;p&gt;나중에 시간되면 이 과정에서 있었던 일을 공유하면 좋겠다. (라고 하지만 언제할 수 있을진 잘 모름)&lt;/p&gt;

&lt;h2 id=&quot;api&quot;&gt;API&lt;/h2&gt;

&lt;p&gt;PyClone에서는 외부 연동 용이성을 위해 HTTP API와 Documentation을 제공한다.&lt;/p&gt;

&lt;p&gt;FastAPI는 swagger-ui랑 바로 연동되서 docs 만드는데 별로 많은 작업이 필요하지 않아서 좋다.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://pyclone-api.namjun.kim/docs&quot;&gt;Documentation 바로가기&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;데이터가 필요하다면 그냥 API를 통해서 가져가면 된다. 단, 너무 자주 요청하게 되면 서버가 뻗을 수도 있으니..&lt;/p&gt;

&lt;p&gt;제발 적당히 해주세요.. ㅠㅠ&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;생각보다 재밌는 데이터가 많아서 아마 당분간은 여기서 나온 데이터만 볼 것 같다.&lt;/p&gt;

&lt;p&gt;오랜만에 다시 해 보는 오픈 서비스였는데 역시 서비스를 만드는 것이 제일 재밌다.&lt;/p&gt;

&lt;h2 id=&quot;future-work&quot;&gt;Future Work&lt;/h2&gt;

&lt;p&gt;처음 시작은 오픈소스 라이브러리 모니터링 도구를 만드는데 그 목적을 가지고 있었지만, PyPI 말고도 더 많은 Repository를 대상으로 하기 위해 확장하고 있다.&lt;/p&gt;

&lt;p&gt;그리고, 데이터를 모니터링 하던 중 생각보다 키보드 실수를 노린 공격이 많은 것을 확인해서 관련 기능을 추가하고자 하고 있다.&lt;/p&gt;

&lt;p&gt;생각해 둔 기능은 대충 있는데, 아직 다 테스트를 안해봐서 조금 더 데이터가 쌓이면 업데이트 하도록 하겠다.&lt;/p&gt;

&lt;p&gt;아마.. 퇴근하고 하는 것이라 언제 완성될 진 모르겠지만 그래도 생각한 대로 되면 꽤 재밌는 데이터가 나올 것 같다.&lt;/p&gt;

&lt;p&gt;언젠가 쓸 수 있겠지..&lt;/p&gt;

&lt;h2 id=&quot;변경사항&quot;&gt;변경사항&lt;/h2&gt;

&lt;h3 id=&quot;101-20220607&quot;&gt;1.0.1 (2022.06.07)&lt;/h3&gt;

&lt;h4 id=&quot;1-소스코드-정적-스캔-방식-변경&quot;&gt;1. 소스코드 정적 스캔 방식 변경&lt;/h4&gt;

&lt;p&gt;1.0.0 버전에서는 정규표현식을 이용한 도메인, IP 주소를 추출 하였다.&lt;/p&gt;

&lt;p&gt;이는 매우 빠르게(?) 할 수 있다는 장점이 있지만, 실제 그 문자열이 사용되는지 확인할 수 없다는 문제를 가지고 있다. 그리고 주석에 링크가 있는 경우에도 탐지되서 불필요하게 많은 탐지 로그를 남긴다는 문제를 가지고 있었다.&lt;/p&gt;

&lt;p&gt;1.0.1 버전에서는 파이썬 스크립트를 AST (Abstract Syntax Tree, 추상 구문 트리)로 표현, 분석하여 문자열을 추출하여 비교하는 방식으로 변경 하였다.&lt;/p&gt;

&lt;p&gt;이를 통해 기존 데이터 중 일부를 샘플링하여 테스트 한 결과 코드 내 주석 등에서 사용된 링크에서 정보를 파싱하지 않고 실제 코드 내에서 사용되는 URL에 대해서만 파싱하는 것을 확인 하였다.&lt;/p&gt;

&lt;h4 id=&quot;2-e-mail-reverse-search-기능&quot;&gt;2. E-mail Reverse Search 기능&lt;/h4&gt;

&lt;p&gt;이메일 리버스 검색 (소유자의 이메일 - 패키지, 소유자의 이메일 도메인 주소 - 패키지) 지원 기능을 추가함.&lt;/p&gt;

&lt;p&gt;특정 유저가 무작위로 리포지토리를 생성하는 행위 혹은 소유자의 이메일이 등록된 도메인이 만료된 경우를 탐지하기 위해 해당 기능을 추가 하였다.&lt;/p&gt;</content><author><name>Namjun</name></author><category term="security" /><category term="python" /><category term="development" /><category term="security" /><summary type="html">PyClone: 파이썬 오픈소스 라이브러리 변경사항 모니터링 시스템</summary></entry><entry><title type="html">Elasticsearch에서 아랍어 키워드 검색하기</title><link href="http://iam.namjun.kim/arabic/nlp/elasticsearch/2022/04/30/elasticsearch-do-arabic-nlp/" rel="alternate" type="text/html" title="Elasticsearch에서 아랍어 키워드 검색하기" /><published>2022-04-30T00:00:00+00:00</published><updated>2022-04-30T00:00:00+00:00</updated><id>http://iam.namjun.kim/arabic/nlp/elasticsearch/2022/04/30/elasticsearch-do-arabic-nlp</id><content type="html" xml:base="http://iam.namjun.kim/arabic/nlp/elasticsearch/2022/04/30/elasticsearch-do-arabic-nlp/">&lt;h1 id=&quot;elasticsearch에서-아랍어-키워드-검색하기&quot;&gt;Elasticsearch에서 아랍어 키워드 검색하기&lt;/h1&gt;

&lt;p&gt;이번 글에서는 Elasticsearch에서 아랍어 키워드 검색하는 방법과 그 삽질에 대해 알아 보고자 합니다.&lt;/p&gt;

&lt;p&gt;영어로 된 글은 적지만 좀 있는데 한국어로 된 글은 거의 없어서 한번 정리하는 겸 해서 써봤습니다.&lt;/p&gt;

&lt;h2 id=&quot;아랍어-기초&quot;&gt;아랍어 기초&lt;/h2&gt;

&lt;p&gt;일단 아랍어는 중동 지역과 북아프리카 지역에서 주로 사용하고 있습니다. UN 공용어에 화자는 규모 면에서 세계 5위를 자랑할 정도로 많은 사람들이 사용합니다.&lt;/p&gt;

&lt;p&gt;주로 우리나라에서는 제2외국어로 아랍어를 많이 선택하며, 이슬람교의 영향을 많이 받았습니다.&lt;/p&gt;

&lt;p&gt;다들 잘 알고 있는 사실은 읽기 매우 어렵다, 반대로 읽는다 정도 그리고 모음이 없다(반모음이 있음) 정도입니다.&lt;/p&gt;

&lt;p&gt;우리가 아랍어가 어렵다고 생각하는 이유는 바로 아래 사진으로 이해할 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://w.namu.la/s/efaee5afc737de1c2a95c16b9ebf18ed4c3fe7e3ed63328b20ad6a1fe4d13497a89f959d2c988b4ed600a3e906e3a7d8420e31a0237ee0d339b855622e6642a7a9e571d21a8fd6e5ec040ebfe7c8428b2dbc47e6c7356382e825cdecb8c5a5f5&quot; alt=&quot;external/www.omn...&quot; /&gt;&lt;/p&gt;

&lt;p&gt;아랍 문자는 그 문자가 독립적으로 사용될 때와 단어에서 사용되는 위치에 따라 그 모양이 바뀝니다.&lt;/p&gt;

&lt;p&gt;그래서 처음 배울 때 어말-어중-어두-독립형 (+함자) 까지 외워야 하는 상황이 발생합니다. (러닝커브가..)&lt;/p&gt;

&lt;h3 id=&quot;아랍-방언&quot;&gt;아랍 방언&lt;/h3&gt;

&lt;p&gt;우리가 일반적으로 접하는 아랍어(문어체)는 Modern Standard Arabic(MSA, 현대 표준 아랍어)입니다. 주로 뉴스, 공공문서와 같은 공적인 자료에서 많이 사용되며 약 1300년전 사용되었던 언어입니다.&lt;/p&gt;

&lt;p&gt;그에 비해 AD(Arabic dialects, 아랍 방언)은 지역에 따라 나뉘는데 크게 걸프(Gulf), 이집트(Egyption), 마그렙(Maghrebi, 북아프리카 서쪽지역), 그리고 레반트(Levantine, 레바논과 팔레스타인 그리고 그 근방)으로 나뉩니다. 그 이외에도 더 많이 나뉘지만 편의상 4개만 언급 하였습니다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://blogs.transparent.com/arabic/wp-content/uploads/sites/12/2017/07/Arabic-question-words-standard-and-dialects.png&quot; alt=&quot;Questions Words in Arabic Dialects&quot; /&gt;&lt;/p&gt;

&lt;p&gt;위 사진을 보면 똑같은 말이라도 지역에 따라 표기가 아예 다른 것을 볼 수 있습니다.&lt;/p&gt;

&lt;p&gt;만약 당신이 표준 아랍어만 처리해야 한다면 이 글을 더 이상 볼 이유가 없긴 합니다. 그러나 많은 사람들이 실제로 인터넷 상에서 지역 방언을 많이 사용하기 때문에.. 서비스를 운영하는 사람 입장이라면 아랍 방언도 같이 고려해야 하는 상황이 올 수 있습니다.&lt;/p&gt;

&lt;h2 id=&quot;아랍어-검색이-잘-안되는-이유&quot;&gt;아랍어 검색이 잘 안되는 이유&lt;/h2&gt;

&lt;p&gt;그래서 왜 안되느냐, 바로 아랍어 방언과 기존 영어식 ngram의 한계입니다.&lt;/p&gt;

&lt;p&gt;(물론 더 다양한 이유가 있을 것 같습니다만, 제가 생각했을 땐 크게 두 가지 이유 같습니다)&lt;/p&gt;

&lt;h3 id=&quot;아랍어-방언&quot;&gt;아랍어 방언&lt;/h3&gt;

&lt;p&gt;아랍어는 아주 다양한 방언을 가지고 있습니다. 공식 석상에서는 현대 표준 아랍어를 사용합니다만, 인터넷 상에서도 다양한 방언들을 쉽게 찾을 수 있습니다.&lt;/p&gt;

&lt;p&gt;한국어로 따져도 대한민국에서 사용하는 언어와 문화어(조선어) 가 있고, 중국 조선족이 사용하는 조선어가 따로 있습니다. 근데 더 많은 화자가 있는 아랍어는 더 다양한 방언을 가지고 있기 때문에 한계가 있습니다.&lt;/p&gt;

&lt;p&gt;영어도 영국 영어, 미국 영어, 싱가포르 영어, 홍콩 영어 등 화자의 지역에 따라 사용하는 단어가 나뉘긴 합니다만,표기법이 크게 다르지 않고 서로 간에 대화가 통하는 정도지만 아랍어는 그렇지 않다는 사실..&lt;/p&gt;

&lt;h3 id=&quot;그럼-ngram을-사용하면-안되나요&quot;&gt;그럼 ngram을 사용하면 안되나요?&lt;/h3&gt;

&lt;p&gt;문자의 위치에 따라 표기가 바뀌지 않는 영어와 한국어와는 달리 아랍어는 위치에 따라 표기가 바뀝니다.&lt;/p&gt;

&lt;p&gt;이를 단순히 ngram으로 처리 하기에는 그 한계점이 명확합니다. -&amp;gt; ngram으로 자른다면 아예 다른 단어가 되기 때문에 잘 검색이 되지 않습니다.&lt;/p&gt;

&lt;p&gt;물론 제가 검색을 해봤을 때 ngram으로 시도하는 경우도 많이 있습니다만, 이런 경우에는 indexing 해야 할 token이 너무 많아져 성능 저하 문제가 발생할 수 있습니다.&lt;/p&gt;

&lt;h3 id=&quot;elasticsearch는-그럼-이-사실을-모르나요&quot;&gt;Elasticsearch는 그럼 이 사실을 모르나요?&lt;/h3&gt;

&lt;p&gt;Elasticsearch과 lucene도 이러한 부분을 잘 알고 있어서 기본 아랍어 analyzer를 제공합니다. (arabic analyzer, https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lang-analyzer.html)&lt;/p&gt;

&lt;p&gt;물론 기본 아랍어 analyzer도 좋은 기능입니다만, 제 경험상 제대로 된 검색이 되지 않는 문제와 여러 방언이 동시에 저장된 데이터에서 검색하는데 그 한계점을 겪었습니다.&lt;/p&gt;

&lt;p&gt;일단 기본적으로 제공하는 analyzer는 어근에 대한 Root Dictionary가 있는 analyzer입니다. 이는 영어와 한글과 같은&lt;/p&gt;

&lt;h2 id=&quot;isri-stemmer&quot;&gt;ISRI Stemmer&lt;/h2&gt;

&lt;p&gt;이러한 부분을 해결 하고자 많은 연구자들이 No Root Dictionary에 대한 부분을 연구 하였습니다.&lt;/p&gt;

&lt;p&gt;그 중 ISRI(Information Science Research Institute) 에서 개발한 Arabic stemmer는 어근 사전이 없는 상태에서도 아랍어에 대한 Stemming이 가능하도록 설계 되었습니다.&lt;/p&gt;

&lt;p&gt;기존의 아랍어 Stemmer인 Al-Fedaghi, Al-Shalabi, Khoja(?), Garside와 어근을 추출하는 단계는 유사하지만 약간의 차이점이 있습니다.&lt;/p&gt;

&lt;h3 id=&quot;khoja-stemmer&quot;&gt;Khoja Stemmer&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;분음 부호를 제거 (원형을 찾기 위함)&lt;/li&gt;
  &lt;li&gt;Stopword, Punctuation, numbers 모두 제거 (어근 추출에 중요하지 않은 요소이므로)&lt;/li&gt;
  &lt;li&gt;정관사 al ( ال ) 을 제거 (명사 앞에 붙어서 명사를 한정시키는 역할을 합니다. (Al Jazeera에서 Al이 그 정관사 Al입니다 - 번역하면 The Peninsula (아라비아 반도))&lt;/li&gt;
  &lt;li&gt;(و, waw) 를 제거합니다.
    &lt;ol&gt;
      &lt;li&gt;“و” (이하 waw) 를 제거하는 이유는 아랍어 문법을 보면 알 수 있습니다. waw는 독립적으로 사용될 때 “또는 (and)” 이란 단어로 사용되며,  자음 뒤에 장모음으로 사용됩니다.&lt;/li&gt;
      &lt;li&gt;이는 단어의 원형에 추가로 붙는 형태라 어근을 찾는 과정에서 불필요한 것으로 인식되어 제거합니다.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;접미사, 접속사 제거&lt;/li&gt;
  &lt;li&gt;논문에서 명시된 패턴 목록과 그 결과를 비교하여 일치하는 경우 그 값을 추출합니다.&lt;/li&gt;
  &lt;li&gt;이 값을 이용하여 기존에 가지고 있던 어근 목록과 비교합니다.&lt;/li&gt;
  &lt;li&gt;اوي 를 waw로 대체합니다.&lt;/li&gt;
  &lt;li&gt;모든 함자(글자 위에 붙은 2 반대로 뒤집은 문자) 를 alif-hamza로 대체합니다.&lt;/li&gt;
  &lt;li&gt;두개의 어근이 확인되는 경우를 위해 검사하고, 그 결과에 따라 문자를 어근에 추가합니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;복잡하지만 간단하게 하면 불필요하게 꾸며주는 단어들에 대해 제거하여 최대한 어근을 찾고, 기존에 가지고 있던 어근 목록과 비교하는 과정을 거칩니다.&lt;/p&gt;

&lt;p&gt;그러나 이런 Khoja의 방식에는 몇몇 약점이 있습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;어근 목록을 계속 관리 해야 한다는 점 (최신 데이터를 반영하지 못한다)&lt;/li&gt;
  &lt;li&gt;단어의 형태에 따라 어근이 원래 사용된 단어와 다르게 결과가 나온다는 점
    &lt;ul&gt;
      &lt;li&gt;이 방식에서는 다른 단어에 붙은 hamza를 alif-hamza로 모두 대체하는데, 이 hamza를 삭제하면서 원래의 뜻이 바뀔 수 있다는 점&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;마지막으로, 삭제하는 문자로 구성된 단어에 대해서는 제대로 stemming을 하지 못한다는 점&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;isri-stemmer-1&quot;&gt;ISRI Stemmer&lt;/h3&gt;

&lt;p&gt;이런 부분을 많이 고려해서인지, ISRI Stemmer는 Khoja Stemmer와 비교하여 단어의 삭제보단 Normalizing에 초점을 뒀다는 것을 알 수 있습니다.&lt;/p&gt;

&lt;p&gt;옆에 있는 부분은 제가 약간의 코멘트를 달아뒀습니다. - 틀린 부분 있으면 알려 주세용&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;분음 부호를 제거 (원형을 찾기 위함) &amp;gt; 동일&lt;/li&gt;
  &lt;li&gt;hamza를 제거하고 그 원형을 사용하는 것 &amp;gt; 기존에는 hamza를 alif-hamza로 대체 하였는데 이를 원형을 사용하는 것으로 변경됨&lt;/li&gt;
  &lt;li&gt;접두사가 길이가 3 혹은 2 인 경우 그 접두사 제거 &amp;gt; 접미사, 접두사를 그냥 모두 제거하는 것보다 일정 길이 이상인 것만 제거함으로서 원형을 살릴 수 있도록 하였음.&lt;/li&gt;
  &lt;li&gt;만약 단어가  و  로 시작한다면, waw를 삭제 &amp;gt; waw를 모두 삭제하는 방향에서 장모음으로 사용되는 경우 삭제하는 것으로 그 범위를 줄였습니다.&lt;/li&gt;
  &lt;li&gt;alif-hamza도 hamza가 아닌 alif 원형을 사용합니다. &amp;gt; 아마 이는 기존에 hamza -&amp;gt; alif-hamza로 대체 함으로서 의도하지 않은 어근 변경에 따른 문제점을 해결하기 위함인 것 같습니다.&lt;/li&gt;
  &lt;li&gt;stem된 token이 길이가 3 이하인 경우 종료 &amp;gt; 길이 3 이하인 것들은 어근으로 판단하는 것 같습니다.&lt;/li&gt;
  &lt;li&gt;token의 길이에 따라 다음과 같이 처리
    &lt;ol&gt;
      &lt;li&gt;길이 4) PR4[논문에 명시] 의 값과 일치하면 그 값을 추출하고 종료. 아닌 경우 접미사 1, 접두사 1을 S1, P1과 비교하여 포함하는 경우 삭제하고 반환.&lt;/li&gt;
      &lt;li&gt;길이 5) 길이 4와 비슷하지만 PR53, PR54를 사용&lt;/li&gt;
      &lt;li&gt;길이 6) 길이 4와 비슷하지만 PR63을 사용&lt;/li&gt;
      &lt;li&gt;길이 7) 여기서는 접미사 1, 접두사 1을 삭제하는데 그 목표를 두고 있음. 길이 6일 때랑 같은 방식으로 한번 더 처리한 뒤 그 값을 보고 반환&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;조금 더 복잡하지만, 최대한 원형을 살릴 수 있게 하는데 그 초점을 뒀습니다.&lt;/p&gt;

&lt;p&gt;그리고 보시면 알겠지만 어근 목록을 가지고 있을 필요가 없어서 아랍어와 같이 방언이 많은 언어에게는 더 효과적으로 쓰일 수 있을 것 같습니다.&lt;/p&gt;

&lt;p&gt;실제로 Python으로 두 Stemmer를 비교 해보니 ISRI Stemmer가 MSA, AD 모두 원형을 더 잘 살리는 것 같았습니다. (그리고 제가 봐도 잘 몰라서 그럴수 있습니다 ㅋㅋ)&lt;/p&gt;

&lt;h2 id=&quot;elasticsearch-플러그인-개발&quot;&gt;Elasticsearch 플러그인 개발&lt;/h2&gt;

&lt;p&gt;Crawler가 자연어 처리를 해서 넣어 주는것도 좋은 방식이지만, 플러그인을 개발해서 쉽게 analyzing 하는 것도 좋은 방식이라고 생각했습니다.&lt;/p&gt;

&lt;p&gt;플러그인에 대한 Reference가 잘 없어서 참고하기 쉽진 않았지만, 공식 사이트에 설명이 있으니 참고 해보면 좋을 것 같습니다.&lt;/p&gt;

&lt;p&gt;링크: https://www.elastic.co/guide/en/elasticsearch/plugins/current/plugin-authors.html&lt;/p&gt;

&lt;p&gt;나중에 기회가 된다면 플러그인 개발에 대해 따로 이야기 해보겠습니다.&lt;/p&gt;

&lt;h2 id=&quot;테스트&quot;&gt;테스트&lt;/h2&gt;

&lt;p&gt;MSA는 테스트 할 수 있는 문구를 잘 구할 수 있는데, 아랍 방언의 경우에는 구하기 쉽지 않았습니다.&lt;/p&gt;

&lt;p&gt;결정적으로 제가 아랍 방언을 모르니.. 이게 봐도 어떤 아랍 방언인지 알 수 없었습니다.&lt;/p&gt;

&lt;p&gt;그러던 중… ` NADI 2020: The First Nuanced Arabic Dialect Identification Shared Task ` 에서 국가/지역별로 라벨링된 트위터 데이터셋을 공개한 사실을 확인 하였습니다.&lt;/p&gt;

&lt;p&gt;이 데이터를 이용하여 각 지역별로 Analyzer가 잘 작동하는지 기본적인 테스트를 진행할 수 있었습니다.&lt;/p&gt;

&lt;h3 id=&quot;egyptian&quot;&gt;Egyptian&lt;/h3&gt;

&lt;h4 id=&quot;기본-문구&quot;&gt;기본 문구&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;قلبي وتر في ربابه بيغني على ليلاه

&amp;gt;&amp;gt; 나의 심장은 랩의 화음이고 밤에는 노래한다 (번역본)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;analyzer-with-isri-stemmer&quot;&gt;Analyzer with ISRI Stemmer&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;قلب  &amp;gt; 심장
وتر  &amp;gt; 힘줄
ربب  &amp;gt; Rabb (이슬람에서 신, 주인)
بيغ  &amp;gt; 반짝이다
ليل  &amp;gt; 밤
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;standard-analyzer&quot;&gt;Standard Analyzer&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;قلب   &amp;gt; 심장
وتر   &amp;gt; 힘줄 
رباب  &amp;gt; Rabb (이슬람에서 신, 주인)
بيغن  &amp;gt; 시작
ليلا   &amp;gt; 밤
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;yemen&quot;&gt;Yemen&lt;/h3&gt;

&lt;h4 id=&quot;기본-문구-1&quot;&gt;기본 문구&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;لازم ي مان أصلا كان م رقدت م بترتاح

&amp;gt;&amp;gt; 나는 쉬다가 잠에 들어버렸다. (구글 번역)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;analyzer-with-isri-stemmer-1&quot;&gt;Analyzer with ISRI Stemmer&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;لزم  &amp;gt; 필요하다
مان  &amp;gt; 남성
أصلا  &amp;gt; 원래
كان  &amp;gt; 그것은
رقد  &amp;gt; 눕다
راح  &amp;gt; ~해버리다
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;standard-analyzer-1&quot;&gt;Standard Analyzer&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;لازم      &amp;gt; 필요한
يمان     &amp;gt; Yaman (번역불가)
اصلا      &amp;gt; 원래
مرقدت    &amp;gt; 신사
مبترتاح  &amp;gt; 안도하다
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;libya&quot;&gt;Libya&lt;/h3&gt;

&lt;h4 id=&quot;기본-문구-2&quot;&gt;기본 문구&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;وهذي كيف تجي عاد

&amp;gt;&amp;gt; 그리고 이렇게 돌아왔습니다. (구글 번역)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;analyzer-with-isri-stemmer-2&quot;&gt;Analyzer with ISRI Stemmer&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;وهذ  &amp;gt; 이것
كيف  &amp;gt; 어떻게
تجي  &amp;gt; 오다
عاد  &amp;gt; 돌아왔다
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;standard-analyzer-2&quot;&gt;Standard Analyzer&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;هذ   &amp;gt; 이것
كيف  &amp;gt; 어떻게
تج   &amp;gt; Taj, Iran (도시 이름)
عاد  &amp;gt; 돌아왔다
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;테스트를 더 진행하면 좋겠지만, 분류하는데 시간이 오래 걸려서 그냥 몇몇개 댓글만 해 봤습니다.&lt;/p&gt;

&lt;p&gt;위 예시를 보면 어떤 글에 대해서는 Standard Analyzer가 어근을 이상하게 잘려서 이상하게 tokenizing 되는 부분이 몇몇 있었지만, ISRI Stemmer에서는 그런 현상이 조금은 줄어든 것을 확인 할 수 있었습니다.&lt;/p&gt;

&lt;p&gt;물론 더 다양하게 실험하고 그 결과를 봐야 확실하게 알 수 있을 것 같습니다. (이거 해주실분.. 있음.. 공유주세요)&lt;/p&gt;

&lt;p&gt;그리고 ISRI Stemmer가 Sliver Bullet 이라는 의미가 아닙니다. 그냥 좀 더 나은 방법을 찾아가는 과정이지, 이는 절대 완벽한 tokenizing을 의미하진 않습니다.&lt;/p&gt;

&lt;h2 id=&quot;elasticsearch-arabic-dialect-plugin&quot;&gt;elasticsearch-arabic-dialect-plugin&lt;/h2&gt;

&lt;p&gt;그냥 이런 것이 있다 정도로 소개하면 심심하실 것 같아 Elasticsearch에 적용할 수 있는 플러그인을 함께 제공 하였습니다.&lt;/p&gt;

&lt;p&gt;Source code: https://github.com/bunseokbot/elasticsearch-arabic-dialect-plugin&lt;/p&gt;

&lt;h3 id=&quot;elasticsearch-plugin&quot;&gt;Elasticsearch Plugin&lt;/h3&gt;

&lt;p&gt;혹시 Elasticsearch에 직접적으로 사용하고 싶으신 분들은 운영중인 모든 노드에 다음과 같이 설치 하시고 재시작하시면 됩니다.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./bin/elasticsearch-plugin install https://github.com/bunseokbot/elasticsearch-arabic-dialect-plugin/releases/download/v1.0.0/elasticsearch-arabic-dialect-plugin-1.0.0.zip&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;elasticsearch-query&quot;&gt;Elasticsearch Query&lt;/h3&gt;

&lt;p&gt;Index 생성 시 settings 내에 custom analyzer - filter에 “arabic_dialect_filter”를 추가 하시면 됩니다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PUT http://localhost:9200/arabic/
{
  &quot;settings&quot;: {
  	&quot;analysis&quot;: {
    	&quot;analyzer&quot;: {
        	&quot;arabic_analyzer&quot;: {
            	&quot;type&quot;: &quot;custom&quot;,
              	&quot;tokenizer&quot;: &quot;whitespace&quot;,
              	&quot;filter&quot;: [&quot;arabic_dialect_filter&quot;]
            }
        }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Analyzer 테스트 하기&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;POST http://localhost:9200/arabic/_analyzer

{
	&quot;analyzer&quot;: &quot;arabic_analyzer&quot;,
	  &quot;text&quot;: &quot;يمكنك الاعتماد على الجزيرة في مسألتي الحقيقة والشفافيةنحن نتفهّم أن خصوصيتك على الإنترنت أمر بالغ الأهمية، وموافقتك على تمكيننا من جمع بعض المعلومات الشخصية عنك يتطلب ثقة كبيرة منك.&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;문구는 Al Jazeera 개인정보보호 관련 팝업 안내문을 참고 하였습니다.&lt;/p&gt;

&lt;h2 id=&quot;결론&quot;&gt;결론&lt;/h2&gt;

&lt;p&gt;음.. 어렵다.&lt;/p&gt;

&lt;p&gt;원래 목표는 한국어로 된 자료가 없어서 한번 써 보게 되었는데 생각보다 많은 것들을 배우게 되었다.&lt;/p&gt;

&lt;p&gt;나중에 기회가 된다면 Vectorization / Scoring 부분도 함 해보면 좋을 것 같다.&lt;/p&gt;

&lt;h2 id=&quot;reference&quot;&gt;Reference&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Boujou, ElMehdi &amp;amp; Chataoui, Hamza &amp;amp; el Mekki, Abdellah &amp;amp; Benjelloun, Saad &amp;amp; Chairi, Ikram &amp;amp; Berrada, Ismail. (2021). An open access NLP dataset for Arabic dialects : Data collection, labeling, and model construction.&lt;/li&gt;
  &lt;li&gt;K. Taghva, R. Elkhoury and J. Coombs, “Arabic stemming without a root dictionary,” International Conference on Information Technology: Coding and Computing (ITCC’05) - Volume II, 2005, pp. 152-157 Vol. 1, doi: 10.1109/ITCC.2005.90.&lt;/li&gt;
  &lt;li&gt;Muaad, A.Y.; Jayappa, H.; Al-antari, M.A.; Lee, S. ArCAR: A Novel Deep Learning ComputerAided Recognition for CharacterLevel Arabic Text Representation and Recognition. Algorithms 2021, 14, 216. https://doi.org/10.3390/a14070216&lt;/li&gt;
  &lt;li&gt;Omar F. Zaidan, Chris Callison-Burch; Arabic Dialect Identification. &lt;strong&gt;Computational Linguistics&lt;/strong&gt; 2014; 40 (1): 171–202. doi: https://doi.org/10.1162/COLI_a_00169&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Namjun</name></author><category term="arabic" /><category term="nlp" /><category term="elasticsearch" /><category term="development" /><summary type="html">Elasticsearch에서 아랍어 키워드 검색하기</summary></entry><entry><title type="html">2021 사이버 보안 챌린지 후기</title><link href="http://iam.namjun.kim/security/challenge/development/2022/04/16/cyber-security-challenge/" rel="alternate" type="text/html" title="2021 사이버 보안 챌린지 후기" /><published>2022-04-16T00:00:00+00:00</published><updated>2022-04-16T00:00:00+00:00</updated><id>http://iam.namjun.kim/security/challenge/development/2022/04/16/cyber-security-challenge</id><content type="html" xml:base="http://iam.namjun.kim/security/challenge/development/2022/04/16/cyber-security-challenge/">&lt;h1 id=&quot;2021-사이버-보안-챌린지-후기&quot;&gt;2021 사이버 보안 챌린지 후기&lt;/h1&gt;

&lt;p&gt;이번 글에서는 동아리 사람들(+a)과 나간 2021 사이버 보안 챌린지에서 평가를 위한 공격 탐지 시스템(IDS)를 만들면서 경험한 후기에 대해 이야기 해 보고자 한다.&lt;/p&gt;

&lt;p&gt;별로 긴 내용은 아니지만 그래도 까먹기 전에 정리하면 좋을 것 같아서 써본다.&lt;/p&gt;

&lt;h2 id=&quot;사이버-보안-챌린지&quot;&gt;사이버 보안 챌린지?&lt;/h2&gt;

&lt;p&gt;사이버 보안 챌린지는 자동차 보안 R&amp;amp;D 경진대회로서 자율주행차량 보안 기술 확보를 위한 공개 경진대회이다.&lt;/p&gt;

&lt;p&gt;대회 운영진 측에서 제공하는 차량 인포테인먼스 시스템(Automotive Grade Linux, AGL)에 대해 공격 + 공격 탐지를 위한 IDS 개발 두 부분을 모두 고려해서 개발해야 했다.&lt;/p&gt;

&lt;h2 id=&quot;평가-기준&quot;&gt;평가 기준&lt;/h2&gt;

&lt;p&gt;각 팀에서 찾은 취약점을 이용해서 공격을 재현(reproduce)하고, 공격 대상 환경의 저널 로그만을 기반으로 하여 탐지를 진행하는 방식이였다.&lt;/p&gt;

&lt;p&gt;공격 시간을 맞추는 경우 점수를 얻을 수 있지만, 오탐한 경우 점수가 깎이기 때문에 최대한 정탐을 하는 것이 중요했다.&lt;/p&gt;

&lt;p&gt;공격 벡터는 Wi-Fi, Bluetooth 등 다양했지만, 탐지 경로가 저널 로그 하나라 좀 부족한 점이 없진 않았다.&lt;/p&gt;

&lt;h2 id=&quot;개발-과정&quot;&gt;개발 과정&lt;/h2&gt;

&lt;p&gt;이 대회에서 사용한 공격 탐지 시스템을 개발한 과정은 다음과 같다.&lt;/p&gt;

&lt;h4 id=&quot;1-공격-로그-수집&quot;&gt;1. 공격 로그 수집&lt;/h4&gt;

&lt;p&gt;일단 개발 하기 전에 우리 팀에서 수집한 공격 로그와 정상 환경의 로그 모두 필요했다. 이게 있어야 개발을 할 때 기준이 되고 또 테스트를 진행할 수 있기 때문이다.&lt;/p&gt;

&lt;p&gt;대회 규정에서는 15분간 로그를 수집한다고 했기 때문에 모든 로그에 대해 15분간 수집을 진행했다.&lt;/p&gt;

&lt;h4 id=&quot;2-로그-정규화&quot;&gt;2. 로그 정규화&lt;/h4&gt;

&lt;p&gt;어짜피 동일 환경에서 구현되기도 했고 JSON으로 남았기에 생각보다 정규화 하는 과정은 어렵지 않았다.&lt;/p&gt;

&lt;p&gt;하지만.. 제일 어려운 점은 로그 파일은 하나인데 여러 프로세스(agl-dashboard, agl-hvac등)가 동시에 로그를 남기고 있단 사실이였다.&lt;/p&gt;

&lt;p&gt;즉, A 프로세스의 sequence 10에 해당하는 로그 다음에 B 프로세스의 sequence 5에 해당하는 로그가 올 수 있다는 점이며 프로세스가 많기 때문에 어떤 로그가 남을진 아무도 모르는 상황이였다.&lt;/p&gt;

&lt;p&gt;여기서 주목할 점은 우리가 프로세스 별로는 sequence가 일정하다는 부분이다. 이러한 점을 이용하여 어떤 프로세스에서 로그가 남았는지 분리할 수 있다.&lt;/p&gt;

&lt;h4 id=&quot;3-탐지-엔진-뼈대-제작&quot;&gt;3. 탐지 엔진 뼈대 제작&lt;/h4&gt;

&lt;p&gt;어짜피 탐지 룰을 가장 잘 아는 사람은 공격 POC를 작성한 사람이라 나는 그들이 잘 룰을 작성할 수 있게 그 뼈대를 제공하는데 초점을 두었다.&lt;/p&gt;

&lt;p&gt;엔진에서 로그를 추출하면 여러 항목들이 있었지만 실질적으로 아래 항목들만 필요하기에 다른 로그들은 최초에 로그를 읽는 과정에서 사용하지 않았다.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;설명&lt;/th&gt;
      &lt;th&gt;특이사항&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;_PID&lt;/td&gt;
      &lt;td&gt;Process ID&lt;/td&gt;
      &lt;td&gt;리눅스 PID&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;_UID&lt;/td&gt;
      &lt;td&gt;User ID&lt;/td&gt;
      &lt;td&gt;프로세스 User ID&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;_GID&lt;/td&gt;
      &lt;td&gt;Group ID&lt;/td&gt;
      &lt;td&gt;프로세스 Group ID&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;__REALTIME_TIMESTAMP&lt;/td&gt;
      &lt;td&gt;시간&lt;/td&gt;
      &lt;td&gt;로그가 기록된 기준 시간을 timestamp로 저장한다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;_EXE&lt;/td&gt;
      &lt;td&gt;프로세스&lt;/td&gt;
      &lt;td&gt;프로세스명 (e.g. agl-dashboard)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MESSAGE&lt;/td&gt;
      &lt;td&gt;메시지&lt;/td&gt;
      &lt;td&gt;nested object 처럼 json이 될 수도, 그냥 plain-text message가 될 수도 있다.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;4-설정-값-업데이트-기능&quot;&gt;4. 설정 값 업데이트 기능&lt;/h4&gt;

&lt;p&gt;AGL에서는 설정 값이 변경되면 그 정보를 로그에 남기도록 개발되어 있다. 예를 들어 공기정화시스템(HVAC) 값이 활성화 되면 ` hvacSocketSet  `값이 0에서 1로 변경되었다는 사실이 로그로 남게 된다.&lt;/p&gt;

&lt;p&gt;대부분 공격이 비정상적 경로를 통해 설정 값을 바꾸거나 하는 것이 많아서 이러한 설정 정보를 가져올 수 있도록 하는 것이 중요했다.&lt;/p&gt;

&lt;h4 id=&quot;5-성능-개선&quot;&gt;5. 성능 개선&lt;/h4&gt;

&lt;p&gt;처음 성능 측정을 했을 땐 모든 row의 값을 python built-in library인 json으로 읽었기에 성능이 느렸다.&lt;/p&gt;

&lt;p&gt;팀원들과 여러 이야기를 하면서 성능 개선에 대해 이야기를 해 보았고, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandas&lt;/code&gt;가 더 빠르다는 상섭이형의 이야기를 들었기에 실제로 테스트를 진행 해보니 더 빨라서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandas&lt;/code&gt;를 도입하게 되었다.&lt;/p&gt;

&lt;p&gt;그리고 탐지 부분에서도 값을 비교하는 과정에서 정규표현식을 많이 사용하였다.&lt;/p&gt;

&lt;p&gt;사람마다, 그리고 코드에 따라 다른 수 있었지만 성능 측정을 하는 과정에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;split&lt;/code&gt;를 사용해서 자르고 비교하는 과정보다 간결하고 빠르게 작성할 수 있어서 정규표현식을 많이 사용했다.&lt;/p&gt;

&lt;h4 id=&quot;6-고도화&quot;&gt;6. 고도화&lt;/h4&gt;

&lt;p&gt;알려지지 않은 공격에 대해서 커버리지를 늘리기 위해 머신러닝을 사용했다.&lt;/p&gt;

&lt;p&gt;우리 팀이 찾은 공격에 대해서는 룰로 작성하지만, 우리 팀이 놓쳤던 부분에 대해 보완할 수 있는 방법으로 머신러닝을 일부 도입하였다.&lt;/p&gt;

&lt;h2 id=&quot;대회-결과&quot;&gt;대회 결과&lt;/h2&gt;

&lt;p&gt;율곡관605B라는 팀명으로 동아리 사람들(+a)이 출전하였고, 최종 2등으로 대회를 마무리하였다.&lt;/p&gt;

&lt;p&gt;나는 입사한지 얼마 안되서 대회 현장에 갈 수 없었지만, 그래도 다른 분들이 잘 대응 해주셔서 좋은 성적으로 마무리 할 수 있었던 것 같다.&lt;/p&gt;

&lt;p&gt;특히 이번 대회에서 찬모가 내 멱살 잡고 와서 이렇게 잘 끝낼 수 있었던 것 같다 :)&lt;/p&gt;

&lt;h2 id=&quot;그리고-6개월-뒤&quot;&gt;그리고 6개월 뒤&lt;/h2&gt;

&lt;p&gt;이 글을 쓰는 시점은 대회 본선 이후로 6개월이 지났다.&lt;/p&gt;

&lt;p&gt;사실 당시 기록과 기억을 찾아가면서 쓰는거라 틀린 부분이 있을 수도 있다.&lt;/p&gt;

&lt;p&gt;그 부분을 고려하면서 그냥 얘는 이렇게 생각했구나~ 정도로 읽으면 좋을 것 같다.&lt;/p&gt;</content><author><name>Namjun</name></author><category term="security" /><category term="challenge" /><category term="development" /><category term="security" /><summary type="html">2021 사이버 보안 챌린지 후기</summary></entry><entry><title type="html">LG München - 3 O 17493/20 vom 20.01.2022 (번역)</title><link href="http://iam.namjun.kim/gdpr/translation/privacy/2022/02/01/gdpr-judgement-translation/" rel="alternate" type="text/html" title="LG München - 3 O 17493/20 vom 20.01.2022 (번역)" /><published>2022-02-01T00:00:00+00:00</published><updated>2022-02-01T00:00:00+00:00</updated><id>http://iam.namjun.kim/gdpr/translation/privacy/2022/02/01/gdpr-judgement-translation</id><content type="html" xml:base="http://iam.namjun.kim/gdpr/translation/privacy/2022/02/01/gdpr-judgement-translation/">&lt;h1 id=&quot;lg-münchen-3-o-1749320-vom-20012022&quot;&gt;LG München: 3 O 17493/20 vom 20.01.2022&lt;/h1&gt;

&lt;p&gt;심심한 설날을 보내던 중 재미있는 판결문을 보게 되어서 번역 해 보았다.&lt;/p&gt;

&lt;p&gt;GDPR 관련 독일 법원에서 선고한 판결문인데 알아두면 나쁘지 않을 것 같다.&lt;/p&gt;

&lt;p&gt;원문: https://rewis.io/s/u/zH2/&lt;/p&gt;

&lt;p&gt;최대한 본문 내용 그대로를 담고 싶었으나 오역이 있을 수 있습니다.&lt;/p&gt;

&lt;p&gt;본 내용은 전문 번역이 아니므로 오역이 있을 수 있으므로 참고 목적으로만 사용하시기 바랍니다.&lt;/p&gt;

&lt;h2 id=&quot;redaktioneller-leitsatz-결정의-요약&quot;&gt;Redaktioneller Leitsatz (결정의 요약)&lt;/h2&gt;

&lt;p&gt;동적 IP 주소는 웹 사이트 운영자에게 개인 정보로서 대표(이용)될 수 있다. 그 이유는 제3자 (관할 기관, 인터넷 서비스 제공자)의 도움을 받아 개인을 식별할 수 있는 법적 수단으로서 저장된 IP 주소는 이용될 수 있기 때문이다. (BGH VI ZR 135/13)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;BGH VI ZR 135/13: 웹 사이트 운영자가 저장한 동적 IP 주소는 접속자를 식별하는데 합리적으로 사용할 수 있다는 추상적인 법적 수단을 가지고 있다라고 판시한 독일 연방 사법 재판소의 2017년 5월 16일 판결문 인용&lt;/em&gt; - 이를 인용하는 이유는 Google이 IP 주소를 연결할 수 있는지에 대한 여부가 중요한게 아니라 추상적인 “가능성”만으로도 식별할 수 있는데 그 근거로 사용하였다.&lt;/p&gt;

&lt;p&gt;Google Fonts와 같은 글꼴 제공 서비스를 사용하는 경우 방문자(여기서는 웹 사이트의 사용자를 말한다)가 구글 서버에 직접적으로 연결을 맺지 않고도 글꼴 사용이 가능하기 때문에 GDPR 제6조 제1항 제f호를 적용할 수 없다.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;GDPR 제6조 제1항: 개인정보의 처리는 적어도 다음 각 호의 하나에 해당하고 그 범위에서만 적법하다&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;GDPR 제6조 제1항 제f호: 컨트롤러 또는 제3자의 정당한 이익 목적을 위해 처리가 필요한 경우로서, 개인정보가 보호되어야 할 정보주체의 이익 또는 기본적 권리와 자유가 우선되는 경우는 제외한다. 정보주체가 어링니인 경우에는 특히 그러하다&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;방문자는 자신의 IP 주소를 “암호화”할 의무가 없다. (예를 들어 VPN를 사용하여 위장하는 경우)&lt;/p&gt;

&lt;p&gt;위에서 언급한 내용에 따라 방문자의 IP 주소를 이전하고, 일반적인 개인 권리에 대한 침해와 데이터에 대한 통제력 상실과 관련하여, 방문자의 데이터를 수집하는 것으로 알려진 회사인 Google 및 사용자가 개인의 불편함을 인식하는 방식이 매우 중대하므로 손해에 대한 배상 청구 요청은 정당하다.&lt;/p&gt;

&lt;h2 id=&quot;gegenstand-권리&quot;&gt;Gegenstand (권리)&lt;/h2&gt;

&lt;p&gt;Google Fonts를 사용하여 Google에 IP 주소를 전달 하였음으로 사용자에게 손해 배상 (100유로) 와 Unterlassungsanspruch (금지 명령 구제: 독일 민법 1004조 1항, 한국에서는 금지 가처분 소송으로 해석될 수 있습니다. 여기서는 저장된 방문자의 IP 주소 제거 또는 사용 금지 조치 정도로 번역 가능할 것 같습니다)&lt;/p&gt;

&lt;h2 id=&quot;tenor-본-판결의-본질&quot;&gt;Tenor (본 판결의 본질)&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;피고는 이 사건에 대해 최고 25만유로의 벌금이나 6개월 이하의 징역을 선고 받을 수 있다. 피고는 운영하는 웹 사이트의 IP 주소를 Google Fonts 측에 제공하여 청구자가 해당 폰트 공급자에게 IP 주소를 공개하지 못하도록 한다.&lt;/li&gt;
  &lt;li&gt;피고는 원고와 관련된 개인정보가 처리되고 있는지에 대한 여부와 그 정보를 원고에게 제공하고, 처리 중인 경우 원고에 대해 어떤 개인정보가 저장되고 있는지에 대한 정보를 제공하야 한다.&lt;/li&gt;
  &lt;li&gt;피고는 원고에게 2021년 1월 28일 100유로에 기본 이자율보다 5%포인트 높은 이자를 더한 금액(법정 이자율 + 5%)을 지불해야 한다.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;entscheidungsgründe-결정의-이유&quot;&gt;Entscheidungsgründe (결정의 이유)&lt;/h2&gt;

&lt;p&gt;정당한 소송은 대부분 근거를 가지고 있다. (독일 법원 결정문에 항상 첫 문장, 가내 평안 뭐 이런 느낌)&lt;/p&gt;

&lt;p&gt;신청자(원고)는 민법 제823조와 민법 제1004조에 따라 원고의 IP 주소를 구글에 전달하지 않을 것을 청구할 권리가 있다.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;독일 민법 제823조 (손해배상 의무): 고의적이거나 부주의하게 다른 사람의 권리를 부당하게 침해하는 경우 법에 내용에 따라 보상 의무가 발생한다&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;독일 민법 제1004조 (제거 요청): 소유주는 소유권의 박탈이나 보류 이외의 방법으로 재산에 영향을 미치는 경우 그 장애물의 제거를 요청할 수 있다&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;원고의 동적 IP 주소를 Google 측에 무단으로 전달 하면서 정보 자급권(informationelle Selbstbestimmung) 을 침해하였다. 여기서 정보 자급권은 개인정보 제공 및 사용에 관한 결정을 내릴 권리를 포함한다. 이는 민법 제823조의 일반 인격권 침해에 해당한다.&lt;/p&gt;

&lt;p&gt;(1) 피고가 Google 측에 전달한 동적 IP 주소는 독일 개정 텔레미디어법 제12조 제1항과 2항, 독일연방데이터보호법 제3조 제1항, GDPR 제4조 제1항에 따라 개인정보로 볼 수 있다.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;독일 개정 텔레미디어법 제1조와 제2조 (정의): 서비스 제공자는 자신 또는 제3자의 텔레미디어를 사용할 수 있도록 하거나 그 접근을 제공하는 자연인 혹은 법인을 의미한다.(제1조) 서비스 제공자는 고정된 장소에서 지속적으로 텔레미디어를 제공하는 제공자를 의미한다. 이 때 시설의 위치만으로 제공자의 지위를 결정하지 않는다. (제2조)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;독일 연방데이터보호법 제3조 제1항 (개인정보 처리의 범위와 그 정의): 개인정보란 식별되거나 식별 가능한 자연인(데이터의 주체)과 관련된 모든 정보를 의미한다. 자연인은 특히 이름, 식별 번호, 위치 데이터, 온라인 식별자와 같은 식별자 또는 물리적, 생리적, 유전적, 정신적, 경제적, 문화적, 또는 사화적 정체성을 포함한다.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;GDPR 제4조 제1항: 개인정보는 식별된 또는 식별 가능한 자연인과 관련된 일체의 정보를 가리킨다.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;동적 IP 주소는 웹 사이트 운영자에게 개인 정보를 나타낼 수 있으며, 그 이유는 운영자는 기본적으로 인터넷 서비스 제공자 혹은 관할 당국과 같은 제3자를 통해 저장된 IP 주소를 이용하여 개인을 식별할 수 있는 법적 수단을 가지고 있기 때문이다. 이는 피고가 IP 주소 뒤에 있는 개인을 추상적으로 식별할 수 있으며 이는 피고 혹은 Google 이 IP 주소와 개인을 연결할 수 있는 방법을 가지고 있는지와는 무관한다.&lt;/p&gt;

&lt;p&gt;(2) 피고는 원고가 피고의 웹사이트를 방문했을 때 원고의 IP 주소를 무단으로 Google 측에 전달함으로서 원고의 정보 자급권을 침해하였다.&lt;/p&gt;

&lt;p&gt;원고의 IP 주소를 구글에 제공하는 것은 독일 연방 데이터보호법에 따라 허용되지 않는 침해 사항으로, 이는 원고의 일반적인 인격권에 대한 침해이다. 이는 독일 개정 텔레미디어법 제13조 제 2항과 GDPR 제6조 제1항에 명시되어 있다.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;독일 개정 텔레미디어법 제13조 제2항 (서비스 제공자의 의무): 서비스 제공 업체는 사용자가 동의에 대해 의식이 있는 상태에서 분명하게 동의하였으며, 이를 기록하고 언제라도 내용을 검색할 수 있으며, 사용자가 언제라도 동의를 철회할 수 있도록 보증된 경우 전자적으로 그 내용을 설명할 수 있다.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;(3) 또한, 일반 인격권 침해에 대한 정당한 사유가 있다고 보기 어렵다. 피고는 GDPR 제6조 제1항의 적용을 주장하였으나, Google Fonts는 사이트를 방문할 때 Google 측에 사용자의 IP 주소를 전송하지 않고도 피고가 사용할 수 있기 때문이다.&lt;/p&gt;

&lt;p&gt;(4) 또한, 피고는 웹 사이트에 접속하기 전 사용자의 IP 주소를 암호화 할 의무를 가지고 있지 않았다. 이러한 암호화 의무를 원고에게 부여하는 것은 개인정보를 처리할 때 발생하는 위협으로부터 자연인을 보호하는 것을 목표로 하는 독일 연방 데이터보호법의 목적에 반하는 것이지만, 이러한 의무를 원고에게 부여할 경우 데이터의 권리를 보유하고 있는 자의 권리를 행사하는 것을 오히려 방해할 수 있다. 이러한 점은 보호받을 가치가 있는 권리를 제한하는 행위이다. (판결문 참조: LG Dresden, Urteil vom 11.01.2019, Aktenzeichen 1 AO 1582/18)&lt;/p&gt;

&lt;p&gt;(5) 재발의 위험을 가지고 있다. 원고가 피고의 웹 사이트를 방문했을 때 원고의 IP 주소가 Google에 전달된 부분에 대해서는 논란의 여지가 없이 사실이다. 이전의 불법적인 침해에 대해서는 피고가 반박하지 않았으며, 이는 실제로 침해를 야기할 수 있다. 피고가 현재 웹 사이트 방문자의 IP 주소를 더 이상 Google 측에 전달하지 않더라도 Google Fonts를 사용하고 있기 때문에 그 위험이 제거되었다고 보기는 어렵다. 이러한 위협을 제거하기 위해서는 처벌을 통한 제공 금지 명령을 통해야만 그 위협을 제거할 수 있다.&lt;/p&gt;

&lt;p&gt;정보에 대한 원고의 권리는 GDPR 제15조, 제4조 제2항에 따른다.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;GDPR 제15조 (정보주체의 열람권): 정보 주체는 본인과 관련된 개인정보가 처리되고 있는지 여부에 관련해 컨트롤러로부터 확답을 얻을 권리를 가지며 이 경우 개인정보 및 다음 각 호의 정보에 대한 열람권을 가진다.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;원고는 GDPR 제82조 제1항에 따라 손해 배상을 청구할 수 있다.&lt;/p&gt;

&lt;p&gt;GDPR 제82조의 내용 중에서 “손해”의 개념은 요약문에 명시된 제3항에 따라 광범위적으로 해석되어야 한다. 본 규정의 해석은 제제 및 예방의 목적을 가지고 진행하며 해당 규정의 목적과 부합해야 한다.&lt;/p&gt;

&lt;p&gt;여기서 GDPR 제82조 제1항에 따르면 비 물질적 손해의 성립 요건이 완성되었다고 볼 수 있다. 그러나 이 손해에서 손해를 특정 수준 이상으로 입었는지에 대한 것과 경미한 손해에 대해서는 제외해야 하는지에 대해서는 논란의 여지를 가지고 있다. (vgl. BVerfG NJW 2021, 1005,Rz. 20 m.w.N.; Kohn ZD 2019, 498 (501); Paal MMR 2020, 14 (16)) 그러나 이 경우에는 피고가 웹 사이트를 수정하기 전에 원고의 IP 주소를 Google 측에 전송한 사실을 인정하였다. 그리고 이 IP 주소는 한번만 전송된 것이 아니다. 사용자의 개인정보를 수집하는 것으로 알려진 Google에 대한 개인정보의 통제 상실 및 그 결과 원고가 느끼는 불편함과 관련하여 일반적인 개인 권리에 대한 침해가 발생 하였으므로 해당 청구 소송은 정당하다고 볼 수 있다. 또한, 적절한 수준의 데이터 보호가 보장되지 않은 상태에서 IP 주소를 미국의 Google 서버에 전송된 부분에 대해서는 의심의 여지가 없으며 이 부분을 고려해야 한다. (vgl. EuGH, Urteil vom 16.7.2020 - C-311/18 (Facebook Ireland u. Schrems), NJW 2020, 2613) GDPR 제82조 제1항에 다른 책임은 추가적인 개인정보 유출을 방지하고 보안 조치에 대한 인센티브 목적을 가지고 있으므로 청구된 벌금 액수는 침해의 심각성과 기간을 고려하였을 때 적절하며 이 부분에 대해 피고가 이의를 제기하지 않았다.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;GDPR 제82조 제1항 (보상 권리 및 책임): 본 규정의 침해로 인한 물질적 또는 비 물질적 피해를 입은 자는 누구든지 컨트롤러 또는 프로세서로부터 피해 보상을 받을 권리가 있다.&lt;/em&gt;&lt;/p&gt;</content><author><name>Namjun</name></author><category term="gdpr" /><category term="translation" /><category term="privacy" /><category term="privacy" /><summary type="html">LG München: 3 O 17493/20 vom 20.01.2022</summary></entry><entry><title type="html">Review 2021</title><link href="http://iam.namjun.kim/retrospective/2021/12/31/2021-retrospective/" rel="alternate" type="text/html" title="Review 2021" /><published>2021-12-31T00:00:00+00:00</published><updated>2021-12-31T00:00:00+00:00</updated><id>http://iam.namjun.kim/retrospective/2021/12/31/2021-retrospective</id><content type="html" xml:base="http://iam.namjun.kim/retrospective/2021/12/31/2021-retrospective/">&lt;h1 id=&quot;review-2021&quot;&gt;Review 2021&lt;/h1&gt;

&lt;p&gt;2021년 시간 순서대로 어떤 일이 있었는지 한번씩 살펴 보도록 하자.&lt;/p&gt;

&lt;h2 id=&quot;학교&quot;&gt;학교&lt;/h2&gt;

&lt;p&gt;놀랍게도 아직도 학교를 다니고 있다. 여전히 코로나 상황에 온라인 수업이라 학교는 거의 못갔다.&lt;/p&gt;

&lt;p&gt;이젠 130학점 중 1.5학점이 남았다. 이것만 끝나면 졸업이다.&lt;/p&gt;

&lt;p&gt;2022년에는 오프라인을 할 것 같지만 이제 학교에 수업을 들으러 갈 일이 없다. 제일 아쉬운 부분..&lt;/p&gt;

&lt;p&gt;학교는 정말 다니는지 모르게 조용히 다녔다. 앞으로도 그럴 예정이다.&lt;/p&gt;

&lt;p&gt;그리고 4학년이 되면서 경제학 수업을 정말 많이 들었다. 투자를 위한건 아니고 그냥 재밌어서 들었다.&lt;/p&gt;

&lt;p&gt;동기들이 거의 모두 졸업해버려 학교에 아는 사람보다 모르는 사람이 훨씬 더 많아졌다 ㅠㅠ&lt;/p&gt;

&lt;h2 id=&quot;회사&quot;&gt;회사&lt;/h2&gt;

&lt;p&gt;2020년 6월에 퇴사하고 백수로 열심히 놀다가 2021년 11월에 좋은 기회가 닿아서 지금 회사에 입사했다.&lt;/p&gt;

&lt;p&gt;나름 재밌는 일을 하고 있는 것 같아서 아직은 좋다. 내가 잘 하고 있는지는 잘 모르겠지만.&lt;/p&gt;

&lt;h2 id=&quot;대회&quot;&gt;대회&lt;/h2&gt;

&lt;p&gt;올해도 나름 재밌게 대회를 했다. 학생 신분의 장점!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2021 사이버 보안 챌린지 - 2위&lt;/li&gt;
  &lt;li&gt;KOSPO 웹서비스 정보보안 경진대회 - 우수상&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;해킹 못하는 사람이 무슨 대회인가 싶었는데.. 상을 타버렸다.&lt;/p&gt;

&lt;p&gt;암튼 그렇다.&lt;/p&gt;

&lt;h2 id=&quot;프로젝트&quot;&gt;프로젝트&lt;/h2&gt;

&lt;p&gt;올해는 별 다른 프로젝트를 하진 않았다. 라고 할랬는데..&lt;/p&gt;

&lt;p&gt;블로그에도 글을 썼지만 “모델을 훔치는 완벽한 방법 (robster)” 와 “Safebrowing for Tor network - Orbit” 이라는 주제의 프로젝트를 진행했었다.&lt;/p&gt;

&lt;p&gt;긴 기간의 프로젝트는 아니지만 나름 재밌게 프로젝트를 했다. Golang을 도입해 본 첫 프로젝트기도 하고..&lt;/p&gt;

&lt;h3 id=&quot;그리고-pl&quot;&gt;그리고 PL&lt;/h3&gt;

&lt;p&gt;차세대 보안리더 양성 프로그램에서 진행하는 프로그램의 PL(Project Leader) 역할을 맡게 되었다.&lt;/p&gt;

&lt;p&gt;원래 PL할 생각은 크게 없었는데 재밌는 프로젝트가 보이기도 했고 같이 하자는 연락이 와서 참여하게 되었다.&lt;/p&gt;

&lt;p&gt;근데 내가 도움이 많이 못된 것 같아서 미안하다. 자주 봤어야 하는데 바쁘다는 이유로 자주 못봐서..&lt;/p&gt;

&lt;p&gt;그리고 이상하게도 내가 배우는 것들이 많았던 프로젝트였다. 신기한 것도 많았고 암튼 재밌었다.&lt;/p&gt;

&lt;h2 id=&quot;자격정지&quot;&gt;자격정지&lt;/h2&gt;

&lt;p&gt;나쁜 이유로 자격을 정지 당한건 아니고 연장 등록을 하지 않아 가지고 있던 자격증이 정지 되었다.&lt;/p&gt;

&lt;p&gt;연장 등록에 대해 길게(30분 정도) 생각했지만, 해야 할 이유를 찾지 못해 연장하지 않았다.&lt;/p&gt;

&lt;p&gt;아마 앞으로도 하지 않을 것 같다.&lt;/p&gt;

&lt;h2 id=&quot;거창한-것&quot;&gt;거창한 것&lt;/h2&gt;

&lt;p&gt;근데 뭐.. 거창한게 뭐가 있을까. 그냥 평범한 일상이 모여 특별한 무언가를 만드는거 아닐까 싶어서 써봤다.&lt;/p&gt;

&lt;h2 id=&quot;앞으로&quot;&gt;앞으로&lt;/h2&gt;

&lt;p&gt;뭘 할진 모르겠지만 그 길이 재밌었으면 좋겠다.&lt;/p&gt;

&lt;h3 id=&quot;연애&quot;&gt;연애&lt;/h3&gt;

&lt;p&gt;작년도 올해도 뭐 비슷하다.&lt;/p&gt;

&lt;p&gt;올해는 그냥 딱 그런 성적표였다.&lt;/p&gt;</content><author><name>Namjun</name></author><category term="retrospective" /><category term="retrospective" /><summary type="html">Review 2021</summary></entry><entry><title type="html">Containerized Honeypot Managment Framework</title><link href="http://iam.namjun.kim/research/2021/09/14/containerized-honeypot/" rel="alternate" type="text/html" title="Containerized Honeypot Managment Framework" /><published>2021-09-14T00:00:00+00:00</published><updated>2021-09-14T00:00:00+00:00</updated><id>http://iam.namjun.kim/research/2021/09/14/containerized-honeypot</id><content type="html" xml:base="http://iam.namjun.kim/research/2021/09/14/containerized-honeypot/">&lt;h1 id=&quot;containerized-honeypot-managment-framework-honey-v&quot;&gt;Containerized Honeypot Managment Framework: Honey-V&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;본 프로젝트는 2020년 2학기 세종대학교 창의학기제에서 진행하였습니다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;논문에선 말할 수 없었던 허니팟 운영 히스토리에 대해 설명하고자 한다. 기술적인 부분보단 개발기와 운영적인 부분에 대해 주로 초점을 맞춰 설명할 것 같다. (+기술을 살짝 포함한)&lt;/p&gt;

&lt;h2 id=&quot;entrypoint&quot;&gt;Entrypoint&lt;/h2&gt;

&lt;p&gt;필자가 다니는 학교에는 “창의학기제” 라는 아주 좋은 제도가 있다. 자기주도적인 프로젝트를 하면 학기당 최대 12학점의 전공선택(전선) 학점을 주는 제도인데, 안그래도 초과학기 각이 뜨고 있던 나에겐 아주 감사한 제도였다.&lt;/p&gt;

&lt;p&gt;여튼, 동아리원 중에 4명을 모아서 창의학기제 프로젝트를 진행하게 되었다. (최대 인원을 꽉꽉 모았다)&lt;/p&gt;

&lt;p&gt;일단 프로젝트가 아니라 사람을 모아서 한 것이라 프로젝트를 정하고 있었고 여러 주제중에 허니팟이 나름 재밌어 보여서 시작하게 되었다.&lt;/p&gt;

&lt;p&gt;근데.. 단순히 허니팟만 하면 재미 없어서 어떤 것을 섞어야 시너지 효과를 얻을 수 있을까..? 하던 중에 갑자기 옛날에 친구가 웹 서버가 엄청난 로그 공격을 받았던 (스캔) 기억을 받았다. 그 때 단순히 친구가 샘플 분석하실분 하면서 로그를 공유했는데 이런걸 자동화 시켜서 플랫폼을 만들면 재밌지 않을까 싶어서 시작했다.&lt;/p&gt;

&lt;p&gt;결론: 허니팟으로 공격 정보를 수집해서 분석하거나 해독(detoxify) 시키는 학습을 할 수 있는 플랫폼을 만들자.&lt;/p&gt;

&lt;h2 id=&quot;honeypot&quot;&gt;Honeypot?&lt;/h2&gt;

&lt;p&gt;허니팟의 정의는 간단하게 하면 “비정상적인 접근을 탐지하기 위해 의도적으로 설치해 둔 시스템” 이다. 이걸 통해서 공격 정보를 수집하고 그 정보를 가지고 차단을 하거나 공격 패턴을 탐지할 수 있었다.&lt;/p&gt;

&lt;p&gt;그러나 기존에 공개된 허니팟은 그냥 특정 시스템을 단순히 clone한 시스템에 불과했다. 사실 대부분 일반적인 사람들은 이게 더 필요하긴 했다.&lt;/p&gt;

&lt;p&gt;근데 우리가 해야 할 프로젝트에는 특정 시스템이 아니라 우리가 원하는 다양한 시스템을 자동으로 허니팟으로 운영할 수 있도록 해야 했다. 근데.. 없네..?&lt;/p&gt;

&lt;p&gt;그럼 뭐다?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.ytimg.com/vi/U3Km2lyJAgs/hqdefault.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;만듭시다. 없으면 만들어야지.&lt;/p&gt;

&lt;h2 id=&quot;develop&quot;&gt;Develop&lt;/h2&gt;

&lt;p&gt;처음부터 애초에 Docker를 사용할 생각을 가졌었다. 여러 허니팟을 한번에 관리하고 일정 시간마다 정상 상태로 되돌릴 수 있는 방법 중에 우리가 현실적으로 선택 가능한 도구는 Docker였다.&lt;/p&gt;

&lt;p&gt;다행히도(?) Docker를 매우 사랑해서 오래 사용하고 있던 터라 개발이 크게 어렵지는 않았다. 대신 Go언어로 개발한 것은 처음이라 지금 보면 왜 이렇게 짰을까 싶은 부분이 너무 많다.&lt;/p&gt;

&lt;p&gt;나중에 시간이 되면 제대로 리팩토링을 해야 할 것 같다.&lt;/p&gt;

&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;

&lt;p&gt;이 허니팟에서 제공해야 하는 기능은 아래와 같았다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;허니팟 네트워크 트래픽 덤프&lt;/li&gt;
  &lt;li&gt;파일 변경 내역&lt;/li&gt;
  &lt;li&gt;이미징 (with 해시값)&lt;/li&gt;
  &lt;li&gt;stdout/stderr 값&lt;/li&gt;
  &lt;li&gt;프로세스 정보&lt;/li&gt;
  &lt;li&gt;감염 이전의 값으로 되돌리기 (revert)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;생각보다 빡센 것들이 많았다.&lt;/p&gt;

&lt;h3 id=&quot;docker-api&quot;&gt;Docker API&lt;/h3&gt;

&lt;p&gt;Docker를 그냥 사용해도 되지만 좀 더 정교한 컨트롤을 위해 Docker Go API를 사용했다. Docker도 Go로 작성된거라 생각보다 쉽게 integrate 할 수 있었다.&lt;/p&gt;

&lt;p&gt;근데 제공해야 하는 기능 중에 제일 어려운 부분은 “허니팟 컨테이너 별로 네트워크 트래픽을 어떻게 수집할 것인가?” 였다. 도커 자체 기능을 이용해서 허니팟 컨테이너 별로  가상 네트워크 인터페이스를 할당했지만, 그 이후로 어떻게 패킷을 뜰 수 있는지에 대해서는 생각이 필요했다.&lt;/p&gt;

&lt;h3 id=&quot;base-image&quot;&gt;Base Image&lt;/h3&gt;

&lt;p&gt;허니팟의 기본이 되는 이미지는 2개의 방법 (docker-compose 까지 해서 3개까지 생각했지만 기간 사정상 2개만 가지고 구현했다) 을 통해 제공할 수 있다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Official Docker Hub&lt;/li&gt;
  &lt;li&gt;Dockerfile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 정도면 사용자가 customize한 dockerfile을 제공할 수 있으며, 버전별로 기본 이미지를 docker hub에서 받아 사용할 수 있으니 괜찮을 것 같았다.&lt;/p&gt;

&lt;p&gt;이 부분은 딱히 문제는 없었지만 그래도 구현에 제일 고생했던 부분이라 한번 써봤다.&lt;/p&gt;

&lt;h3 id=&quot;collect-the-artifacts&quot;&gt;Collect the artifacts&lt;/h3&gt;

&lt;p&gt;아까 “허니팟 컨테이너 별로 네트워크 인터페이스가 있다” 라고 했다. 즉, 해당 네트워크 인터페이스를 모니터링 하면 모든 패킷을 빠짐없이 수집할 수 있다고 생각했다.&lt;/p&gt;

&lt;p&gt;근데.. 컨테이너별로 계속 수집기가 돌려면.. 스레드 관리가 되나?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;우리에겐 goroutine과 channel이 있사옵니다.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;진짜 이거 하나로 모든 걱정이 해결 되었다. (사실 docker의 pcapdump 쪽에서 메모리 이슈가 좀 있긴 했지만 이 문제도 너무 쉽게 해결했다. 나중에 기회가 된다면 패치한 부분을 PR 보내는 것도 괜찮은 것 같다)&lt;/p&gt;

&lt;p&gt;너무 쉽게 병렬 프로그래밍이 되었고, 상태 관리나 channel을 통해서 패킷 덤프의 종료 시점과 그 사실을 전달할 수 있었다.&lt;/p&gt;

&lt;h3 id=&quot;classification&quot;&gt;Classification&lt;/h3&gt;

&lt;p&gt;여튼, 이렇게 수집된 정보들을 가지고 공격이 발생했는지 여부를 확인해야 했다. 그런게 없다면 모든 아티팩트를 저장해야 하는데 이건.. 좀.. 용량 문제가 너무 심각했다.&lt;/p&gt;

&lt;p&gt;여러 방법을 생각하다가 아래와 같은 순서로 구분하기로 했다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;외부 트래픽이 발생하지 않은 경우 무조건 패스 (공격 시도 조차 없었으므로)&lt;/li&gt;
  &lt;li&gt;허니팟의 base image에서 수집한 정보와 비교하여 다른 점이 있는 경우 추가&lt;/li&gt;
  &lt;li&gt;프로세스의 변경점이 있는 경우 무조건 추가&lt;/li&gt;
  &lt;li&gt;인간의 분류&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;사실 4번이 제일 현실적인데 3번까지 하면 사실상 허니팟에서 공격 이벤트만 골라서 수집할 수 있었다.&lt;/p&gt;

&lt;p&gt;그리고 이 과정에서 추가적인 공격 정보에 대해 태깅 작업도 진행했다.&lt;/p&gt;

&lt;h3 id=&quot;compression&quot;&gt;Compression&lt;/h3&gt;

&lt;p&gt;이렇게 수집된 아티팩트를 사용자에게 제공하기 위해서는 (비용을 절감하기 위해) 압축은 필수다.&lt;/p&gt;

&lt;p&gt;대부분 압축에는 ZIP Compression 을 생각하지만, Go언어에서는 퍼포먼스 이슈가 있다. &lt;a href=&quot;https://github.com/golang/go/issues/20031&quot;&gt;#20031&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;처음에 이 이슈를 모르고 그냥 썼는데. 테스트 과정에서 비정상적으로 오래 걸려서 문제가 발생했다.&lt;/p&gt;

&lt;p&gt;해결법: tar을 애용하자. 훨씬 빠르다.&lt;/p&gt;

&lt;h2 id=&quot;operate&quot;&gt;Operate&lt;/h2&gt;

&lt;p&gt;1차 운영은 8월 초부터 9월 말까지 했다. 이 기간동안 운영한 허니팟의 종류는 아래와 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Nginx&lt;/li&gt;
  &lt;li&gt;SSH&lt;/li&gt;
  &lt;li&gt;Wordpress&lt;/li&gt;
  &lt;li&gt;RDP&lt;/li&gt;
  &lt;li&gt;MySQL&lt;/li&gt;
  &lt;li&gt;Elasticsearch&lt;/li&gt;
  &lt;li&gt;Redis&lt;/li&gt;
  &lt;li&gt;Telnet&lt;/li&gt;
  &lt;li&gt;Printer Server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;위의 허니팟에서 2달동안 총 196건의 공격 이벤트를 수집했다. (중복 제외)&lt;/p&gt;

&lt;p&gt;더 많은 공격 이벤트를 수집할 수 있었지만, 기술적 문제보단 서버의 자원이 부족했던 점이 너무 컸다.&lt;/p&gt;

&lt;p&gt;그리고 자동화된 봇 친구들의 엄청난 DDoS 공격 덕분에 서버 운영사에서 경고를 먹기도 했다. DDoS에 대한 비용을 더 지불한 덕분에 좋은 샘플을 잘 얻어가게 되었다.&lt;/p&gt;

&lt;p&gt;이 기간 이후에도 개선점을 반영하여 2차 운영을 12월 초 까지 했는데 이 기간동안 청구된 비용은 22만원 정도였다. (대부분 트래픽에 대한 비용이다)&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;이런 프로젝트를 하면서 생각보다 많은 공격이 있는 것을 확인했다.&lt;/p&gt;

&lt;p&gt;절대 포트는 함부로 여는 것이 아니다.. 진짜.. 봇들이 너무 많다. 아마 이런 봇들만 없어도 전 세계 트래픽의 상당수는 아낄 수 있을 것 같다. (아마 클라우드 비용도 많이 줄지 않을까 싶다)&lt;/p&gt;

&lt;p&gt;나름 해보면서 재밌었고, 무엇보다도 Docker API 사용하면서 이해가 안되는 부분은 Docker 코드를 뜯어보면서 이해하는 과정에서 내부적인 작동 구조에 대해 배운 것이 좋았다.&lt;/p&gt;

&lt;p&gt;현재 해당 프로젝트를 특별히 관리는 하고 있진 않다.&lt;/p&gt;

&lt;p&gt;아마 수집된 공격 행위 중 좀 괜찮은 애들을 선발해서 동아리 내부 워게임에 공유를 할 것 같다.&lt;/p&gt;

&lt;p&gt;끝.&lt;/p&gt;

&lt;h2 id=&quot;open-source&quot;&gt;Open Source&lt;/h2&gt;

&lt;p&gt;필요한 사람이 있을진 잘 모르겠지만 그래도 나름 간단하게 오픈소스를 해봤다.&lt;/p&gt;

&lt;p&gt;https://github.com/bunseokbot/Honey-V&lt;/p&gt;

&lt;p&gt;Golang을 사용했고, libpcap과 Docker가 설치된 환경에서만 작동한다.&lt;/p&gt;

&lt;p&gt;프로젝트의 이름은 Honey-V라고 지었는데 다들 Hyper-V 짝퉁이라고 한다. 지금 보니 그런 것 같다.&lt;/p&gt;

&lt;h2 id=&quot;citation&quot;&gt;Citation&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;BibTeX&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-latex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@inproceedings&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;kimcertplatform,
  title=&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;CERT Training Platform oevr the Event-Recordable Container&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;,
  author=&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;Namjun Kim, Chanmo Yang, Daebeom Cho, Seung Hyeon Geum, and Ki-Woong Park&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;,
  booktitle=&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;2020 ACM International Conference on Intelligent Computing and its Emerging Applications&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;,
  address=&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;GangWon, Republic of Korea&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;,
  month=&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;December&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;,
  year=&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;2020&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Namjun</name></author><category term="research" /><category term="docker," /><category term="training," /><category term="honeypot," /><category term="research" /><summary type="html">Containerized Honeypot Managment Framework: Honey-V</summary></entry><entry><title type="html">Orbit - Safebrowsing for Tor network</title><link href="http://iam.namjun.kim/research/2021/08/10/orbit-release/" rel="alternate" type="text/html" title="Orbit - Safebrowsing for Tor network" /><published>2021-08-10T00:00:00+00:00</published><updated>2021-08-10T00:00:00+00:00</updated><id>http://iam.namjun.kim/research/2021/08/10/orbit-release</id><content type="html" xml:base="http://iam.namjun.kim/research/2021/08/10/orbit-release/">&lt;h1 id=&quot;orbit-safebrowsing-for-tor-network&quot;&gt;Orbit: Safebrowsing for Tor network&lt;/h1&gt;

&lt;p&gt;그냥 문뜩 그런 생각이 들었다.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;왜 꼭 Tor network를 범죄와 연관을 지어서만 생각해야 할까?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;물론, 이전의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;darklight&lt;/code&gt; 에서도 봤다시피 많은 범죄 관련 데이터를 찾을 수 있긴 했다.&lt;/p&gt;

&lt;p&gt;그래도. 아주 그래도 극소수의 사용자들은 본래의 목적인 익명 보장과 검열 우회를 위해서 사용하진 않을까? 라는 생각을 했고 이런 곳에서 Tor 버전의 &lt;a href=&quot;https://safebrowsing.google.com/&quot;&gt;safebrowsing&lt;/a&gt; 같은 것이 있으면 좋지 않을까 해서 시작하게 되었다.&lt;/p&gt;

&lt;p&gt;뭐.. 원래 만들어 놨던 엔진의 구조 개선도 좀 필요해서 겸사겸사 이 프로젝트도 같이 껴서 하게 되었다.&lt;/p&gt;

&lt;h2 id=&quot;enhancement&quot;&gt;Enhancement&lt;/h2&gt;

&lt;p&gt;우선, 제일 필요한 구조 개선이 필요했다.&lt;/p&gt;

&lt;p&gt;&lt;del&gt;백수가 되면서&lt;/del&gt; 학교로 돌아 가면서 가장 큰 문제는 정기적인 수입이 없다는 점 이였다. 이 떄문에 원래 운영하던 개인 서버에 많은 변화가 생기게 되었고 최소한의 리소스를 효율적으로 사용해야 했다.&lt;/p&gt;

&lt;p&gt;이전에 만들어 둔 엔진은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Python&lt;/code&gt; 기반으로 작성된 아주 옛날 버전이였고, multiprocessing을 통해서 worker를 관리했다. 많은 사람들이 알다시피 Python 의 GIL 때문에 threading 방식이 우리가 아는 일반적인 thread가 아니기 때문에 여러개의 프로세스를 마치 thread처럼 띄워서 쓰는 방식을 사용했는데 나는 이 방식을 매우 싫었다.&lt;/p&gt;

&lt;p&gt;그러던 중에 아는 분으로부터 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go&lt;/code&gt; 언어에 대해 알게 되었다. 특히, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;goroutine&lt;/code&gt; 이라는 것을 알게 되었는데 이 부분을 듣자마자 도입하지 않을 수 없었다. 마치 내가 꼭 필요했던 부분을 해결 해 주는 친구가 나타난 기분이랄까?&lt;/p&gt;

&lt;p&gt;여튼, 그 이후로 go에 대해 공부하게 되었고 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;darklight&lt;/code&gt; 엔진을 개선시켜 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;orbit&lt;/code&gt; 이라는 새로운 엔진의 이름으로 만들었다.&lt;/p&gt;

&lt;h2 id=&quot;domains&quot;&gt;Domains&lt;/h2&gt;

&lt;p&gt;기존에 여러 프로젝트를 통해 모아뒀던 약 10만개의 Onion 도메인이 있었다.&lt;/p&gt;

&lt;p&gt;근데.. next generation onion services (“v3”) 가 나오게 되면서 많은 것들이 바뀌었다.. &lt;a href=&quot;https://gitweb.torproject.org/torspec.git/tree/rend-spec-v3.txt&quot;&gt;prop224&lt;/a&gt; 참고&lt;/p&gt;

&lt;p&gt;사실상 리소스 문제로 새로운 Onion 도메인 수집을 중단 상태로 두었지만, 이를 다시 살리고 업데이트 된 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt;를 추가해야 했다. 기존의 가지고 있던 도메인은 이미 죽은 경우가 많기 때문에..&lt;/p&gt;

&lt;p&gt;또한, 데이터베이스에 v2와 v3를 구분하는 컬럼을 추가해야 했다. 그래도 다행인 점은 그냥 길이를 통해 구분할 수 있어 그리 어려운 작업은 아니였으니 SQL 쿼리문을 짜서 돌리니 생각보다 금방 해결 되었다.&lt;/p&gt;

&lt;h2 id=&quot;orbit&quot;&gt;Orbit&lt;/h2&gt;

&lt;p&gt;Orbit 이라는 이름으로 정하게 된 이유는 아래와 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;이름이 마음에 들어서&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;뭔가 저궤도위성처럼 계속 모니터링 하는 이름과 비슷한 느낌이라서&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;이달의 소녀 (LOONA) 의 공식 팬덤 이름이라서&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;다들 다음 장 보기 전에 영상 하나 보고 가도록 하자.&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://youtu.be/e1427qPo30M&quot;&gt;&lt;img src=&quot;https://i.ytimg.com/vi/e1427qPo30M/hq720.jpg?sqp=-oaymwEcCOgCEMoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&amp;amp;rs=AOn4CLCN1Lnc6Jw7N5JAnDrja5r2qxYmWA&quot; alt=&quot;LOONA History&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;여튼 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;darklight&lt;/code&gt; 의 코드 에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Orbit&lt;/code&gt; 으로 이전하기 위해 큰 뼈대만 go로 포팅하고 세부적인 것을 새로 짜는 리모델링 수준의 작업을 진행 하였다.&lt;/p&gt;

&lt;p&gt;기존 상황보다 더 열악한 환경에서 운영해야 했고, 시간과 공간 복잡도를 최소화 하기 위해 로직의 많은 부분을 신경썼다.&lt;/p&gt;

&lt;h3 id=&quot;tor-proxy&quot;&gt;Tor Proxy&lt;/h3&gt;

&lt;p&gt;기존과 크게 달라진 부분은 없었다. 원래는 HTTP Tunneling 으로 좀 더 쉽게 proxy에 접근할 수 있도록 하려고 했는데 퍼포먼스 테스팅을 해보니 터널링 해 주는 과정이 별도로 필요해서 그냥 socks 프로토콜을 사용했다.&lt;/p&gt;

&lt;p&gt;그 대신 기존의 &lt;a href=&quot;https://hub.docker.com/r/bunseokbot/torproxy&quot;&gt;torproxy&lt;/a&gt; 이미지를 사용하진 않았다. 이 이미지는 너무 옛날에 작성한 이미지여서 불편한 점이 너무 많았다. (포트 지정이라던지.. 이미지의 크기라던지) 그땐 그게 큰 문제가 될지 몰랐는데 단 1메가라도 아끼고 싶어서 상대적으로 엄청 가벼운 alpine 이미지를 사용하였다.&lt;/p&gt;

&lt;p&gt;결과적으로 기존 75.27MB 의 이미지에서 20.7MB로 1/3 수준으로 줄일 수 있었다.&lt;/p&gt;

&lt;h3 id=&quot;interceptor&quot;&gt;Interceptor&lt;/h3&gt;

&lt;p&gt;이번 프로젝트의 주요 목표는 &lt;em&gt;“단 한번의 요청으로 최대한 많은 정보를 가져오자”&lt;/em&gt; 였다.&lt;/p&gt;

&lt;p&gt;이를 위해 Chrome 브라우저에서 제공하는 DevTool을 많이 이용 하였는데 해당 도구에서 가져올 수 있는 모든 정보에 대해 interface를 만든 뒤 실제로 작업할 때 해당 interface를 이용하는 방식을 택했다.&lt;/p&gt;

&lt;p&gt;생각보다 &lt;a href=&quot;https://developer.chrome.com/docs/devtools/&quot;&gt;DevTool&lt;/a&gt; 에서 제공하는 기능이 많아서 이를 이용해서 기존 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;darklight&lt;/code&gt; 에서는 제공하지 않았던 다양한 정보들을 가져올 수 있었다.&lt;/p&gt;

&lt;h3 id=&quot;patterns&quot;&gt;Patterns&lt;/h3&gt;

&lt;p&gt;탐지에 제일 중요한 부분이였다.&lt;/p&gt;

&lt;p&gt;내가 탐지 룰을 만들 수 있는 시간과 능력이 없기 때문에 외부에 공개된 패턴을 많이 가져다가 사용하였다. 거기에 일부 불필요한 패턴이나 중복되는 패턴을 제거하고 탐지명을 일원화 시키는 과정을 진행 하였다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Microsoft&lt;/li&gt;
  &lt;li&gt;Fireeye&lt;/li&gt;
  &lt;li&gt;Crowdstrike&lt;/li&gt;
  &lt;li&gt;US CERT&lt;/li&gt;
  &lt;li&gt;Rapid7&lt;/li&gt;
  &lt;li&gt;Trend Micro&lt;/li&gt;
  &lt;li&gt;Palo Alto&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;대표적으로 저 위에 있는 목록의 회사, 단체들에서 공개한 패턴을 사용했으며 그 이외에도 최대한 다양한 기업들의 패턴들을 사용하고자 노력했다.&lt;/p&gt;

&lt;p&gt;사실 원래는 내가 일일이 만들고자 했는데 시간적인 한계가 분명히 존재했고, 내가 만든 패턴으로 탐지하게 되면 그 신뢰성에 대해서도 문제가 생길 가능성이 있어 최대한 믿을 수 있는 그런 패턴들을 사용하기 위해 저런 방법을 사용했다.&lt;/p&gt;

&lt;p&gt;그래도 나중에 시간이 되고 관심이 더 생기면 내가 패턴을 넣을 수 있도록 미리 작업 자체는 해 두었다. (근데 할까?)&lt;/p&gt;

&lt;h3 id=&quot;coverage-test&quot;&gt;Coverage Test&lt;/h3&gt;

&lt;p&gt;이번 프로젝트부터 코드 커버리지를 검사하기 시작했다. 나중에 코드 베이스가 더 커지게 되거나 새로운 아이디어를 적용할 때도 수치로 딱 보여줄 수 있는 정보가 있어서 객관적인 지표로 사용할 수 있었다.&lt;/p&gt;

&lt;p&gt;원래는 개인 프로젝트에선 테스트 코드만 좀 짰었는데 이번에 처음으로 개인 프로젝트에도 커버리지를 도입하게 되었다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;\assets\images\posts\orbit-api\coverage.JPG&quot; alt=&quot;coverage&quot; /&gt;&lt;/p&gt;

&lt;p&gt;물론 처음에는 별로 안좋았지만 커버리지가 느는 것을 보면서 개인적으로 만족하기도 했다.&lt;/p&gt;

&lt;p&gt;저기서 보이는 커버리지 100%는 사실 테스트 목적으로 코드를 별로 안짜서 그런 것이다. 기능이 점점 늘어나니 반대로 줄어드는 모습을 보였지만 그래도 저 나머지 부분은 짤 수 없는 부분이라 어쩔 수 없는 부분이라..&lt;/p&gt;

&lt;p&gt;여튼 나중에 더 개선해서 커버리지를 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;70%&lt;/code&gt; 이상까지 늘려 볼려고 한다.&lt;/p&gt;

&lt;h3 id=&quot;performance&quot;&gt;Performance&lt;/h3&gt;

&lt;p&gt;위의 작업들을 모두 한 결과 기존 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;darklight&lt;/code&gt; 엔진 대비 새로 개발한 엔진에서 약 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;40%&lt;/code&gt; 의 스캔 속도 개선이 있었다.&lt;/p&gt;

&lt;p&gt;여러번 테스트를 더 해보면 다른 결과가 나올 수 있었지만 go에서 제공하는 channel, goroutine 같은 기능을 나름 잘 사용한 것 같아서 기분은 좋았다.&lt;/p&gt;

&lt;p&gt;그리고 기존에 도메인을 각 process로 넘기는 과정을 통해 뜻하지 않은 overhead가 있었지만 그 부분도 go언어로 넘어오면서 해결되었다.&lt;/p&gt;

&lt;h2 id=&quot;release&quot;&gt;Release&lt;/h2&gt;

&lt;p&gt;2021년 7월 18일 - Alpha version release (v0.5)&lt;/p&gt;

&lt;p&gt;2021년 7월 26일 - Initial release (v1.0)&lt;/p&gt;

&lt;p&gt;현재까지 1일 평균 3-4건 탐지 (최소 0건, 최대 6건) 건수를 보여주고 있다.&lt;/p&gt;

&lt;p&gt;지금 Orbit을 운영하는 환경은 vCPU 2Core에 RAM 4GB를 다른 서비스와 공유하여 사용하고 있다. (돈이 없어요)&lt;/p&gt;

&lt;p&gt;사실 높은 건 수를 보여주는건 아니지만 그래도 개인적으로 분석하는데에는 유의미한 건 수를 보여주고 있는 것 같다. (너무 많으면 오히려 오탐인지 확인하기 어렵다 ㅠ)&lt;/p&gt;

&lt;h2 id=&quot;open-source&quot;&gt;Open Source&lt;/h2&gt;

&lt;p&gt;저번 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;darklight&lt;/code&gt; 과 달리 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Orbit&lt;/code&gt; 은 오픈소스로 하진 않았다.&lt;/p&gt;

&lt;p&gt;아직 개선해야 할 점도 많고, 오픈소스 프로젝트로 관리 할 수 있을 자신이 없기 때문이다.&lt;/p&gt;

&lt;p&gt;대신, 간단하게 검색을 해보고 싶으신 분들을 위해 GraphQL API를 만들어서 공개하였다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GraphQL IDE: https://orbit.namjun.kim&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GraphQL: https://orbit.namjun.kim/graphql&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;이렇게 GraphiQL을 따로 오픈한 이유는 쉽게 테스트 해 보면 좋을 것 같아서이다.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;\assets\images\posts\orbit-api\graphql.JPG&quot; alt=&quot;graphql&quot; /&gt;&lt;/p&gt;

&lt;p&gt;대신, 서버 한계때문에 1시간에 100건 이상 요청할 수 없도록 제한을 걸어 두었다. 그냥 이런 것이 있다 정도만 보는 용도로 사용 하도록 하자.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2022년 1월 12일 추가&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;해당 API의 사용자 수가 많지 않아 &lt;strong&gt;2022년 1월 31일 (월) 부로&lt;/strong&gt; 서비스를 종료 하기로 결정 하였습니다.&lt;/p&gt;

&lt;p&gt;31일 이후에는 GraphQL IDE 대신 서비스 종료 안내 문구가 출력될 예정이니 사용에 참고 하시기 바랍니다.&lt;/p&gt;

&lt;p&gt;이제까지 서비스를 사랑해주신 러시아의 어느 웹 스캐너에게 감사의 말씀 올립니다.&lt;/p&gt;

&lt;h2 id=&quot;api-reference&quot;&gt;API Reference&lt;/h2&gt;

&lt;p&gt;Swagger를 통한 API Reference도 좋지만 이렇게 블로그 글에 담아 두는것도 좋을 것 같아서 한번 써 봤다.&lt;/p&gt;

&lt;h3 id=&quot;queries&quot;&gt;Queries&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;searchByUrl&lt;/strong&gt; (Onion URL을 검색하여 탐지 이력이 있는지 확인)&lt;/p&gt;

&lt;p&gt;Type: &lt;em&gt;Record!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Arguments&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Name&lt;/th&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;url&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;탐지 이력을 확인 하고자 하는 URL&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;downloadSample&lt;/strong&gt; (샘플 다운로드)&lt;/p&gt;

&lt;p&gt;Type: &lt;em&gt;Sample!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Arguments&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Name&lt;/th&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;id&lt;/td&gt;
      &lt;td&gt;Int&lt;/td&gt;
      &lt;td&gt;탐지 ID&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;objects&quot;&gt;Objects&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Record&lt;/strong&gt; (탐지 이력)&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Name&lt;/th&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Id&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;탐지 ID&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;time&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;탐지 시간&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;url&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;탐지된 Onion URL&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;tags&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;탐지명 (delimiter: “,”)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;sample_file&lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;샘플 파일의 SHA256 해시 값&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Sample&lt;/strong&gt; (샘플)&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Name&lt;/th&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;String&lt;/td&gt;
      &lt;td&gt;base64 인코딩 된 샘플 파일&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;생각보다 많은 샘플을 얻을 순 없었고, 분석하기엔 본업이 아니라 할 시간이 많지 않다.&lt;/p&gt;

&lt;p&gt;그래도 시간이 나거나 심심하다면 한번쯤은 분석해 봐도 좋을 것 같다.&lt;/p&gt;

&lt;p&gt;그리고 더 시간이 남는다면 저기 샘플 중 하나를 분석해서 올려 볼 수도 있다. (언젠가?)&lt;/p&gt;</content><author><name>Namjun</name></author><category term="research" /><category term="darkweb," /><category term="malware," /><category term="research" /><summary type="html">Orbit: Safebrowsing for Tor network</summary></entry></feed>