시험 서버를 새로 만들었는데 이미지가 안 보이고 링크를 누르면 운영으로 갔다.
Table of contents
Open Table of contents
주소가 박혀 있었다
코드는 이랬다.
echo '<img src="http://shop.example.com/upload/' . $file . '">';
redirect('http://shop.example.com/order/complete');
시험 서버에서도 운영 주소를 부른다. 이미지는 운영에서 가져오고 주문을 끝내면 운영으로 넘어간다. 링크만 넘어가는 것이 아니라 자료를 바꾸는 요청도 운영으로 가서 시험하다 운영 자료를 건드릴 뻔했다.
몇 군데인지 셌다.
$ grep -rn "http://shop.example.com" --include=*.php application/ | wc -l
142
142곳이었고 뷰 파일이 대부분이며 컨트롤러에도 있었다.
$ grep -rln "http://shop.example.com" --include=*.php application/ | wc -l
38
파일로는 38개다.
세어 놓아야 고친 뒤에 남은 것이 있는지 알 수 있다. 세지 않고 고치면 다 고쳤다고 생각하게 된다. 이 숫자가 나중에 확인의 기준이 됐다.
설정에서 가져오게 바꿨다
CodeIgniter 에는 이미 그런 자리가 있었다.
// config.php
$config['base_url'] = 'http://shop.example.com/';
echo '<img src="' . base_url('upload/' . $file) . '">';
redirect('order/complete');
redirect 는 원래 상대 경로를 받는데 절대 주소를 넣고 있었던 것이다.
환경마다 다르게도 정했다.
// config.php
$host = $_SERVER['HTTP_HOST'] ?? 'shop.example.com';
$config['base_url'] = 'http://' . $host . '/';
붙은 주소를 그대로 쓰게 하면 서버를 늘려도 설정을 안 고친다. 다만 이렇게 하면 요청 헤더를 믿는 것이 되고 HTTP_HOST 를 조작해 다른 주소로 링크가 만들어질 수 있다.
$allowed = ['shop.example.com', 'test.example.com', 'localhost'];
$host = in_array($_SERVER['HTTP_HOST'] ?? '', $allowed, true)
? $_SERVER['HTTP_HOST'] : 'shop.example.com';
$allowed 에 있는 것만 쓴다.
헤더를 그대로 믿는 것과 목록으로 거르는 것의 차이는 한 줄이었다. 그 한 줄이 없으면 링크가 남의 주소로 만들어질 수 있다. 편한 쪽과 안전한 쪽이 갈리는 자리였다.
이미지 주소는 나중에 다른 서버에 둘 수 있어서 따로 뺐다.
$config['asset_url'] = 'http://shop.example.com/upload/';
지금은 같은 자리를 가리키지만 asset_url 로 나눠 두니 이미지 서버를 옮길 때 한 줄만 고치면 된다.
저장된 자료에도 있었다
코드만 고쳐서 될 일이 아니었다.
SELECT COUNT(*) FROM board WHERE content LIKE '%http://shop.example.com%';
842
게시글 본문에 주소가 들어 있었다. 편집기가 이미지를 넣을 때 절대 주소로 넣고 있었다.
이미 들어간 것은 그대로 두고 앞으로 들어가는 것을 상대 경로로 바꿨다. 옛 글을 고치면 이력이 남지 않아서 위험했기 때문이다.
$content = str_replace('http://shop.example.com/upload/',
$this->config->item('asset_url'), $row['content']);
읽을 때 str_replace 로 바꿔서 보여 주는 것으로 했다.
저장된 값은 손대지 않으니 되돌릴 일도 없다. 새로 저장할 때는 주소를 빼고 넣게 했다. 옛것은 변환으로 다루고 새것은 처음부터 안 넣는 두 갈래였다.
검증 — 다시 세어 본 남은 것
다 고쳤다고 생각한 뒤에 한 번 더 찾아봤다.
$ grep -rn "http://shop" --include=*.php application/
(없음)
$ grep -rn "https\?://[a-z.]*example" --include=*.php application/ | grep -v config.php
두 번째 명령으로 다른 형태도 찾았다. https 로 적힌 것과 다른 하위 도메인이 세 곳 더 있었다. 한 문자열로만 찾으면 남는다.
고치고 나서 시험 서버에서 열어 봤다.
$ curl -s http://test.example.com/product/1 | grep -o 'src="[^"]*"' | head
src="http://test.example.com/upload/a.jpg"
시험 주소가 나온다. 운영 주소가 하나라도 나오면 아직 남은 것이다.
세어 본 숫자가 0이 되는 것과 화면에서 확인하는 것은 다른 검증이었다. 앞은 소스에 없다는 것이고 뒤는 실제로 안 나온다는 것이다. 둘을 다 봐야 끝난 것이 된다.
정리
- 주소를 코드에 박으면 다른 환경에서 운영을 부른다
- 시험하다 운영 자료를 건드릴 수 있다
- 몇 군데인지 세고 설정에서 가져오게 바꾼다
base_url()과redirect()는 상대 경로를 받는다- 붙은 주소를 쓰되 허용 목록으로 거른다.
HTTP_HOST는 조작될 수 있다 - 나중에 자리를 옮길 것은
asset_url처럼 따로 설정으로 둔다 - 코드만이 아니라 저장된 자료에도 주소가 있다
- 이미 들어간 것은 두고 읽을 때
str_replace로 바꿔서 보여 준다 - 한 문자열로만 찾으면 남는다. 다른 형태도 찾는다
- 시험 서버에서 열어 운영 주소가 나오는지 확인한다