이 장에서 다룰 내용
•
보안에 대해 전체적으로 이해하기
•
위협 모델 활용하기
•
SQL 삽입, CSRF, XSS, 오버플로와 같은 일반적인 보안 함정 방지하기
•
공격자의 공격력을 줄이는 기술 알아보기
•
암호를 올바르게 저장하기
•
트로이 목마의 예처럼 보안은 인간의 심리에 대한 넓고도 깊은 용어이다.
◦
이것이 바로 우리가 확장시켜야할 새로운 관점이다. 보안은 소프트웨어나 정보에만 국한된 것이 아니라 사람과 환경에 대한 것이다.
1. 해커를 넘어서
•
보안은 테스트와 같이 서비스, 데이터, 비즈니스의 신뢰성을 나타내는 부분 집합이다.
•
보안을 신뢰성의 관점에서 보면 보안과 관련된 결정을 내리기가 더 쉬워진다.
•
보안과 관련된 결정을 신뢰성에 대한 기술 부채로 보는 것은 전반적으로 우리의 삶을 최적화하는 데 도움이 된다.
2. 위협 모델링
•
위협 모델이란 보안 측면에서 무엇이 잘못될 수 있는지를 명확히 이해하는 것이다.
◦
위협 모델의 목표는 수행해야 하는 보안 조치의 우선순위를 정하고, 비용을 최적화하며, 효율성을 높이는 것이다.
•
실제 위협 모델링은 행위자, 데이터 흐름, 신뢰 경계를 분석하는 것이 포함된다.
1. 주머니에 들어갈 만큼 작은 위협 모델
•
모든 애플리케이션에 대해 위협 모델링을 해야 하는 것은 아니다. 하지만 최소한의 보안 코드를 작성해야 한다.
•
기본으로 애플리케이션을 위한 소형 위협 모델이 필요하다. 소형 위협 모델은 다음과 같은 요소를 포함한다.
◦
애플리케이션의 자산
▪
기본적으로 소스 코드, 설계 문서, 데이터베이스, 개인 키, API 토큰, 서버 구성, 넷플릭스 시청 내역 등 손실되거나 유출되고 싶지 않은 모든 항목이 자산이다.
◦
자산을 저장하는 서버
▪
모든 서버는 일부 사용자 그룹에 의해 액세스되고, 일부 다른 서버에 액세스한다. 잠재적인 문제를 파악하려면 이러한 관계를 아는 것이 중요하다.
◦
정보 민감성
▪
여러분은 스스로에게 “만약 이 정보가 공개된다면 얼마나 많은 사람과 기관이 피해를 입을 것인가?”, “이로 인한 잠재적인 피해가 얼마나 심각한가?”, “정보 유출로 인해 감옥에 들어갔던 사람이 있었나?”라는 몇 가지 질문을 던짐으로써 이를 평가할 수 있다.
◦
리소스에 대한 액세스 경로
▪
애플리케이션이 데이터베이스에 액세스할 수 있다.
•
접근할 수 있는 다른 방법이 있는가?
•
누가 접근할 수 있는가?
•
접근 권한이 있는 사람이 얼마나 안전한가?
•
누군가가 그들을 속여서 데이터베이스에 접속하면 어떻게 될까?
•
그들은 ︎︎︎︎︎︎︎︎︎︎을 실행하여 프로덕션 데이터베이스를 쉽게 삭제할 수 있는가?
•
정해진 사람만 소스 코드에 액세스할 수 있는가?
•
그렇다면 소스 코드에 액세스할 수 있는 모든 사용자는 프로덕션 데이터베이스에도 효과적으로 액세스할 수 있다.
•
이러한 정보를 가지고 기본 위협 모델을 종이에 그려볼 수 있다. 애플리케이션이나 웹 사이트를 사용하는 모든 사용자를 위한 위협 모델은 위 그림와 같다.
•
시도 가능한 위협이 있으면 그 구멍을 메우는 식으로 해당 위협을 해결하기 시작할 수 있다.
◦
웹 앱이나 API가 인기 있는 후보 중 하나이기 때문에 웹 애플리케이션을 작성하는 동안 보안 코드를 작성하는 방법을 아는 것은 중요하다.
3. 웹 앱을 안전하게 작성하라
1. 보안을 염두에 두고 설계하라
•
보안은 나중에 개선하기가 어렵다. 따라서 애플리케이션을 설계할 때부터 보안을 고려하는 것이 중요하다.
1.
글로 적거나 머리 속에 있는 위협 모델을 검토하라.
•
위험, 지금 보안을 유지하는 데 드는 비용, 나중에 보안을 유지하는 데 드는 비용을 파악하라.
2.
앱에 필요한 보안 암호(데이터베이스 비밀번호, API 키)를 저장할 위치를 결정하라.
•
암호를 어렵게 만들어라. 모든 사용자가 소스 코드에 접근할 수 있다고 가정하라.
3.
최소한의 권한을 주도록 설계하라.
•
이상적으로 어떤 코드든 작업을 수행하는 데 필요한 것보다 더 많은 권한을 요구하지 말아야 한다.
•
예를 들어 여러분의 앱으로 정기적인 데이터베이스 복구 작업을 예약할 필요가 없다면 앱에 데이터베이스 관리자 권한을 주지 마라.
•
일부 작업만을 위해서 더 높은 권한이 필요한 경우에는 별도의 앱과 같이 별도의 독립된 엔터티(Entity)로 분할하는 것을 고려하라.
•
가능한 한 최소의 권한을 갖는 계정으로 웹 앱을 실행하라.
4.
이 원칙을 조직 전체에 적용하라.
•
누구도 일상적인 작업을 수행하는 데 필요하지 않은 리소스에 접근 권한을 가져서는 안 된다.
•
CEO는 데이터베이스나 서버에 대한 접근 권한이 전혀 필요 없다.
•
이렇게까지 하는 이유는 아무도 믿을 수 없어서가 아니라 그들의 접근 권한이 외부의 개인이나 조직에 의해 유출될 수 있기 때문이다.
2. 은둔 보안 방식의 유용성
•
정보 보안 전문가들은 은둔 보안(security by obscurity)을 매우 싫어한다.
◦
은둔 보안에 반대하는 이유는 이것을 통해 시간을 벌 수 없고, 시간을 벌더라도 미미한 수준이기 때문이다.
•
노력에 비해 성과가 미미하고 위험이 상당히 클 때는 항상 은둔 보안보다 실제 보안을 선호해야 한다. 숨기는 것만으로 진정한 보안을 얻을 수는 없지만, 때때로 여러분이 이 문제를 해결할 때까지 시간을 벌 수는 있다.
•
우선순위에 따라 결정하자. 은둔 보안보다 실제 보안이 더 좋지만, 상충 관계를 따져보았을 때, 은둔 보안이 더 이득이 클 수도 있다.
3. 자신만의 보안을 구현하지 마라
•
•
프로덕션에서 자신만의 보안 코드를 사용하면 안 된다. 이 조언은 또한 “자신만의 암호를 굴리지 마라”라는 관용구로 자주 쓰인다.
4. SQL 삽입 공격
•
매개변수화된 쿼리를 사용하는 것이 이상적인 해결책이다.
◦
매개변수화는 강력하다. 애플리케이션의 보안을 지키고 동시에 쿼리 계획 캐시를 적절한 크기로 유지하는 데 이상적이지만, 잘못된 쿼리 최적화와 같은 단점을 가진다.
5. XSS
•
XSS(Cross-site Scripting)는 크로스 사이트 스크립팅 또는 사이트 간 스크립팅이라고 한다.
◦
저자는 극적인 효과를 위해 이를 자바스크립트 인젝션이라고 불렀어야 한다고 생각한다고 했다.
•
XSS는 2단계 공격이다.
◦
첫 번째는 페이지에 자바스크립트 코드를 삽입하는 기능
◦
두 번째는 네트워크를 통해 더 큰 자바스크립트 코드를 로드하여 웹 페이지에서 실행하는 것
•
여기에는 여러 가지 장점이 있다. 세션 가로채기라고 하는데, 다른 세션의 세션 쿠키를 빼내어 사용자의 작업, 정보, 심지어 그들의 세션을 캡처하는 것이다.
6. 크로스 사이트 요청 위조(CSRF, XSRF)
•
https://twitter.com/delete/{tweet_id}라는 URL에서 트위터의 삭제 작업이 POST 작업으로 작동한다고 가정해 보자.
•
내 도메인인 streetcoder.org/about에 웹 사이트를 두고, 자바스크립트를 한 줄도 사용하지 않고 다음 코드와 같은 폼을 넣는다면 무슨 일이 일어날까?
<h1>Welcome to the super secret website!</h1>
<p>Please click on the button to continue</p>
<form method="POST"
action="https://twitter.com/i/api/1.1/statuses/destroy.json">
<input type="hidden" name="id" value="123" />
<button type="submit">Continue</button>
</form>
SQL
복사
•
다행히 123이라는 아이디의 트윗은 없겠지만, 만약 존재했고 트위터가 CSRF를 방어하는 방법을 모르는 작은 스타트업이었다면, 우리는 사용자에게 수상한 웹 사이트를 방문하라고 요청하는 것만으로도 다른 사람의 트윗을 삭제할 수 있었을 것이다.
•
이러한 종류의 문제를 방지하는 방법은 모든 폼을 위해 랜덤하게 생성된 번호를 사용하고, 폼 자체와 웹 사이트 응답 헤더 양쪽에 번호를 복제하는 것이다.
4. 첫 번째 플러드 그리기
•
서비스 거부(Denial of service, DoS) 공격은 서비스가 멈추도록 만드는 가장 대표적인 이름이다.
◦
단순히 서버가 중지, 중단, 충돌하거나 CPU 사용량이 급증하거나 사용 가능한 대역폭이 포화 상태가 되도록 만든다.
•
굉장히 많은 일반 사용자가 정상적으로 접속하더라도 웹 사이트가 다운될 수 있기 때문에, 플러드에 대한 완벽한 해결책은 없다.
◦
합법적인 사용자와 공격자를 구별하는 것은 어려운 일이다.
•
DoS 공격을 완화하여 공격의 가능성을 줄이는 방법이 있는데, 유명한 방법 중 하나는 캡차를 활용하는 것이다.
1. 캡차를 사용하지 마라
•
캡차(captcha)는 웹의 골칫거리이다. 진짜와 가짜를 분리하기 위해 잘 알려진 방법이지만, 서비스를 이용하는 사용자에게는 큰 장애물이다.
◦
캡차의 기본 원리는 “점심으로 무엇을 먹을까?”와 같이 사람이라면 쉽게 풀 수 있지만 공격에 사용되는 자동화된 소프트웨어는 해결하기 어려운 수학적으로 복잡한 문제를 물어보는 과정을 통해 사람을 가려내는 것이다.
•
캡차는 유용하지만 동시에 서비스 거부 공격만큼이나 유해하다.
2. 캡차의 대체 방안
•
성능을 고려하여 코드를 작성하고 캐시를 공격적으로 사용하며 필요에 따라 스로틀링을 사용하라.
⇒ 처리율 제한 장치와 유사한 느낌인듯?
3. 캐시를 구현하지 마라
•
가능하면 프레임워크에서 제공하는 기존의 캐시 인프라를 활용할 것을 추천한다.
5. 암호 저장하기
•
암호(비밀번호, 개인용 키, API 토큰)는 여러분의 왕국으로 들어가기 위한 열쇠이다. 비록 암호는 작은 데이터 조각이지만 비교할 수 없는 양의 데이터에 접근하는 것을 허용한다.
•
보안 위협에 대한 최상의 완화 조치 중 하나는 구획화다. 암호를 안전하게 보관하는 것은 보안 위협을 완화하는 한 가지 방법이다.
1. 소스 코드에 암호를 유지하는 것
•
올바른 스토리지
◦
가장 이상적인 방법은 암호 관리자를 콜드 스토리지로 사용하거나 Azure Key Vault, AWS KMS 등의 클라우드 키 볼트와 같이 해당 목적으로 설계된 별도의 스토리지에 암호를 저장하는 것이다.
•
데이터는 언젠가 유출될 것이다
◦
항상 데이터가 유출될 수 있는 위험을 가정하고 이에 대비하여 설계해야 한다.
•
필요 없는 데이터는 수집하지 않는다
◦
처음부터 데이터가 없다면 유출될 일도 없다. 서비스 기능에 영향을 준다고 생각되는 데이터가 아니라면, 다른 데이터를 수집하는 것에 적극적으로 반대하라.
•
올바른 비밀번호 해싱 방법
◦
비밀번호 유출을 막는 가장 일반적인 방법은 해싱 알고리즘을 사용하는 것이다. 비밀번호를 저장하는 대신 암호학적으로 안전한 비밀번호의 해시 값을 저장한다.
◦
알고리즘을 선택할 때는 사용 중인 프레임워크에서 지원하는 알고리즘을 선호하는 것이 좋다. 만약 사용할 수 없다면 가장 널리 검증된 알고리즘을 선택해야 한다.
•
문자열을 안전하게 비교하라
◦
비밀번호 자체가 아닌 비밀번호의 해시 값을 저장한다. 이제 사용자에게 암호를 입력받아 해시한 후 데이터베이스에 저장된 암호와 그 해시 값을 비교하기만 하면 된다.
•
고정된 솔트를 사용하지 마라
◦
솔트(salt)는 원래 동일한 해시 값을 갖는 비밀번호이지만 값에 차이를 주기 위해 암호 해싱 알고리즘에 도입된 추가적인 값을 말한다.
◦
규칙적인 정규 의사 난수 값만 사용해서는 안 된다. 암호학적으로 안전한 의사난수 생성기(Cryptographically Secure Pseudo Random Number Generator, CSPRNG)에서 생성된 값이 필요하다.
•
UUID는 랜덤이 아니다
◦
완전히 랜덤한 UUID는 인덱스 분포 측면에서 좋지 않다.
▪
레코드 두 개를 연속적으로 삽입하더라도 데이터베이스 인덱스에서 전혀 관련이 없는 위치에 배치되어 순차적으로 읽는 속도가 느려진다.
◦
이를 피하기 위해 새로운 UUID 표준, 즉 UUIDv6, UUIDv7, UUIDv8이 등장했다.
▪
이러한 UUID에는 여전히 임의성이 있지만 훨씬 더 균일한 인덱스 분포를 만들기 위한 타임스탬프를 포함하고 있다.
6. 요약
•
보안 조치의 우선순위를 지정하고 약점을 파악하기 위해 마음 속에 혹은 종이에 적은 위협 모델을 사용하라.
•
중간에 보안을 다시 손보는 것은 어려울 수 있으니 보안을 처음부터 염두에 두고 설계하라.
•
숨기는 것에 의존하는 은둔 보안은 진짜 보안이 아니며, 심각한 손해의 원인이 될 수 있다. 우선순위를 정하라.
•
두 해시 값을 비교하는 경우에도 자신만의 보안 프리미티브를 직접 구현하지 마라. 이미 잘 테스트되고 잘 구현된 솔루션을 신뢰하라.
•
사용자 입력은 나쁘다.
•
SQL 삽입 공격을 막기 위해 매개변수화된 쿼리를 사용하라. 매개변수화된 쿼리를 사용할 수 없을 경우, 사용자 입력을 적극적으로 검증하고 검사하라.
•
XSS 취약성을 방지하려면 페이지에 포함된 사용자 입력이 HTML로 올바르게 인코딩되었는지 확인하라.
•
DoS 공격을 방지하려면, 특히 성장하는 단계에서는 캡차를 피하라. 대신 스로틀링이나 적극적인 캐싱과 같은 다른 방법을 시도하라.
•
암호를 소스 코드가 아닌 별도의 암호 저장소에 저장하라.
•
목적에 맞게 설계된 강력한 알고리즘을 이용하여 암호 해시를 데이터베이스에 저장하라.
•
보안 관련 컨텍스트에서 GUID가 아닌 암호학적으로 안전한 의사 난수를 사용하라.