CSRF 토큰이란?

Django에서 CSRF(Cross-Site Request Forgery) 공격을 방지하기 위해 CSRF 토큰을 사용합니다. CSRF 공격은 인증된 사용자의 브라우저를 악의적으로 이용하여, 해당 사용자가 원하지 않는 요청을 서버에 보내는 공격입니다. CSRF 토큰은 이러한 공격을 방지하기 위해 사용되는 보안 메커니즘 중 하나입니다.

 

Django에서의 CSRF

Django에는 애플리케이션의 요청과 응답을 처리하는데 사용되는 다양한 middleware가 있다. 그중 csrf 공격을 막기위한 middleware는 "django.middleware.csrf.CsrfViewMiddleware"로 구현 되어있다. 이 미들웨어는 모든 POST 요청에 대하여 CSRF 토큰을 요청하고, 토큰이 없는 경우에는 요청을 실패 처리한다.

 

문제상황

DRF를 활용하여 API를 개발하고 테스트하는 과정에서 Chrome 브라우저에서 해당 api를 Front End를 붙여 테스트 하였을 때 CSRF토큰이 없어 403에러가 지속적으로 발생하는 문제가 발생했다.

 

트러블 슈팅

1. 해당 API에 적용된 인증 클래스들에 어떤것이 있는지 확인 -> 어떠한 인증 클래스도 적용되지 않았음

2. Settings.py에 "django.middleware.csrf.CsrfViewMiddleware"가 설정되어 있는지 확인 -> 설정되어있지 않음

3. 해당 api에 @csrf_exempt() decorator를 사용하여 csrf token 인증 무효화 -> 계속 csrf token missing 에러 발생

4. DRF의 DEFAULT_AUTHENTICATION_CLASS에 아무것도 없을 때 default로 다른 인증이 적용되지 않은 api에 대하여 Session 인증을 적용한다는 사실 확인 -> 브라우저에서 처음 서버와 통신할때 CSRF 토큰을 발급 받고 Session 인증시에 CSRF 토큰 유무를 확인한다는 것을 새롭게 알게됨

5.  DEFAULT_AUTHENTICATION_CLASS에 빈 리스트를 넣어 모든 api에 대한 설정을 바꾸는데 무리가 있다는 것을 고려하여 기존 settings.py를 수정하지 않음

6. Chrome이 아닌 다른 브라우저에서는 잘 작동하는 것을 확인 -> Chrome 브라우저에서 https 인증서에 문제가 있는 도메인에 대해서는 csrf토큰이 담기는 쿠키가 저장되지 않는다는 것을 알게됨

7. 사이트 설정을 변경하여 쿠키를 잘 저장하도록 한 후 문제해결

 

 

개발환경 : Mac OS Big sur

 

1. 설치하기>>

다양한 다운로드 방법이 있지만, 필자는 brew 를 이용하여 다운로드

brew install postgresql

2. 설치 확인>>

brew services start postgresql

위처럼 나오면 성공적으로 설치가 완료된 것이다.

3. python과 postgresql을 연동시켜주는 psycopg2 설치>>

pip install psycopg2

4. postgreSQL 접속>>

psql -d postgres

위 명령어를 통해 관리자 계정으로 접속>>

//접속후 #표시가 뜨면 정상적으로 접속된것입니다.
CREATE DATABASE backend OWNER '맥사용자명';
// backend라는 데이터베이스를 '맥사용자명'이라는 사용자로 만들어라

5. Django에 연결>>

django settings.py 의 DATABASES 항목을 수정하여 연결한후

>>python manage.py makemigrations
>>python manage.py migrate
>>python manage.py createsuperuser 

통해서 데이터베이스에 마이그레이트 하면 연결이 완료됩니다.

1:N(1 대 다 관계)

1 대 다 관계 예시) 포스팅과 댓글, 사용자와 포스팅, 사용자와 댓글

N측에 명시 해야 한다 

즉 예를 들어 포스팅 (Post) 와 댓글(Comment) 관계에서는 댓글에 명시해야한다.

 

ForeignKey(to, on_delete)

to : 클래스를 직접 지정

 

on_delete : Record 삭제시에 어떻게 할지 결정

옵션들:

CASCADE : 외래키로 참조하는 다른 모델의 레코드도 삭제

PROTECT : ProtectedError를 발생시키고 삭제를 방지한다

SET_NULL : null로 대체 필드에는 null=True 옵션 필수

SET_DEFAULT : 디폴트 값으로 대체, default 값 지정 필수

SET : 대체할 값이나 함수 지정. 함수의 경우 호출하여 리턴값을 사용

DO_NOTHING : 어떠한 액션도 하지 않음. DB에 따라서 오류가 발생할 수 도 있음

 

예시)

class Post(models.Model):
    title = models.CharField(max_length =100)
    content = models.TextField()
    is_public = models.BooleanField() # 공개여부

class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    message = models.Textfield()

Django orm으로 데이터 불러오기

 

