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토큰이 담기는 쿠키가 저장되지 않는다는 것을 알게됨
CPU가 기계어 명령을 수행하기 위해 프로세스의 논리적 주소가 실제 물리적 메모리의 어느 위치에 매핑되는지 확인하는 과정
메모리 단편화
내부 단편화 : 프로세스가 필요한 공간 보다 더 많은 메모리가 할당되어 메모리가 낭비되는 상황
외부 단편화 : 메모리 중간중간에 사용하지 않는 공간이 생겨서 더이상 사용할 수 없는 상황
Q1. Paging이란 무엇인가요?
paging 기법은 process의 메모리 공간을 동일한 크기의 page 단위로 나누어 물리적 메모리의 서로 다른 위치에 page들을 저장하는 메모리 관리 기법입니다. paging 기법에서는 물리적 메모리를 page와 같은 크기의 frame으로 미리 나누어둡니다.
Paging 기법에서는 주소 바인딩을 위해 모든 프로세스가 각각의 주소 변환을 위한 page table을 갖습니다.
꼬리 질문 1 : Paging 기법 사용시 발생할 수 있는 메모리 단편화 문제에 대해 설명하시오.
paging기법에서는 process의 논리적 주소 공간과 물리적 메모리가 같은 크기의 page 단위로 나누어지기 때문에 외부 단편화 문제는 발생하지 않습니다. 하지만 process 주소 공간의 크기가 page 크기의 배수라는 보장이 없기 때문에 프로세스의 주소 공간 중 마지막에 위치한 page에서는 내부 단편화 문제가 발생할 가능성이 있습니다.
Q2. Segmentation에 대해서 설명해주세요.
Segmentation 기법은 process가 할당받은 메모리 공간을 논리적 의미 단위(segment)로 나누어 연속되지 않는 물리 메모리 공간에 할당될 수 있도록 하는 메모리 관리 기법입니다. 일반적으로 메모리 영역을 Code, Data, Heap, Stack 등의 기능 단위로 segment를 정의하는 경우가 많습니다. Segmentation 기법에서는 주소 바인딩을 위해 모든 프로세스가 각각의 주소 변환을 위한 segmentation table을 갖습니다.
꼬리 질문 1 : Segmentation의 메모리 단편화 문제에 대해 설명하시오.
Segmentation 기법에서는 Segment의 크기만큼 메모리를 할당하므로 내부 단편화 문제는 발생하지 않습니다. 하지만 서로 다른 크기의 segment들이 메모리에 적재되고 제거되는 일이 반복되면 외부 단편화 문제가 발생할 가능성이 있습니다.
꼬리 질문 2 : Segmentation과 paging의 차이는 뭔가요?
Paging은 일정한 크기의 단위로 나누어 할당을 하는데 반해 Segmentation 기법에서는 code, data, heap, stack등의 기능단위로 물리 메모리를 할당하는 기법입니다. paging의 경우 내부 단편화의 문제가 발생할 수 있는데, 이에 반해 segmentation은 외부 단편화의 문제가 발생할 수 있습니다.
꼬리 질문 3 : Paged segmentation 기법에 대해 설명하시오.
paged sementation이란 segmentation을 기본으로 하되 이를 다시 동일한 크기의 page로 나누어 물리 메모리에 할당하는 메모리 관리 기법입니다. 즉, 프로그램을 의미하는 단위의 segment로 나누고 개별 segment의 크기를 page의 배수가 되도록 하는 방법입니다.
이를 통해 segmentation 기법에서 발생하는 외부 단편화 문제를 해결함과 동시에 segment 단위로 processrksdml rhddbsk process내의 접근 권한 보호가 이루어지도록 하여 paging 기법의 단점을 해결 합니다.
Q3. 가상 메모리에 대해서 설명해주세요
가상메모리(virtual memory)는 실제의 물리 메모리 개념과 개발자 입장의 논리 메모리 개념을 분리한 것입니다. 이렇게 함으로써 개발자가 메모리 크기에 관련한 문제를 염려할 필요 없이 쉽게 프로그램을 작성할 수 있게 해줍니다. 운영체제는 가상 메모리 기법을 통해 프로그램의 논리적 주소 영역에서 필요한 부분만 물리적 메모리에 적재하고 직접적으로 필요하지 않은 메모리 공간은 디스크(Swap 영역)에 저장하게 됩니다.
요구 페이징(demend paging)
당장 사용될 주소 공간을 page단위로 메모리에 적재하는 방법을 요구 페이징(demend paging)이라고 합니다. 요구 페이징 기법에서는 특정 page에 대해 cpu의 요청이 들어온 후에 해당 page를 메모리에 적재합니다. 당장 실행에 필요한 page만을 메모리에 적재하기 때문에 메모리 사용량이 감소하고 프로세스 전체를 메모리에 적재하는 입출력 오버헤드도 감소하는 장점이 있습니다.
요구 페이징 기법에서는 유효/무효 비트(valid/invalid bit)를 두어 page가 메모리에 존재하는지 표시하게 됩니다.
Page Fault
CPU가 무효 비트로 표시된 page에 엑세스하는 상황을 page fault라고 합니다.
CPU가 무효 page에 접근하면 주소 변환을 담당하는 하드웨어인 MMU가 page fault trap을 발생시키게 되고 다음과 같은 순서로 page fault를 처리하게 됩니다.
page fault가 발생하면 요청된 page를 디스크에서 메모리로 가져옵니다. 이때 물리적 메모리에 공간이 부족할 수 있습니다. 그럴 경우에는 메모리에 올라와있는 page를 디스크로 옮겨서 메모리 공간을 확보해야 합니다. 이것을 페이지 교체(page replacement)라고 하고 어떤 page를 교체할 것이냐를 결정하는 알고리즘이 page교체 알고리즘입니다.
Hash table은 효율적인 탐색을 위한 자료구조로써 key-value쌍의 데이터를 입력받습니다. Hash function h에 key값을 입력으로 넣어 얻은 해시값을 h(k)를 위치로 지정하여 key-value 데이터 쌍을 저장합니다. 저장,삭제, 검색의 시간복잡도는 모두 O(1)입니다.
Collision
충돌을 피하는 방법
Open addressing - 미리 정한 규칙에 따라 비어있는 slot 찾기
Linear Probing - 일정한 값만큼(+1,+2,+3,...) 건너 뛰어 비어있는 slot에 저장
Quadratic Probing - 제곱수(+1,+4,+9,...)로 건너 뛰어 비어 있는 slot에 저장
Double Hashing - 위 두 방법은 이동 폭이 일정하기 때문에 클러스터링 문제가 발생할 수 있어서 해싱함수를 2개를 사용하는 방식. 하나의 해시는 최초 해시값을 얻을 때 사용하고, 다른 하나의 해시는 충돌이 일어날 때 탐사 이동폭을 얻기위해 사용.
Separate Chaining
Linked list 또는 Tree를 이용하여 collision 해결
삽입- O(1)
검색과 삭제는 기본적으로 O(1)이지만 최악의 경우 O(n)도 가능
BST를 활용하면 최악의 경우를 O(logn)으로 낮출 수 있다.
코딩테스트 활용
Python의 경우 Dictionary 자료형 활용
Hash Table은 메모리를 활용하여 시간복잡도를 줄일 때 사용
Dictionary의 "key in {}"를 잘 사용하여 시간복잡도를 줄여보자
예제 문제
def longestConsecutive(self, nums: list [int]) -> int:
longest = 0
dictionary = {}
for n in nums:
dictionary[n] = True
for n in dictionary:
cnt = 1
target = n + 1
while target in dictionary:
cnt += 1
target += 1
longest = max(longest, cnt)
return longest