2020년 3월 25일 보안정보 스크래핑

2020년 3월 25일 보안정보 스크래핑 3월 25일 보안정보 스크래핑 ==================================================================== + 주요 취약점 - 메일전송 프로토콜을 이용한 원격 명령어 실행 주의 권고 외 1건 1. 메일전송 프로토콜을 이용한 원격 명령어 실행 주의 권고 최근 OpenSMTPD* 취약점이 발견되는 등 메일전송 프로토콜에서 원격 명령어 실행이 가능하여 주의를 권고함 공격자는 취약점을 악용하여 피해를 발생시킬 수 있으므로, 해결방안을 참고하여 조치 필요 - https://www.krcert.or.kr/data/secNoticeView.do?bulletin_writing_sequence=35302 2. Django 제품 SQL Injection 취약점 보안 업데이트 권고 최근 Django*에서 SQL Injection취약점(CVE-2020-9402)을 악용할 수 있는 개념증명코드(Proof of concept, PoC)가 인터넷상에 공개되어 사용자의 보안 업데이트 필요 - https://www.krcert.or.kr/data/secNoticeView.do?bulletin_writing_sequence=35301 ==================================================================== + 취약점 - Apple Safari 취약점 1. Apple Safari 취약점 Apple Safari security bypass CVE-2020-3885 - https://exchange.xforce.ibmcloud.com/vulnerabilities/178339 Apple Safari security bypass CVE-2020-3887 - https://exchange.xforce.ibmcloud.com/vulnerabilities/178338 Apple Safari inform...

Django 3.0 ASGI > 실시간 TodoApp 만들어보기 - 1

Django 3.0 ASGI > 실시간 TodoApp 만들어보기 - 1

카테고리 없음

Django 3.0 ASGI > 실시간 TodoApp 만들어보기 - 1

1. 설치(Channels 공식사이트를 참고했다.)

https://channels.readthedocs.io/en/latest/tutorial/part_1.html

cmd > cd { project Folder} django-admin startproject config . django-admin startapp todoapp python -m venv myvenv cd myvenv cd scripts activate.bat cd .. cd .. (Project Path) python -m pip install --upgrade pip pip install django channels channels_redis code . (in vscode)

나의 경우 보통 초기 세팅은 cmd로 하는 편이다.

cmd를 통해 구성하고 싶은 폴더로 들어가서

django-admin startproject config .

을 통해 현재 폴더에 config라는 프로젝트를 생성한다.

그리고 django-admin startapp todoapp

을 통해 앱을 생성한다.

그 뒤,

python -m venv myvenv

를 통해 myvenv라는 가상환경을 만들고,

cd myvenv

cd scripts

activate.bat

를 통해 가상환경을 실행한 뒤, cd .. 으로 빠져나온다.

대충 이런 식으로 설정하고

python -m pip install --upgrade pip

를 통해 pip를 업그레이드 시키고,

pip install django channels channels_redis

를 통해 한 방에 원샷으로 장고, 채널스, 채널스레디스까지 모두 일괄 설치해준다.

그 다음에서야 Visual Code를

code . 이라는 명령어로 실행시킨다.

2. 세팅(초기 설정)

제일 먼저 필요한 것이 세팅이다.

# in settings.py # ... ALLOWED_HOSTS = ['*'] # ... # ... INSTALLED_APPS = [ # ... 'todoapp', # 추가 'channels', # 추가 ] # ... TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR,'templates')], # 추가 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] # ... WSGI_APPLICATION = 'config.wsgi.application' ASGI_APPLICATION = "config.routing.application" # 추가 CHANNEL_LAYERS = { # 추가 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': { "hosts": [('127.0.0.1', 6379)], }, }, } # .. STATIC 설정은 패스..

그리고 settings.py 옆에 routing.py를 작성해준다.

# config.routing # filepath : config/routing.py from channels.routing import ProtocolTypeRouter application = ProtocolTypeRouter({ # (http->django views is added by default) })

setting.py 중

ASGI_APPLICATION = "config.routing.application" # 추가

이 부분은 바로 위에 작성한 routing.application을 일컫는거다.

3. TodoApp 만들기

대충 이런 느낌의 체크박스 형식이면 좋을것 같다.

3-1 TodoApp 모델 만들기

장고는 뭘 하려면 제일 먼저 세팅, 그다음 모델, 그다음 뷰, URL 세팅, 그리고 나머지 디테일은 html에서 처리해야 하는것 같다.

제일 먼저 만들어야 할 것은 모델이다.

모델은 그냥 간단히 만들어보았다.