다 -> 1로 접근시

댓글과 연결된 포스트 객체 = comment.post

 

1 -> 다로 접근시 (디폴트 reverse_name  = "모델명소문자"_set)

게시물과 연결된 댓글들 = post.comment_set.all()  == Comment.objects.filter(post=post)

*** 디폴트 속성명이 다른 앱과 동일하여 충돌이 날 경우

ex) blog 앱의 Post 모델, author = FK(User)

      shop 앱의 Post 모델, author = FK(User)

user.post_set 사용시 충돌 발생 -> makemigrations 할때 충돌

 

충돌을 피하는 방법)

1. 한쪽의 FK에 대해 reverse_name 포기 -> related_name = '+' 지정시 포기

2. 어느 한쪽의 혹은 모두의 FK의 reverse_name 편경

    - FK(User,.....,related_name = "blog_post_set")

    - FK(User,.....,related_name = "shop_post_set")

 

+ 추가적인 정보)

ForeinKey.limit_choices_to 옵션

 

ex)

Commen에 post FK 연결

post = models.ForeignKey(Post, on_delete=models.CASCADE, limit_choices_to = {'is_public' : True})

이때 댓글을 공개된 포스팅에만 댓글이 달릴 수 있다.

 

Primary Key : AutoField, --> 자동으로 생성된다.

문자열 : CharField, TextField, SlugField(문자열을 모두 소문자로 치환하고, 공백을 하이푼으로 연결함)

날짜/시간 : DateField, TimeField, DateTimeField, DurationFeild

참/거짓 : BooleanField, NullBooleanField

숫자 : IntegerField, SmallIntegerField, PositiveIntegerField, PositiveSmallIntegerfield, BigIntegerField, DecimalField, FloatField

파일 : BinaryField, FileField, ImageField, FilePathField

이메일 : EmailField

URL : URLField

UUID : UUIDField

아이피 : GenericIPAddressField

관계형 타입 : Foreignkey, ManyToManyField, OneToOneField

 

자주쓰는 ***Field(옵션)

blank : 장고 단에서 validation시에 empty 허용 여부 (디폴트: False)

null(DB옵션) : null 허용 여부 (디폴트: False)

db_index(DB 옵션) : 인덱스 필드 여부 (디폴트: False)

default : 디폴트 값 지정, 혹은 값을 리턴해줄 함수 지정

unique(DB옵션) : 현재 테이블 내에서 유일성 여부 (디폴트: False)

choices : select 박스 소스로 사용

validators : validators를 수행할 함수 다수 지정

verbose_name : 필트 레이블, 미지정시 필드명 사용

help_text : 필드 입력 도움말

 

Account.objects.all()을 사용할 때 원하는 슬라이싱을 할 수 있다.

ex Account.objects.all()[1:3:2]

 

class Account(models.Model):
    # 장고 유저와 내가 만든 모델 1대1 연결
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    nickname = models.TextField(max_length="20")
    position = models.TextField(max_length="20")
    
    def __str__(self):
        return self.nickname
    
    class Meta:
        ordering=['id'] # 오름차순 정렬
        #ordering=['-id] 내림차순

 

Model 클래스 안에 Meta 클래스를 선언하고 ordering에 칼럼을 명시하면 해당 칼럼을 기준으로 "-"를 붙이면 내림차순 붙이지 않으면 오름차순으로 객체들이 정렬되어 저장된다.

 

 

django-debug-toolbar를 활용한 sql 디버깅

$pip install django-debug-toolbar

django-debug-toolbar를 설치하고 자신의 django프로젝트의 settings.py의 INSTALLED_APPS에 'debug_toolbar'를 추가해주세요.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'main',
    'debug_toolbar',
]

혹시 자신의 settings.py와 다르더라도 괜찮습니다. 제가 개발하던 프로젝트에서 가져온것이니까요

 

동일하게 settings.py의 MIDDLEWARE에 'debug_toolbar.middleware.DebugToolbarMiddleware' 를 추가해 줍니다.

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

 

sql을 디버깅할 때 확인할 수 있는 정보는 매우 중요한 정보들이기 때문에 자신의 로컬 서버를 작동시켰을 때만 확인할 수 있도록 

settings.py에 INTERNAL_IPS를 추가하고 로컬서버의 ip인 '127.0.0.1'을 넣어줍니다.

INTERNAL_IPS=['127.0.0.1']

다음으로는, 각 앱에 만든 urls.py가 아닌 project 안에 있는 urls.py에

from django.conf import settings

settings를 import 해온 후,

if settings.DEBUG:
    import debug_toolbar
    urlpatterns += [
        path('__debug__/',include(debug_toolbar.urls)),
        ]

위의 코드를 넣어준다. 

 

이후 로컬에서 서버를 키게 되면 화면 우측에 아래와 같은 디버그 툴바가 생겨서 다양한 사항들을 확인할 수 있습니다.

 

