오늘의 전혀 훌륭하지 않은 PHP 상식


  1. 같은 데이터베이스를 기반으로 다른 두 개의 API가 구축되어 있는 회사 서비스가 있다. 내가 만든 API는 일종의 관리 툴에 쓰이는 것으로, 다른 관리자나 일반 사용자를 추가, 삭제하는 기능이 있다. 내가 회사에 오기 전부터 구축되어 있던 API는 일반 사용자들이 쓰는 API다. BCrypt로 비밀번호를 암호화하고 있다고 하길래 내가 새롭게 만들기 시작한 API에서도 BCrypt 방식을 사용했다. 여태까지 별 문제가 없는 줄 알았는데 언젠가부터 관리 툴에서 사용자를 만들고 다른 API를 통해 로그인을 시도하면 비밀번호가 잘못되었다는 에러가 발생한다는 리포트가 들어왔다.

  2. 코드상 문제는 없어보여서 DB를 열어봤다. 한 눈에 대단한 문제점이 보이지 않는 가운데 암호화된 비밀번호 문자열에서 특이한 사항이 보였다. 기존 API를 통해 만들어진 암호화 문자열이 $2a$로 시작하는 반면, 내가 만든 API의 암호화 문자열은 $2y$로 시작하는 것이 아닌가. 검색을 해봤다.

  3. 스택익스체인지 보안 관련 질문 스레드를 찾았다. 정리하면 이런 이야기다. PHP에서 BCrypt를 구현한 crypt_blowfish라는 패키지가 있다. 해당 패키지에서 아스키 코드가 아닌 문자열로 해시를 했을 때 잘못된 결과를 뱉어내는 버그가 발견되었고 2011년 6월에 버그를 수정했다고 한다. 여기까지는 일반적이다. 하나 일반적이지 않은 것은, 그렇게 버그를 수정한 crypt_blowfish 패키지는 해당 버그를 수정했다는 의미에서 기존 $2a$로 시작하는 암호화 문자열을 제멋대로(?) $2y$로 시작하는 형식으로 바꾸자고 제안했다는 것이다. 당연하게도 그 제안은 OpenBSD를 포함, 그 누구도 받아들이지 않았고 오로지 PHP의 crypt_blowfish 패키지만 사용하게 되었다고 한다. 다른 BCrypt 관련 라이브러리, 패키지가 $2y$로 시작하는 암호화 문자열을 제대로 매치하지 못하는 건 당연한 수순.

  4. 암호화 문자열의 시작 부분이 알고리즘 자체에 영향을 미치는 부분은 아니기 때문에 $2y$ 부분을 $2a$로 고쳐주기만 하면 해결되는 문제긴 하다. PHP의 정규식 치환을 사용한 코드로 바꿔주니 제대로 동작하는 것을 확인할 수 있었다. 라라벨의 해시 패키지는 그나마 똑똑한(?) 편이라 $2a$ 버전이든, $2y$ 버전이든 문제없이 해시값을 매치해주는 것 같다.

  5. 하여튼 PHP 너모 무시무시하고 위험한 언어다. 새로 만들게 될 쇼핑몰 API부터 나는 node.js로 완전히 갈아탈 것이다.

Related Posts

비트코인과 페미니즘

비트코인의 세계에는 지갑이라는 개념이 존재한다. 이 지갑이 어떤 로직으로 생성이 되고 보안을 유지하고 어쩌고를 하고 저쩌고를 하는지는 원래는 대단히 중요하고 어려운 이야기지만 오늘의 포스트에서는 별로 주목할 필요가 없다. 이렇게 이야기를 해보자. 최근의 비트코인 지갑은 일정한 안정성과 보안성의 균형을 유지하기 위해 실제 지갑 소유자에게 기억을 하기 쉬운(mnemonic) 단어의 무작위적인 나열을 제시한다. 지갑 소유자가 이 단어를 잃어버리거나 유출하지 않으면 그의 비트코인은 무사하다.