# todoapp.models from django.db import models # Create your models here. class ToDoAppModel(models.Model): main_text = models.TextField(verbose_name="메인 텍스트") is_checked = models.BooleanField(default=False) # todoapp.admin # admin.py ## admin에 위의 모델을 등록하자. from django.contrib import admin from todoapp.models import ToDoAppModel # Register your models here. admin.site.register(ToDoAppModel)

main_text가 있고, 위의 느낌의 check 박스용 체크 값이 있다. 디폴트 값이 있으므로, 그냥 따로 지정하지 않아도 된다.

3-2 Form 만들기

그다음은 뷰에 쓸 form 부터 만들어야겠다.

아무래도 귀찮으니까..

# todoapp.forms # 작성 from django import forms from todoapp.models import ToDoAppModel class TodoAppForm(forms.ModelForm): class Meta: model = ToDoAppModel fields = ('main_text',)

일단은 뷰에서 써보자.

3-3 View 만들기

index 페이지부터 띄어보자.

# views.py # todoapp.view from django.shortcuts import render from .forms import TodoAppForm # Create your views here. def index_view(request): form = TodoAppForm() context = { 'form':form, } return render(request,"todoapp/index.html",context)

파일 위치를 잘 해야하한다.(폴더를 새로 만들어서 해야한다.)

Document {% csrf_token %} {{ form.as_p }}

그 다음 url 을 연결해주자.

# urls.py # config.urls from django.contrib import admin from django.urls import path from todoapp.views import index_view urlpatterns = [ path('admin/', admin.site.urls), path('todoapp/', index_view,name="index"), ]

python manage.py makemigrations

python manage.py migrate

python manage.py createsuperuser

해준 뒤, 뙇

스무스하다. 아직까진.

물론 아직 저장이 되진 않는다.

모델 내 저장이 되는지 확인해보려면

shell을 통해 확인해볼 수 있다 아까 등록했었던 admin을 통해서도 확인 가능하다.

3-4 form 데이터 저장 및 저장 값 불러오기

이제 폼을 submit 하면서, 데이터를 저장하게끔 해보자.

아 그리고, 페이지 내에 결과값 들도 보여주도록 해보자.

# todoapp.views # views.py # 함수 수정 from .forms import TodoAppForm from .models import ToDoAppModel from django.shortcuts import render def index_view(request): form = TodoAppForm() # 폼을 불러오고 todo = ToDoAppModel.objects.all() # 모델 내 데이터들을 불러온다. if(request.method == 'POST'): # 작성하는 부분 form = TodoAppForm(request.POST or None) # request가 POST면, 폼 안에 요청값을 넣는다. if(form.is_valid()): form.save() # 폼을 저장하고 form = TodoAppForm() # 폼을 전부 비운다. context = { 'form':form, 'todo':todo, } return render(request,"todoapp/index.html",context)

todo 리스트를 출력하려면,

index.html 페이지도 손을 봐줘야한다.

Document .list li{ display: inline; font-size: 40px; vertical-align: middle; } .list input{ vertical-align: middle; } .list li .checked{ text-decoration: line-through; } {% csrf_token %} {{ form.as_p }} {% for content in todo %} {{ content.main_text }} {% endfor %}

(CSS도 조금 손봐줬다. Edit은 하려다가 포기했다.)

대충 이런 모양새가 나온다.

3-5 Delete버튼 작동

자 이제 저 Delete 버튼을 작동시킬 수 있게 해보자.

# todoapp.views # views.py 추가 from django.http import HttpResponseRedirect # 추가 # ... # ... def deletetodo(request, todo_id ): # todo_id 를 URL로부터 받아서 삭제토록 한다. item_to_delete = ToDoAppModel.objects.get(id=todo_id) item_to_delete.delete() return HttpResponseRedirect('/todoapp/') # config.urls # urls.py 추가 from todoapp.views import index_view, deletetodo # 추가 urlpatterns = [ path('admin/', admin.site.urls), path('todoapp/', index_view,name="index"), path('deletetodo//', deletetodo), # 추가 ]

url까지 한 방에 설정한다.

이 뜻은

todo_id로 View 단에 넘기되, 이 형식은 int로 하겠다. 라는 뜻으로 이해하면 되겠다.

그리고 index.html에 스크립트 부분을 조금 추가해준다.

Document .list li{ display: inline; font-size: 40px; vertical-align: middle; } .list input{ vertical-align: middle; } .list li .checked{ text-decoration: line-through; } {% csrf_token %} {{ form.as_p }} {% for content in todo %} {{ content.main_text }} {% endfor %} function href_location(id){ current = window.location.href window.location.href = '/deletetodo/' + id + '/' }

잘 들어가있다.

3-6 Todoapp 중간완성

자, 일단 간단한 TodoApp을 만들었다.

언뜻 실시간 처럼 보이지만,

실시간이 아니다.