* template에 <body> 태그가 없다면 디버그 툴바가 보이지 않습니다. 참고해주세요

 

django-debug-toolbar는 ajax 통신 내역은 디버깅할 수 없습니다. ajax 통신은 django-querycount 패키지를 사용하면 디버깅할 수 있습니다.

 

 

'Django' 카테고리의 다른 글

[Django] Postgresql 연동하기  (0) 2021.02.01
[Django] Django ORM - ForeignKey  (0) 2021.01.08
[Django] django ORM 정리  (0) 2021.01.08
[Django] Django Admin 새롭게 안 사실 정리  (0) 2021.01.08
[Django] Django 개발 환경 세팅  (0) 2020.12.29

Django model을 Admin에 등록할 때, 

 

@admin.register(Account)
class AccountAdmin(admin.ModelAdmin):

위처럼 등록하고 

    list_display = ['pk','user','nickname','nickname_length','position']
    list_display_links = ['nickname']
    search_fields = ['nickname']
    list_filter = ['position']
    def nickname_length(self, account):
        return f"{len(account.nickname)} 글자"

위처럼 Django에서 기본제공하는 admin 페이지를 커스텀 할 수 있다.

list_display 는 admin에서 세부 칼럼들을 추가하여 어드민에서 한눈에 레코드를 확인할 수 있게 한다

list_display_link 는 각 객체들의 세부사항을 볼 수 있는 링크를 걸어줄 칼럼을 설정한다

search_fields에 명시한 칼럼을 기준으로 레코드중에서 검색할 수 있게 한다.

list_filter는 위의 어드민 캡쳐사진 우측에 필터 항목을 생성하여 명시한 칼럼의 종류들로 빠르게 필터링하는 도구를 제공한다. boolean 필드가 있는 객체에서 유용할 것 같다.

 

model을 이처럼 등록을 하게되면 좋은점은 만들고 싶은 기능을 커스텀하여 만들고 어드민에 활용할 수 있게 하는 장점이 있다.

    def nickname_length(self, account):
        return f"{len(account.nickname)} 글자"

이와같은 함수를 선언하여 admin에서도 관리자의 필요에 맞춘 데이터를 가공하여 전달할 수 있게한다.

 

'Django' 카테고리의 다른 글

[Django] Postgresql 연동하기  (0) 2021.02.01
[Django] Django ORM - ForeignKey  (0) 2021.01.08
[Django] django ORM 정리  (0) 2021.01.08
[Django] Django Model 새롭게 알게된 것들 정리  (0) 2021.01.08
[Django] Django 개발 환경 세팅  (0) 2020.12.29

본격적인 크롤러를 개발하기 전에, 크롤러를 올릴 Django 웹 플랫폼이 필요했다.

 

필자의 개발환경은 Mac OS이고 IDE는 Visual Code 사용하여 개발하였습니다.

 

 

로컬에 Django를 install 하지 말고 먼저 독립된 개발환경을 위한 Python 가상 환경을 조성하자

 

터미널에서 자신이 작업할 폴더를 생성하고, 해당 디렉토리 위치에서, 아래와 같은 커맨드를 통해 가상 환경을 만들 수 있다.

python3 -m venv myvenv

python을 사용하는데, -m 만들꺼다, venv(virtual environment) 가상 환경을, myvenv라는 이름을 가진. 이라는 뜻으로 해석

 

잠시 기다린 후, 자신이 만든 폴더에 이런 폴더가 만들어지면 가상 환경이 만들어진 것이다. 

 

가상 환경을 만들었으니, 이제 가상 환경을 켜보자

source myvenv/bin/activate

터미널에서 아래와 같은 커맨드를 사용하면 

현재 경로 좌측에 (괄호) 안에 가상 환경 이름이 보이는데 이를 통해 가상 환경이 켜져 있다는 것을 알 수 있다.

가상 환경을 켰다면 이제 독립된 개발환경이 설정이 된 것이다. 그럼 필요한 python package를 설치해 보자.

 

Django를 설치하는 명령어는 다음과 같다.

pip3 install django

나의 개발환경에서는 python2와 python3의 명령어가 통합되지 않아 python3의 package installer 인 pip3를 사용한다.

Django파일들을 모두 다운로드 받는데 시간이 좀 걸린다. 침착하게 기다리면

Successfully installed 라는 답변을 들을 수 있다.

 

그럼 이제 Django 프로젝트를 시작해 보자

django-admin startproject myproject

    django 관리자야, 프로젝트 시작하자, myproject라는 이름의

 

위와 같은 커맨드를 통해서 myproject라는 디렉토리가 생기면서 manage.py 파일이 들어있는 빈 프로젝트가 생성된다.

 

이렇게 django 기본 플랫폼이 완성이 되고, 이 위에 기능별 app들을 붙여나가면 된다.

다음 포스팅부터는 메인화면을 렌더링하는 기능을 시작으로 다양한 기능을 붙여 보겠다.

+ Recent posts