////
Search
6️⃣

6장. 조사를 통한 보안

이 장에서 다룰 내용

보안에 대해 전체적으로 이해하기
위협 모델 활용하기
SQL 삽입, CSRF, XSS, 오버플로와 같은 일반적인 보안 함정 방지하기
공격자의 공격력을 줄이는 기술 알아보기
암호를 올바르게 저장하기
트로이 목마의 예처럼 보안은 인간의 심리에 대한 넓고도 깊은 용어이다.
이것이 바로 우리가 확장시켜야할 새로운 관점이다. 보안은 소프트웨어나 정보에만 국한된 것이 아니라 사람과 환경에 대한 것이다.

1. 해커를 넘어서

보안은 테스트와 같이 서비스, 데이터, 비즈니스의 신뢰성을 나타내는 부분 집합이다.
보안을 신뢰성의 관점에서 보면 보안과 관련된 결정을 내리기가 더 쉬워진다.
보안과 관련된 결정을 신뢰성에 대한 기술 부채로 보는 것은 전반적으로 우리의 삶을 최적화하는 데 도움이 된다.

2. 위협 모델링

위협 모델이란 보안 측면에서 무엇이 잘못될 수 있는지를 명확히 이해하는 것이다.
위협 모델의 목표는 수행해야 하는 보안 조치의 우선순위를 정하고, 비용을 최적화하며, 효율성을 높이는 것이다.
실제 위협 모델링은 행위자, 데이터 흐름, 신뢰 경계를 분석하는 것이 포함된다.

1. 주머니에 들어갈 만큼 작은 위협 모델

모든 애플리케이션에 대해 위협 모델링을 해야 하는 것은 아니다. 하지만 최소한의 보안 코드를 작성해야 한다.
기본으로 애플리케이션을 위한 소형 위협 모델이 필요하다. 소형 위협 모델은 다음과 같은 요소를 포함한다.
애플리케이션의 자산
기본적으로 소스 코드, 설계 문서, 데이터베이스, 개인 키, API 토큰, 서버 구성, 넷플릭스 시청 내역 등 손실되거나 유출되고 싶지 않은 모든 항목이 자산이다.
자산을 저장하는 서버
모든 서버는 일부 사용자 그룹에 의해 액세스되고, 일부 다른 서버에 액세스한다. 잠재적인 문제를 파악하려면 이러한 관계를 아는 것이 중요하다.
정보 민감성
여러분은 스스로에게 “만약 이 정보가 공개된다면 얼마나 많은 사람과 기관이 피해를 입을 것인가?”, “이로 인한 잠재적인 피해가 얼마나 심각한가?”, “정보 유출로 인해 감옥에 들어갔던 사람이 있었나?”라는 몇 가지 질문을 던짐으로써 이를 평가할 수 있다.
리소스에 대한 액세스 경로
애플리케이션이 데이터베이스에 액세스할 수 있다.
접근할 수 있는 다른 방법이 있는가?
누가 접근할 수 있는가?
접근 권한이 있는 사람이 얼마나 안전한가?
누군가가 그들을 속여서 데이터베이스에 접속하면 어떻게 될까?
그들은 ︎을 실행하여 프로덕션 데이터베이스를 쉽게 삭제할 수 있는가? 
정해진 사람만 소스 코드에 액세스할 수 있는가?
그렇다면 소스 코드에 액세스할 수 있는 모든 사용자는 프로덕션 데이터베이스에도 효과적으로 액세스할 수 있다.
이러한 정보를 가지고 기본 위협 모델을 종이에 그려볼 수 있다. 애플리케이션이나 웹 사이트를 사용하는 모든 사용자를 위한 위협 모델은 위 그림와 같다.
시도 가능한 위협이 있으면 그 구멍을 메우는 식으로 해당 위협을 해결하기 시작할 수 있다.
웹 앱이나 API가 인기 있는 후보 중 하나이기 때문에 웹 애플리케이션을 작성하는 동안 보안 코드를 작성하는 방법을 아는 것은 중요하다.

3. 웹 앱을 안전하게 작성하라

1. 보안을 염두에 두고 설계하라

보안은 나중에 개선하기가 어렵다. 따라서 애플리케이션을 설계할 때부터 보안을 고려하는 것이 중요하다.
1.
글로 적거나 머리 속에 있는 위협 모델을 검토하라.
위험, 지금 보안을 유지하는 데 드는 비용, 나중에 보안을 유지하는 데 드는 비용을 파악하라.
2.
앱에 필요한 보안 암호(데이터베이스 비밀번호, API 키)를 저장할 위치를 결정하라.
암호를 어렵게 만들어라. 모든 사용자가 소스 코드에 접근할 수 있다고 가정하라.
3.
최소한의 권한을 주도록 설계하라.
이상적으로 어떤 코드든 작업을 수행하는 데 필요한 것보다 더 많은 권한을 요구하지 말아야 한다.
예를 들어 여러분의 앱으로 정기적인 데이터베이스 복구 작업을 예약할 필요가 없다면 앱에 데이터베이스 관리자 권한을 주지 마라.
일부 작업만을 위해서 더 높은 권한이 필요한 경우에는 별도의 앱과 같이 별도의 독립된 엔터티(Entity)로 분할하는 것을 고려하라.
가능한 한 최소의 권한을 갖는 계정으로 웹 앱을 실행하라.
4.
이 원칙을 조직 전체에 적용하라.
누구도 일상적인 작업을 수행하는 데 필요하지 않은 리소스에 접근 권한을 가져서는 안 된다.
CEO는 데이터베이스나 서버에 대한 접근 권한이 전혀 필요 없다.
이렇게까지 하는 이유는 아무도 믿을 수 없어서가 아니라 그들의 접근 권한이 외부의 개인이나 조직에 의해 유출될 수 있기 때문이다.

2. 은둔 보안 방식의 유용성

정보 보안 전문가들은 은둔 보안(security by obscurity)을 매우 싫어한다.
은둔 보안에 반대하는 이유는 이것을 통해 시간을 벌 수 없고, 시간을 벌더라도 미미한 수준이기 때문이다.
노력에 비해 성과가 미미하고 위험이 상당히 클 때는 항상 은둔 보안보다 실제 보안을 선호해야 한다. 숨기는 것만으로 진정한 보안을 얻을 수는 없지만, 때때로 여러분이 이 문제를 해결할 때까지 시간을 벌 수는 있다.
우선순위에 따라 결정하자. 은둔 보안보다 실제 보안이 더 좋지만, 상충 관계를 따져보았을 때, 은둔 보안이 더 이득이 클 수도 있다.

3. 자신만의 보안을 구현하지 마라

보안은 복잡하다. 해시, 암호화, 또는 스로틀링(throttling)5과 같은 보안 메커니즘을 직접 구현해서는 안 된다.
프로덕션에서 자신만의 보안 코드를 사용하면 안 된다. 이 조언은 또한 “자신만의 암호를 굴리지 마라”라는 관용구로 자주 쓰인다.

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가 아닌 암호학적으로 안전한 의사 난수를 사용하라.