관리자 화면에서 상품 목록을 보는데 우리 것이 아닌 상품이 섞여 나왔다. 같은 product 표를 여러 판매자가 쓰고 있었다.
Table of contents
Open Table of contents
구분이 컬럼 하나였다
표에 seller_no 컬럼이 있긴 했다. WHERE 에 그 조건을 넣으면 자기 것만 나온다. 문제는 안 넣은 곳이 있다는 것이었다.
$ grep -rn "FROM product" --include=*.php application/ | wc -l
47
$ grep -rn "seller_no" --include=*.php application/models/ | wc -l
19
grep -rn 으로 세니 product 를 쓰는 곳이 47군데인데 seller_no 가 든 곳은 19군데였다. 조건을 붙이는 것이 규칙일 뿐 강제가 아니면 붙이지 않은 자리가 생긴다.
빠진 곳을 찾았다
47군데를 하나씩 보니 세 부류였다. seller_no 조건이 있는 곳이 19개다. 관리자 전용 집계처럼 조건이 필요 없는 곳이 12개다. 조건이 빠진 곳이 16개였다.
목록 화면 몇 개는 눈에 띄었는데 나머지는 조용했다. COUNT(*) 가 전체를 세고 있었는데 화면에는 숫자만 나오니 아무도 안 알아챈다. 목록은 남의 상품 이름이 보여서 드러나지만 집계는 값 하나라 틀려도 티가 안 난다.
조건을 빠뜨리기 어렵게 만들었다
매번 조건을 붙이는 방식은 또 빠뜨린다. base() 하나로 모으고 그 자리에서 where 를 붙였다.
private function base()
{
return $this->db->where('seller_no', $this->auth->seller_no())
->from('product');
}
public function get_list($limit, $offset) {
return $this->base()->limit($limit, $offset)->get()->result();
}
base() 를 거치면 새 조회를 만들어도 조건이 자동으로 붙는다. 전체를 봐야 하는 관리자용 조회는 이름이 다른 모델로 뺐다. 이름으로 갈리니 어느 쪽을 쓰는지가 코드에서 보인다.
빠진 곳이 또 생기는 것을 막으려고 검사도 뒀다. base() 를 안 거치고 from('product') 를 직접 부르는 곳을 grep -rn 으로 찾는다. 0 건이 나와야 한다.
이 검사가 실제로 잡는지 확인하려고 일부러 직접 조회 한 줄을 넣고 돌려서 나오는 것을 보고 지웠다. 대조군 없이 0 건만 보면 검사가 아무것도 안 하고 있어도 통과로 읽힌다.
읽기보다 나쁜 쪽
읽기만 문제가 아니었다. UPDATE 와 DELETE 에도 같은 where 가 필요했다.
public function update($productNo, $data)
{
$this->db->where('product_no', $productNo)
->where('seller_no', $this->auth->seller_no())
->update('product', $data);
}
이 조건이 없으면 product_no 만 알면 남의 상품을 고칠 수 있다. 다른 판매자의 product_no 로 POST 를 보내 봤더니 UPDATE 가 그대로 됐다. 읽기는 남의 것을 보는 것이고 쓰기는 남의 것을 바꾸는 것이라 이쪽이 더 나빴다.
자료가 실제로 어떻게 섞여 있는지도 GROUP BY seller_no 로 세어 봤다. 한 판매자 상품이 42개다. 그쪽 관리자 화면에는 product 전체인 5,061개가 보이고 있었다. 숫자를 확인한 뒤에야 문제의 크기를 알았다.
정리
- 한 표를 여러 주체가 쓰면
seller_no조건이 모든 조회에 있어야 한다 grep -rn으로 표를 쓰는 곳 수와 조건이 든 곳 수를 세면 빠진 곳이 드러난다- 목록은 눈에 띄지만
COUNT(*)는 조용히 틀린다 - 조건을 매번 붙이지 말고
base()한 곳으로 모은다 - 전체를 봐야 하는 조회는 이름으로 구분되는 별도 모델에 둔다
- 읽기보다
UPDATE와DELETE가 더 위험하다 - 검사를 만들고 일부러 위반을 넣어 잡는지 확인한다
GROUP BY seller_no로 실제 분포를 세어 문제의 크기를 본다