4-1 Channels routing 설정(공식홈페이지를 참고하였음)

# todoapp.routing # routing.py from django.urls import re_path, path from . import consumer websocket_urlpatterns = [ path('ws/todoapp/', consumer.TodoConsumer), ]

todoapp 내에 routing.py 를 만든다

routing 은 url과 같은 기능을 한다.

consumer는 view와 같은 기능을 한다.

그래서, ws/todoapp/ 이라는 경로로 소켓이 접근 시 TodoConsumer를 실행시켜라

라는 의미와 같다.

이제 config.routing 내의 내용을 수정해보자.

# config.routing # routing.py from channels.routing import ProtocolTypeRouter from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter import todoapp.routing application = ProtocolTypeRouter({ 'websocket': AuthMiddlewareStack( URLRouter( todoapp.routing.websocket_urlpatterns ) ), })

대략적인 그림설명이다.

이제 위에서 정의한 TodoConsumer를 만들어보자.

4-2 TodoConsumer 작성

공식설명 내 기본틀

Function description def connect(self) 연결 시 호출되는 함수 def disconnect(self, close_code) 연결 해제시 호출되는 함수 def receive(self, text_data) 받을 수 호출되는 함수

정도다

이제 이걸 입맛에 맞게 바꿔보려 한다.

from asgiref.sync import async_to_sync from channels.generic.websocket import WebsocketConsumer import json class TodoConsumer(WebsocketConsumer): def connect(self): print('someone connected!') #'/ws/todoapp/' self.room_group_name = 'todousers' async_to_sync(self.channel_layer.group_add)( self.room_group_name, self.channel_name ) self.accept() def disconnect(self, close_code): async_to_sync(self.channel_layer.group_discard)( self.room_group_name, self.channel_name ) # Receive message from WebSocket def receive(self, text_data): text_data_json = json.loads(text_data) message = text_data_json['message'] print('receive_message') self.send(text_data=json.dumps({ 'message': message })) # Send message to room group async_to_sync(self.channel_layer.group_send)( self.room_group_name, { # running class below 'type': 'todo_message', 'message': message } ) # Receive message from room group def todo_message(self, event): message = event['message'] print('todo_message') # Send message to WebSocket self.send(text_data=json.dumps({ 'message': message }))

그리고, 정작 컨슈머를 만들어놓고 쓰질 못한다면 의미가 없다.

그래서 index.html에 사용하게끔 할 함수를 추가하고, 구동해본다.

Document .list li{ display: inline; font-size: 40px; vertical-align: middle; } .list input{ vertical-align: middle; } .list li .checked{ text-decoration: line-through; } {% csrf_token %} {{ form.as_p }} {% for content in todo %} {{ content.main_text }} {% endfor %} function href_location(id){ current = window.location.href window.location.href = '/deletetodo/' + id + '/' } var todoSocket = new WebSocket( 'ws://' + window.location.host + '/ws/todoapp/'); todoSocket.onmessage = function(e) { var data = JSON.parse(e.data); console.log(data); // var message = data['message']; }; todoSocket.onclose = function(e) { console.error('Chat socket closed unexpectedly'); }; function todoSocket_test(){ todoSocket.send(JSON.stringify({ 'message': 'testtest!!' })); }

이처럼 연결도 정상적으로 진행되고, 메세지도 잘 보내진다.

하지만, 메세지가 두 번씩 보내지는데 이 이유는 무엇일까?

프론트단(html)에서 만든 소켓은 요청(request)를 한다. (이러이러한 자료를 보낸다.)

그리고, 서버단(Consumer)에서는 이 요청을 받는다.(receive)

하기 때문에, receive가 먼저 실행된다.

그 뒤 설정한 그룹에 속한 이들에게 메세지를 전송하게 되며,

todo_message(self, evnet)를 호출하게 된다.

이 todo_message는 따로 분기하여,

삭제할건지 내용을 추가할 건지 따로 정의해 분기시킬 수 있게끔 해준다.

결국, 위에서 send가 두번 쓰였기 때문에 사용자가 두 번의 요청을 받는 것인데,

이를 구분해보자면,

send 종류 설명 self.send 전체 채널 이용자에게 전달 async_to_sync(self.channel_layer.group_send) 명명한 그룹 이용자에게만 전달

하는 역할을 한다.

지금은 채널 그룹을 하나만 쓸 예정이기에 뭘 써도 사실상 관계는 없어보인다.

이제 [추가, 삭제] 와 관련한 consumer를 작성해보자.

from http://nadure.tistory.com/14 by ccl(A)

댓글

이 블로그의 인기 게시물

엑스브레인(XBrain) 기업 정보

django 설치 방법

[aws] deploy Angular app with s3 | AWS S3로 angular 앱 배포하기