아이디어와 인사이트

AI 퍼스트 개발, 코딩 가드레일, 그리고 폐기 가능한 아키텍처를 탐구합니다.

의존성은 디스크의 어떤 파일이든 읽을 수 있다. cap-std는 접근 권한을 요청하게 만든다.

Rust 표준 라이브러리는 모든 의존성에 주변 파일 시스템 권한을 부여한다. cap-std는 이를 기능 기반 API로 대체하여 코드가 파일을 열기 전에 해당 경로에 접근할 권리가 있음을 증명하도록 강제한다.

의존성 트리의 모든 크레이트는 를 열거나 디렉터리에 쓰거나, 프로젝트의 모든 파일을 열거할 수 있다. Rust 표준 라이브러리는 접근 권한을 묻지 않는다. 을 호출할 수 있는 코드라면 운영체제가 허용하는 어떤 경로든 접근할 수 있다고 가정한다. cap-std는 이러한 가정을 뒤집는다.…

당신의 서비스는 눈치 채지 못한 공범이다: Confused Deputy 공격의 작동 원리

Confused Deputy 공격은 특권을 가진 서비스를 속여 자신의 권한을 스스로 해치는 데 이용한다. 공격자가 암묵적 신뢰를 어떻게 악용하는지, 그리고 능력 기반 보안이 왜 해결책인지 알아본다.

당신의 서비스에는 클라우드 스토리지에 접근할 수 있는 API 키가 있다. 사용자가 요청을 본다. 서비스는 충실하게 파일을 작성한다. 그 파일은 당신의 버킷이 아닌 공격자의 버킷에 저장되고, 이제 공격자는 당신의 데이터를 손에 넣는다. 당신은 방금 눈치 채지 못한 공범이 되었다. 이것이…

당신의 권한 검사는 거짓말을 하고 있다

능력 기반 보안은 흩어져 있는 역할 검사를 위조할 수 없는 권한 토큰으로 대체합니다. 코드를 읽기 어렵게 만들지 않고 TypeScript에서 이를 구현하는 방법을 소개합니다.

모든 권한 버그는 돌이켜볼 때 똑같이 보인다. 호출 스택 깊숙한 어딘가에 있는 함수가 호출자가 이미 을 검사했을 것이라고 가정한다. 그러나 그렇지 않았다. 또는 새로운 역할이 추가되면, 47개 파일 전체에서 를 grep하며 하나라도 놓치지 않았는지 바라며 뒤진다. 능력 기반 보안은…

만약 모든 함수가 권한을 가정하지 않고 명시적으로 전달받아야 한다면?

대부분의 코드는 주변 권한(ambient authority) 하에서 실행된다: 모든 함수가 모든 것에 접근할 수 있다. 기능 기반 보안(capability-based security)은 함수가 명시적이고 범위가 제한된 권한을 전달받도록 강제함으로써 이 모델을 뒤집는다.

함수는 같은 프로세스 안에 존재한다는 이유만으로 데이터베이스, 결제 게이트웨이, 감사 로그에 모두 접근할 수 있다. 공격자가 하나의 HTTP 핸들러에서 인젝션 버그를 발견하면, 그 모든 권한을 그대로 물려받는다. 이 함수는 이런 권한을 요청한 적 없다. 단지 배치된 위치 덕분에 마치…

타입 검사기가 볼 수 없는 에러 던지기는 이제 그만

던져진 예외는 실패 경로를 타입 시스템에서 감춥니다. 명시적인 에러 반환이 코드를 더 정직하게 만드는 이유, 그리고 삶을 싫어하지 않으면서 이를 도입하는 방법을 알아봅니다.

함수 시그니처가 를 반환한다고 합니다. 그렇지 않습니다. 를 반환하거나 폭발합니다. 타입 시스템은 두 번째 분기에 대해 전혀 모릅니다. 이것이 예외 기반 에러 핸들링의 근본적인 불성실함입니다. 모든 는 컴파일러가 볼 수도, 검사할 수도, 강제할 수도 없는 제어 흐름 경로입니다.…

Rust newtype는 잘못된 상태를 컴파일 타임에, 아무런 비용 없이 표현 불가능하게 만든다

단일 필드 래퍼 struct가 단 한 바이트의 오버헤드도 추가하지 않고 단위 혼합 버그와 타입 혼란을 잡아낸다.

인 사용자 ID를 주문 ID를 기대하는 함수에 넘기면 Rust는 불평하지 않는다. 둘 다 이기 때문이다. 컴파일러는 두 타입이 동일하게 보이므로 당신을 도울 수 없다. 버그는 런타임에, 보통 프로덕션에서, 보통 당신이 안전하다고 생각한 리팩터링 이후에야 발견된다. 이것이 바로…

Switch 문은 컴파일 시점에도 버그를 숨긴다

TypeScript switch 문은 조용히 케이스를 빠뜨리게 만든다. 빌드에서 실패하고 런타임에서는 실패하지 않는 exhaustiveness-checked 패턴으로 대체하는 방법을 알아본다.

shape 타입을 리팩토링하고 새로운 variant를 추가했는데도 TypeScript는 초록불이었다. CI를 통과했고, 배포도 나갔다. 그런데 사용자가 를 반환하는 런타임 분기를 만났고, 앱이 프로덕션에서 터져 버렸다. 범인은 거의 확실히 switch 문이었다. TypeScript의…

컴파일러가 누락된 상태 케이스를 잡아내야 한다. QA팀이 아니라

상태 머신에 새 상태를 추가하는 건 쉽다. 모든 switch 문을 업데이트하는 건 아니다. TypeScript가 케이스를 잊으면 컴파일을 거부하게 만드는 방법을 알아보자.

상태 머신은 처음엔 깔끔하다. 값 세 개, switch 가지 세 개. 그러다 재시도 로직이 네 번째 상태를 추가한다. 부분 실패가 다섯 번째를 추가한다. reducer는 업데이트했는데, 상태 배지 컴포넌트와 analytics 매퍼, 그리고 export 포맷터를 놓친다. 모든 게…

네, 가능합니다. 하지만 타입 시스템이 달러가 뭔지 알아야죠

TypeScript는 모든 숫자를 동일하게 봅니다. 컴파일러가 프로덕션에 도달하기 전에 통화 변환 버그를 잡을 수 있도록 USD와 EUR의 차이를 가르치는 방법입니다.

2022년, 한 대형 핀테크 플랫폼이 32,700,000.00의 대량 이체를 처리했습니다. 금액은 평범한 로 저장되었습니다. 하위 서비스는 센트 단위라고 가정했습니다. 그렇지 않았습니다. 버그는 코드 리뷰, 단위 테스트, 통합 테스트를 모두 통과했습니다. 타입 시스템은 와 를 보고…

숫자는 그냥 숫자가 아니다: 컴파일러가 단위 버그를 잡게 하는 방법

초를 기대하는 곳에 밀리초를 넘기는 버그는 어떤 테스트도 통과한다. 단위를 TypeScript의 타입 시스템에 직접 인코딩하면 런타임 비용 없이 컴파일 시점에 혼동을 잡아내는 방법을 알아보자.

어떤 함수는 타임아웃을 밀리초로 요구한다. 을 넘긴다. 나중에 다른 함수가 타임아웃을 초 단위로 요구한다. 를 넘긴다. 그 사이에 을 호출하면, 한 시간 스물세 분 동안 아무 일도 일어나지 않는다. TypeScript는 여기서 도움이 되지 않는다. 과 는 둘 다 다. 컴파일러는 미터…