서버에 파일을 올렸더니 Permission denied, 구글링하면 일단 chmod 777 하라는 답이 먼저 보이죠. 당장 에러는 사라지지만, 사실 그게 가장 위험한 해결책이에요. 결론부터 말하면 chmod 755 는 rwxr-xr-x(소유자는 전부, 나머지는 읽기·실행), 644 는 rw-r--r--(소유자만 쓰기, 나머지는 읽기)예요. 숫자는 읽기 r=4, 쓰기 w=2, 실행 x=1을 더한 값을 소유자·그룹·기타 순서로 세 자리 적은 8진수이고, 보통 디렉터리와 실행 파일은 755, 일반 파일은 644, 비밀 파일은 600이 기본이에요.
이 글에서는 ls -l 에 보이는 -rwxr-xr-x 를 읽는 법, 숫자 계산법, 자주 쓰는 권한 값과 쓰임새, 파일과 디렉터리에서 r·w·x가 다르게 동작하는 부분, 새 파일의 기본 권한을 정하는 umask, setuid·setgid·sticky bit 같은 특수 권한, 그리고 777이 왜 위험하고 대신 무엇을 해야 하는지 정리할게요. 숫자와 rwx를 바로 바꿔 보고 싶다면 chmod 계산기를 같이 열어 두세요.
ls -l의 rwxr-xr-x 읽는 법
ls -l 을 치면 맨 앞에 10글자짜리 권한 문자열이 나와요.
-rwxr-xr-x 1 smith staff 1204 Oct 15 09:30 deploy.sh
drwxr-xr-x 5 smith staff 160 Oct 15 09:31 public
첫 글자는 파일 종류예요. - 는 일반 파일, d 는 디렉터리, l 은 심볼릭 링크예요. 나머지 9글자를 3글자씩 끊으면 소유자(user) · 그룹(group) · 기타(other) 의 권한이에요. 각 묶음은 항상 r, w, x 순서이고, 없는 권한은 - 로 비워요.
| 위치 | 대상 | deploy.sh 의 값 | 의미 |
|---|---|---|---|
| 2~4번째 | 소유자 (smith) | rwx | 읽기·쓰기·실행 |
| 5~7번째 | 그룹 (staff) | r-x | 읽기·실행 |
| 8~10번째 | 기타 (그 외 모두) | r-x | 읽기·실행 |
리눅스는 접근하는 사람이 소유자면 소유자 칸만, 소유자가 아니고 그룹 소속이면 그룹 칸만, 둘 다 아니면 기타 칸만 봐요. 위에서부터 하나만 골라 적용하는 거라서, 소유자 칸이 --- 이고 기타 칸이 rwx 라면 소유자 본인은 접근이 안 되는 이상한 상황도 생길 수 있어요. 단, root는 읽기·쓰기 검사를 건너뛰어요.
chmod 숫자 계산법: r=4, w=2, x=1
권한 숫자는 각 묶음의 3비트를 8진수 한 자리로 적은 거예요. 3비트는 0~7을 표현하니까 8진수 한 자리와 딱 맞아요.
| 숫자 | 2진수 | 문자 | 계산 |
|---|---|---|---|
| 7 | 111 | rwx | 4 + 2 + 1 |
| 6 | 110 | rw- | 4 + 2 |
| 5 | 101 | r-x | 4 + 1 |
| 4 | 100 | r-- | 4 |
| 3 | 011 | -wx | 2 + 1 (거의 안 씀) |
| 2 | 010 | -w- | 2 (거의 안 씀) |
| 1 | 001 | --x | 1 |
| 0 | 000 | --- | 없음 |
그래서 rwxr-xr-x 는 rwx(7) · r-x(5) · r-x(5) = 755, rw-r----- 는 rw-(6) · r--(4) · ---(0) = 640이에요. 거꾸로 숫자를 문자로 바꿀 땐 각 자리에서 4, 2, 1을 차례로 빼 보면 돼요. 6이면 4를 빼고(r) 2를 빼면(w) 0이 되니까 rw- 예요.
755를 10진수로 읽으면 493이에요
권한 숫자는 8진수라서 10진수로 바꾸면 전혀 다른 값이 돼요. 이게 코드에서 실제로 사고가 나는 지점이에요. 파이썬에서 os.chmod(path, 755) 라고 쓰면 10진수 755(= 8진수 1363)가 들어가서 엉뚱한 권한이 걸려요. 반드시 0o755 처럼 8진수 접두어를 붙이고, Node.js도 fs.chmodSync(path, 0o755) 또는 문자열 '755' 로 넘겨야 해요. 진법 변환 원리는 2진수 16진수 변환 원리 글에서 자세히 다뤘어요.
기호 모드: u+x, g-w, o=r
숫자 대신 기호로도 쓸 수 있어요. 대상은 u(소유자), g(그룹), o(기타), a(모두), 연산은 +(추가), -(제거), =(정확히 이것만)이에요.
chmod u+x deploy.sh # 소유자에게 실행 추가 (나머지는 그대로)
chmod go-w config.yml # 그룹·기타의 쓰기 제거
chmod a=r notice.txt # 모두 읽기만 (444)
chmod u=rw,go= .env # 600과 같음
기호 모드 문법 전체는 chmod(1) 매뉴얼 페이지에 정리돼 있어요. 기호 모드의 장점은 다른 비트를 건드리지 않는다는 거예요. 실행 권한만 하나 추가하고 싶을 때 숫자로 덮어쓰면 기존 권한을 잘못 지울 수 있지만, u+x 는 그 비트 하나만 바꿔요.
자주 쓰는 권한 값 755 644 600 700
| 권한 | 문자 | 주로 쓰는 곳 |
|---|---|---|
| 755 | rwxr-xr-x | 디렉터리, 실행 스크립트, 웹 루트 폴더 |
| 644 | rw-r--r-- | HTML·CSS·이미지, 일반 설정 파일 |
| 600 | rw------- | SSH 개인 키, .env, DB 비밀번호가 든 설정 |
| 700 | rwx------ | ~/.ssh 디렉터리, 개인 스크립트 폴더 |
| 750 | rwxr-x--- | 같은 그룹만 들어가야 하는 앱 디렉터리 |
| 640 | rw-r----- | 웹 서버 그룹이 읽어야 하는 비밀 설정 |
| 775 / 664 | rwxrwxr-x / rw-rw-r-- | 팀이 같은 그룹으로 함께 쓰는 공유 폴더 |
| 444 / 400 | r--r--r-- / r-------- | 실수로 고치면 안 되는 파일 |
SSH는 권한을 엄격히 검사해요. 개인 키가 644 처럼 다른 사람이 읽을 수 있으면 UNPROTECTED PRIVATE KEY FILE! 경고와 함께 키 사용을 거부하니, 개인 키는 600, ~/.ssh 는 700 으로 맞춰 두세요.
디렉터리에서 r, w, x는 뜻이 달라요
파일의 r·w·x는 이름 그대로인데, 디렉터리에서는 의미가 달라서 여기서 많이 헷갈려요. 디렉터리는 "파일 이름 목록"이 든 특수한 파일이라고 생각하면 이해가 쉬워요.
| 권한 | 파일에서 | 디렉터리에서 |
|---|---|---|
| r | 내용 읽기 | 안에 있는 이름 목록 보기 (ls) |
| w | 내용 수정 | 안의 파일 생성·삭제·이름 변경 (x도 필요) |
| x | 프로그램으로 실행 | 안으로 들어가기(cd), 이름으로 파일에 접근 |
그래서 이런 일이 생겨요.
- r만 있고 x가 없으면
ls로 이름은 보이는데 파일을 열 수 없고,ls -l하면 크기·권한이?로 나와요. - x만 있고 r이 없으면 목록은 못 보지만 정확한 이름을 알면 파일을 열 수 있어요.
- 파일 삭제는 디렉터리 권한으로 정해져요. 파일이
444여도 디렉터리에 w와 x가 있으면 지울 수 있어요. 반대로 파일이777이어도 디렉터리에 w가 없으면 못 지워요. - 경로의 모든 상위 디렉터리에 x가 있어야 해요.
/home/smith/public/index.html이 644여도/home/smith가 700이면 웹 서버는 파일에 닿지 못하고 403을 내요.
chmod -R 755를 조심하세요
폴더 전체를 chmod -R 755 로 바꾸면 안에 있는 일반 파일까지 전부 실행 가능해져요. 디렉터리와 파일을 나눠서 바꾸는 게 맞아요. 대문자 X 는 "디렉터리이거나 이미 누군가에게 실행 권한이 있는 파일에만 x를 붙이라"는 뜻이라 한 줄로도 처리돼요.
find ./site -type d -exec chmod 755 {} +
find ./site -type f -exec chmod 644 {} +
# 또는
chmod -R u=rwX,go=rX ./site
umask 022, 새 파일의 기본 권한이 정해지는 방식
새로 만든 파일이 왜 644 이고 디렉터리는 755 인지는 umask가 정해요. 기본값은 파일 666, 디렉터리 777 에서 출발하고, umask에 켜진 비트를 꺼요. 파일은 출발점부터 x가 없어서, 새로 만든 스크립트에 chmod +x 를 따로 해 줘야 하는 거예요.
| umask | 새 파일 | 새 디렉터리 | 쓰임 |
|---|---|---|---|
| 022 | 644 | 755 | 대부분 배포판 기본값 |
| 002 | 664 | 775 | 사용자별 개인 그룹을 쓰는 배포판, 팀 공유 |
| 027 | 640 | 750 | 기타 사용자 차단이 필요한 서버 |
| 077 | 600 | 700 | 개인 비밀 파일 작업 |
umask는 빼기가 아니라 비트를 끄는 연산이에요. umask가 033 이면 666 − 033 = 633이 아니라, 666에서 033에 켜진 비트(그룹·기타의 w와 x)를 끈 644가 돼요. 원래 없던 x 비트는 끌 것도 없기 때문이죠. 현재 값은 umask 명령으로 확인하고, 셸에서 바꾸면 그 세션에서만 적용돼요(동작 정의는 umask(2) 매뉴얼 참고).
setuid, setgid, sticky bit 특수 권한
세 자리 앞에 한 자리를 더 붙이면 특수 권한이에요. 4는 setuid, 2는 setgid, 1은 sticky bit예요.
| 특수 권한 | 숫자 | 표시 위치 | 효과 | 대표 예 |
|---|---|---|---|---|
| setuid | 4000 | 소유자 x 자리에 s | 실행하는 사람이 아니라 파일 소유자 권한으로 실행 | /usr/bin/passwd (-rwsr-xr-x, 4755) |
| setgid | 2000 | 그룹 x 자리에 s | 파일: 소유 그룹 권한으로 실행. 디렉터리: 안에 만든 파일이 디렉터리 그룹을 물려받음 | 팀 공유 폴더 2775 |
| sticky | 1000 | 기타 x 자리에 t | 디렉터리 안 파일을 소유자만 삭제·이름 변경 | /tmp (drwxrwxrwt, 1777) |
passwd 는 일반 사용자가 실행해도 root만 쓸 수 있는 비밀번호 파일을 고쳐야 해서 setuid가 붙어 있어요. 그만큼 setuid 파일에 버그가 있으면 바로 root 권한 탈취로 이어져서, 보안 점검 때 find / -perm -4000 으로 목록을 확인하는 게 기본이에요. 리눅스는 셸 스크립트의 setuid를 무시하니, 스크립트에 붙여도 효과가 없어요.
/tmp 는 누구나 쓸 수 있어야 하지만 남의 파일을 지우면 안 되니 sticky bit가 붙어 있어요. 표시가 대문자 S 나 T 로 보이면 특수 비트는 켜졌는데 그 자리의 x가 없다는 뜻이에요.
chmod 777이 위험한 이유와 대신 할 것
777 은 서버의 모든 사용자와 모든 프로세스가 그 파일을 읽고, 고치고, 실행할 수 있다는 뜻이에요. 문제가 되는 장면은 이래요.
- 웹 업로드 폴더가 777이면 취약한 플러그인 하나로 올라온 악성 스크립트가 그대로 실행될 수 있어요.
- 크론이 root로 실행하는 스크립트가 777이면 일반 계정 하나만 뚫려도 그 스크립트를 고쳐 root 권한을 얻어요.
- 홈 디렉터리나
~/.ssh가 다른 사람에게 쓰기 가능하면 sshd가 키 인증을 거부해서 오히려 로그인이 막혀요.
Permission denied 가 나면 777 대신 이렇게 확인해 보세요.
ls -l과id로 파일 소유자와 지금 실행하는 사용자가 누구인지 봐요. 웹 서버는 보통www-data나nginx로 돌아요.- 소유자가 다르면
chown으로 소유자를 맞추거나, 공통 그룹을 만들어chgrp후775/664를 줘요. - 경로상 상위 디렉터리에 x가 다 있는지
namei -l /경로/파일로 한 번에 확인해요. - 도커 볼륨이라면 컨테이너 안 사용자 UID와 호스트 파일 UID가 맞는지 확인해요. 777이 제일 많이 등장하는 곳이 여기예요.
참고로 Git은 권한 중 실행 비트 하나만 기록해요(파일 모드 100755 또는 100644). 그래서 저장소에서 받은 파일이 600이나 777로 오지는 않고, 스크립트 실행 권한은 git update-index --chmod=+x 파일 로 커밋할 수 있어요.
자주 묻는 질문
chmod 755와 644 중 무엇을 써야 하나요?
실행하거나 들어가야 하는 것(디렉터리, 셸 스크립트, 바이너리)은 755, 읽기만 하면 되는 것(HTML, 이미지, 설정 파일)은 644예요. 일반 파일에 755를 주면 실행 권한이 불필요하게 열려요.
현재 권한을 숫자로 보려면 어떻게 하나요?
리눅스는 stat -c '%a %n' 파일, macOS는 stat -f '%Lp %N' 파일 로 755 같은 숫자를 바로 볼 수 있어요. ls -l 의 문자를 4·2·1로 더해 직접 계산해도 돼요.
파일 권한이 444인데 삭제가 돼요. 왜 그런가요?
삭제는 파일 내용이 아니라 디렉터리의 이름 목록을 고치는 일이라서, 파일이 아니라 디렉터리의 w·x 권한으로 정해져요. 남이 지우지 못하게 하려면 디렉터리에 sticky bit를 걸거나 디렉터리 쓰기 권한을 막아야 해요.
chmod 4755처럼 네 자리는 무슨 뜻인가요?
맨 앞자리는 특수 권한이에요. 4는 setuid, 2는 setgid, 1은 sticky bit이고, 뒤 세 자리는 일반 권한이에요. 4755는 setuid가 걸린 rwxr-xr-x라서 ls -l 에 -rwsr-xr-x 로 보여요.
심볼릭 링크의 권한은 왜 항상 rwxrwxrwx인가요?
리눅스에서 심볼릭 링크 자체의 권한은 쓰이지 않고, 실제 접근은 링크가 가리키는 대상 파일의 권한으로 검사해요. 그래서 링크에 chmod를 하면 링크가 아니라 대상 파일의 권한이 바뀌어요.
정리
chmod 숫자는 r=4·w=2·x=1을 더한 값을 소유자·그룹·기타 순으로 적은 8진수예요. 755(rwxr-xr-x)는 디렉터리와 실행 파일, 644(rw-r--r--)는 일반 파일, 600·700은 SSH 키와 비밀 파일에 쓰는 게 기본이에요. 디렉터리의 x는 통과 권한이고 파일 삭제는 디렉터리의 w로 정해지며, 새 파일 권한은 umask(보통 022)가 비트를 꺼서 만들어요.
Permission denied 를 777로 덮지 말고 소유자와 그룹부터 맞추세요. rwx 체크박스로 숫자를 만들거나 반대로 풀어 보려면 chmod 계산기를, 8진수·2진수 자체가 헷갈리면 진법 변환기를 써 보세요. 크론으로 스크립트를 돌릴 때 권한과 함께 자주 막히는 부분은 크론 표현식 읽는 법 글에 정리해 두었어요.