├── .gitignore ├── .idea └── vcs.xml ├── .travis.yml ├── GLOSSARY.md ├── README.md ├── SUMMARY.md ├── _config.yml ├── author.md ├── book.json ├── encryption.md ├── firewall.md ├── linux.md ├── php-security-checklist.md ├── public-key-encryption.md ├── secure-coding.md ├── security-basic.md ├── seealso.md ├── selinux.md ├── ssh-server.md ├── ssl-tls-https.md ├── styles └── website.css ├── web-app-server.md └── web-server.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/vim 2 | 3 | ### Vim ### 4 | [._]*.s[a-w][a-z] 5 | [._]s[a-w][a-z] 6 | *.un~ 7 | Session.vim 8 | .netrwhist 9 | *~ 10 | 11 | _book/ 12 | node_modules/ 13 | .idea/ 14 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "4.1" 5 | before_script: 6 | - npm install -g free-programming-books-lint 7 | script: fpb-lint . 8 | -------------------------------------------------------------------------------- /GLOSSARY.md: -------------------------------------------------------------------------------- 1 | # 용어 정리 2 | 3 | 4 | 5 | ## 제로 데이 공격(Zero Day Attack) 6 | 7 | 소프트웨어의 취약점에 대한 패치가 나오지 않은 시점에서 이루어지는 공격을 말한다. 8 | 9 | https://ko.wikipedia.org/wiki/%EC%A0%9C%EB%A1%9C_%EB%8D%B0%EC%9D%B4_%EA%B3%B5%EA%B2%A9 10 | 11 | ## 중간자 공격 12 | 13 | 중간자 공격(MiTM; Man in The Middle Attack)은 통신을 연결하는 두 사람 사이에 중간자가 침입하여, 한쪽에서 전달된 정보를 도청 및 조작한 후 다른 쪽으로 전달하는 공격을 말한다. 14 | 15 | 두 사람은 상대방에게 연결했다고 생각하지만 실제로는 두 사람은 중간자에게 연결되어 있으며 중간자는 둘 사이의 모든 정보를 해독할 수 있다. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 견고한 웹 서비스 만들기 2 | 3 | 인터넷으로 서비스를 제공하기 위해 챙겨야 할 실용적인 보안 가이드. 4 | 5 | [**github**](https://github.com/lesstif/web-service-hardening)에 공개되어 있으며 EBook은 **[gitbook](https://lesstif.gitbooks.io/web-service-hardening/content/)** 또는 **[gitbook v2](https://lesstif.gitbook.io/web-service-hardening/)** 에서 읽으실 수 있으며 다음 포맷으로 다운로드 가능합니다. 6 | 7 | - [PDF](https://www.gitbook.com/download/pdf/book/lesstif/web-service-hardening) 8 | - [EPub](https://www.gitbook.com/download/epub/book/lesstif/web-service-hardening) 9 | - [Mobi](https://www.gitbook.com/download/mobi/book/lesstif/web-service-hardening) 10 | 11 | 로컬에서 gitbook 을 설치하고 볼 경우는 다음 명령어로 gitbook-cli 를 설치(node 와 npm 필요)합니다. 12 | 13 | ```sh 14 | npm install gitbook-cli -g 15 | gitbook build . 16 | gitbook serve . 17 | ``` 18 | 19 | 설치가 완료되면 브라우저로 http://localhost:4000/ 에 연결하면 됩니다. 20 | 21 | > [!TIP] 22 | > Windows 에서 gitbook serve 시 'EPERM: operation not permitted, mkdir' 에러가 날 경우 [여기](http://stackoverflow.com/questions/34600932/npm-eperm-operation-not-permitted-on-windows)를 참고하세요. 23 | 24 | 25 | ## 목적 26 | 최근 P 사이트의 개인 정보 유출 사태에서 보듯이 보안은 하면 좋고 안해도 그만인 요소가 아니라 고객과 비즈니스를 보호하기 위해서는 우선적으로 챙겨야 할 핵심 요소입니다. 27 | 28 | 보안을 소홀히 하거나 관심이 없어서 해킹을 당하거나 정보 유출이 발생한다면 사용자 이탈, 평판 하락등 심각한 피해를 입을 수 있으며 특히 강화된 개인정보보호법과 정보통신망법에 의해 큰 사업상의 손실을 입을 수도 있습니다. 29 | 30 | 하지만 기밀 정보를 보호하고 시스템을 안전하게 유지하는 것은 많은 보안 지식을 필요로 하므로 경험이 많지 않다면 쉽지 않은 일입니다. 31 | 32 | 이 글에서는 리눅스를 기반으로 웹 서비스를 제공할 경우 필수적으로 챙겨야 할 보안의 핵심 사항을 정리해 보고 실무에서 활용할 수 있는 베스트 프랙티스를 제공하고자 합니다. 33 | 34 | 리눅스 배포판은 우분투 서버(Ubuntu Server)와 레드햇 엔터프라이즈 리눅스(RHEL;Red Hat Enterprise Linux)과 이의 파생판인 CentOS 를 대상으로 작성하였으며 웹 서버, 애플리케이션 서버, 인프라 서비스등은 오픈 소스 제품만을 다루고 있습니다. 35 | 36 | 37 | ## 독자 38 | 개발자와 운영자, 아키텍트등 서비스를 개발하고 운영하는 기술자들을 대상으로 합니다. 39 | 40 | 41 | ## 면책 조항 42 | 이 문서는 보안을 점검하기 위한 체크 리스트 목적으로 만들어진 자료며 여기에 있는 내용을 모두 적용 했다고 완벽한 보안이 보장되지 않으며 잘못된 결과에 대해서는 저자는 아무 책임이 없습니다. 43 | 44 | ## 제안과 기여 45 | 46 | 본 문서에서 틀린 부분이나 수정/추가되어야 할 부분이 있으면 Fork 후에 [PR](https://github.com/lesstif/security-best-practices/pulls) 을 날려주시기 바라며 제안은 [issue](https://github.com/lesstif/security-best-practices/issues) 를 통해서 해주시면 됩니다. 47 | 48 | ## 기여자 49 | 50 | 본 문서에 기여하신 분들입니다. 51 | 52 | * [roupkk](https://github.com/roupkk) 님 53 | * [haruair](https://github.com/haruair) 님 54 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Introduction](README.md) 4 | * [저자 소개](author.md) 5 | * [기본적인 보안](security-basic.md) 6 | * [리눅스 설치와 패키지 관리](linux.md) 7 | * [보안 강화 리눅스(SELinux)](selinux.md) 8 | * [iptables/firewalld](firewall.md) 9 | * [ssh 서버](ssh-server.md) 10 | * [웹 서버](web-server.md) 11 | * [HTTPS(SSL/TLS)](ssl-tls-https.md) 12 | * [WAS](web-app-server.md) 13 | * [대칭키 암호화](encryption.md) 14 | * [공개키 암호화](public-key-encryption.md) 15 | * [시큐어 코딩](secure-coding.md) 16 | * [PHP 보안강화 하기](php-security-checklist.md) 17 | * [용어 정리](GLOSSARY.md) 18 | * [같이 읽기](seealso.md) 19 | 20 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /author.md: -------------------------------------------------------------------------------- 1 | #### 정광섭 : 치킨집 안 하고 65세까지 코딩 하려고 노력하는 늙은 개발자 2 | #### github : [https://github.com/lesstif](https://github.com/lesstif) 3 | #### blog: [https://lesstif.com](https://lesstif.com) 4 | 5 | #### ["쉽게 배우는 라라벨 5 프로그래밍 : 견고하고 확장성 있는 PHP 웹 애플리케이션 개발"](http://wikibook.co.kr/laravel-5-programming/) - 저술 6 | !["쉽게 배우는 라라벨 5 프로그래밍"](http://image.aladin.co.kr/product/8317/4/cover/k692434452_1.jpg "쉽게 배우는 라라벨 5 프로그래밍") 7 | 8 | #### ["리눅스를 활용한 회사 인프라 구축의 모든 것"](http://www.aladin.co.kr/shop/wproduct.aspx?ItemId=50274345) 저술 9 | ![리눅스를 활용한 회사 인프라 구축의 모든 것](http://image.aladin.co.kr/product/5027/43/cover/899813974x_1.jpg "리눅스를 활용한 회사 인프라 구축의 모든 것") -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "견고한 웹 서비스를 만들기 위한 실용적인 보안 가이드", 3 | "styles": { 4 | "website": "styles/website.css", 5 | "ebook": "styles/ebook.css", 6 | "pdf": "styles/pdf.css", 7 | "mobi": "styles/mobi.css", 8 | "epub": "styles/epub.css" 9 | }, 10 | "plugins": [ 11 | "etoc", 12 | "flexible-alerts", 13 | "disqus", 14 | "ga" 15 | ], 16 | "pluginsConfig": { 17 | "etoc": { 18 | "h2lb": 3, 19 | "mindepth": 3, 20 | "maxdepth": 4, 21 | "notoc": false 22 | }, 23 | "disqus": { 24 | "shortName": "lesstif-gitbook-io" 25 | }, 26 | "ga": { 27 | "token": "UA-135306060-1" 28 | }, 29 | "flexible-alerts": { 30 | "style": "flat" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /encryption.md: -------------------------------------------------------------------------------- 1 | # 대칭키 암호화 2 | 3 | 4 | 5 | 6 | ## 대칭키 알고리즘 7 | 8 | 9 | 대칭키(Symmetric key) 방식은 암호문을 생성(암호화)할 때 사용하는 키와 암호문으로부터 평문을 복원(복호화)할 때 사용하는 키가 동일한 암호 시스템으로 일반적으로 알고 있는 암호 시스템입니다. 10 | 11 | 하나의 키로 암호화와 복호화를 수행하므로 대칭키(Symmetric key) 또는 비밀키(Secret Key) 방식의 암호화라고 합니다. 12 | 13 | 대칭키 방식의 암호화는 데이타를 블록(Block) 단위로 처리하는 Block cipher 와 연속된 스트림으로 처리하는 Stream cipher 가 있으며 일반적으로 대칭키 암호화라고 하면 블록 방식의 암호화를 의미합니다. 14 | 15 | 블록 방식의 주요 알고리즘으로는 예전에 유닉스의 패스워드 파일을 암호화하는데 사용했지만 오래전에 뚫려서 이제는 절대 사용하면 안 되는 DES(Data Encryption Standard)와 현재 미국의 표준 암호 알고리즘인 AES가 있으며 국내에서 개발한 SEED와 ARIA , 그리고 유럽에서 개발한 IDEA 알고리즘 등이 있습니다. 16 | 17 | > [!NOTE] 18 | > 미국의 국가표준기술원(NIST)에서는 미정부와 군에서 사용할 암호화 알고리즘을 공모했습니다. 19 | > 벨기에의 암호학자 둘이 자신들이 설계한 Rijndael 알고리즘을 제출했고 미국은 이를 표준 알고리즘으로 채택하고 개선하여 AES(Advanced Encryption Standard)가 되었습니다. 20 | > 정부가 사용할 암호 알고리즘을 공모하고 외국인이 개발한 제품을 채택하는 열린 자세가 미국의 강력함의 원천인듯 합니다. 21 | 22 | 블록 암호화는 정해진 길이, 또는 가변 길이의 암/복호화 키를 사용하며 알고리즘마다 다릅니다. 23 | 24 | 예로 AES 는 128, 192, 256 비트의 키를 사용할 수 있으며 SEED 는 128를 OpenBSD 에서 많이 사용하는 Blowfish 는 32 ~ 448 비트의 키 길이를 사용합니다. 25 | 26 | 대칭키 암호는 공개키 방식에 대비하여 구현이 용이하고 CPU/메모리를 적게 사용하고 매우 빠른 암/복호화 속도를 장점으로 갖고 있으며 Windows의 BitLocker 와 OS X 의 FileVault 같이 디스크 전체를 암호화할 때도 대칭키 방식(AES) 을 사용합니다. 27 | 28 | 하지만 대칭키 방식은 하나의 키만 사용하므로 상대방과 대칭키 기반으로 암호화 통신을 할 경우 상대방도 사전에 같은 키를 갖고 있어야 합니다. 29 | 30 | [U-571](http://movie.daum.net/moviedb/main?movieId=984)이라는 영화는 2차 대전때 연합군이 독일군의 암호화 통신을 해독하기 위해 [에니그마 장비](https://upload.wikimedia.org/wikipedia/commons/3/3e/EnigmaMachineLabeled.jpg)를 탈취하는 특수 작전에 대해서 다루고 있습니다. 31 | 32 | 이처럼 대칭키 방식의 최대 단점은 암호화 통신을 위한 키 전달과 관리가 어려운 점입니다. 33 | 34 | 가령 대칭키를 보내는 중간에 유출될 우려가 있고 이로 인해 암호화 통신을 도감청 당할 수 있으며 여러 상대방과 통신할 경우 각각의 대칭키를 관리하기(생성, 전달, 유출시 폐기)가 어렵고 여러 상대방과 같은 키를 사용할 경우 키가 노출되면 모든 채널과 새로 키를 전달해야 하는 치명적인 단점이 있습니다. 35 | 36 | 37 | ## 블록 암호 패딩(Padding) 38 | 39 | 블록 알고리즘은 입력 데이타를 정해진 블록으로 잘라서 암/복호화를 수행하며 블록의 크기는 알고리즘마다 다릅니다. 40 | 예로 AES 는 128 bit(16 byte)를 하나의 블록으로 처리합니다. 41 | 42 | 하지만 입력 데이타가 항상 블록의 배수일리는 없으므로 입력데이타를 블록 크기에 맞춰주는 작업이 필요하며 이를 패딩이라고 합니다. 43 | 44 | 즉 AES 를 사용할 경우 암호화할 데이타가 19 바이트일 경우 최소 공배수인 32 byte 로 맞춰주어야 하며 이 작업을 패딩이라고 지칭하며 여러 가지 패딩 방법이 있습니다. 45 | 46 | 47 | 48 | ## 블록 암호 운영 모드(Operation mode) 49 | 50 | 암호화를 수행할 때 개별 블록으로 나눠진 데이타를 처리하는 방식을 블록 암호 운영 모드라고 하며 여러 가지 모드가 있으며 대표적인 2가지 모드를 소개하겠습니다. 51 | 52 | ### ECB(electronic codebook) 53 | 54 | 가장 간단한 방식이며 나눠진 블록을 각각 암호화하는 방식으로 각 블록마다 같은 암호화 키를 사용하므로 보안이 취약한 문제가 있습니다. 55 | 56 | ![ECB 방식](https://upload.wikimedia.org/wikipedia/commons/c/c4/Ecb_encryption.png) 57 | 58 | ### CBC(cipher-block chaining) 59 | 60 | CBC 는 ECB 의 단점을 해결하기 위한 방법으로 각 블록은 암호화되기 전에 이전 블록과 XOR 연산을 거치며 유추가 힘들도록 최초의 입력은 IV(initial vector) 라는 데이타와 연산을 하게 됩니다. 61 | 62 | 이때문에 CBC 암호화를 사용할 경우 대칭키와 IV 가 필요합니다. 63 | 64 | ![CBC 방식](https://upload.wikimedia.org/wikipedia/commons/d/d3/Cbc_encryption.png) 65 | 66 | 67 | 아래의 펭귄 그림이 암호화할 원본 데이타라고 생각해 봅시다. 68 | 69 | ![원본 데이타](https://upload.wikimedia.org/wikipedia/commons/5/56/Tux.jpg "원본 데이타") 70 | 71 | 좌측은 **ECB**, 우측은 **CBC** 방식으로 암호화한 결과이며 그림처럼 ECB 는 원문의 흔적이 남기 때문에 원문을 유추할 수 있으며 특히 원문에 같은 내용의 블록이 있을 경우 키를 알아내고 원문을 손쉽게 해독할 수 있는 취약점이 존재합니다. 72 | 73 | ![ECB 암호화](https://upload.wikimedia.org/wikipedia/commons/f/f0/Tux_ecb.jpg "ECB 암호화") 74 | ![CBC 암호화](https://upload.wikimedia.org/wikipedia/commons/a/a0/Tux_secure.jpg "CBC 암호화") 75 | 76 | ## 암호화 해시 함수 77 | 78 | 암호화 해시 함수(Cryptographic hash function)는 해시 함수의 일종으로 입력에 대해 정해진 길이의 출력을 내주며 해시 값으로부터 원래 값을 유추하기 어렵게 설계된 함수입니다. 79 | 80 | 즉 'abc' 라는 입력이 있을 경우 SHA 256 은 'edeaaff3f1774ad2888673770c6d64097e391bc362d7d6fb34982ddf0efd18cb' 라는 출력을 내게 됩니다. 81 | 82 | 임의의 입력이 있을 경우 정해진 길이의 출력을 하는 일방향 함수(One Way function)를 의미하며 동일한 입력에 대해 동일한 출력을 내야 하며 다른 값인데 동일한 출력을 내는 경우(해시 충돌)가 극히 드물어야 합니다. 83 | 84 | 유명한 알고리즘으로는 MD5(보안 문제때문에 암호용으로는 사용 되지 않음), SHA1(현재는 안전하지 않음), SHA-2(SHA256, SHA384, SHA512) 등이 있습니다. 85 | 86 | ## 비밀번호 암호화 87 | 88 | 비밀 번호 암호화는 일반적인 암호화와는 달리 **복호화를 할 필요가 없습**니다. 89 | 90 | 비밀 번호 암호화는 PBKDF 같은 함수를 사용하거나 또는 bcrypt 알고리즘을 구현한 메서드를 사용하면 됩니다, 91 | 92 | 직접 비밀번호 암호화하는 기능을 구현할 수 있겠지만 검증된 라이브러리를 사용하는 것을 권장하며 직접 만들어 쓸 경우 다음 사항을 꼭 지키십시요. 93 | 94 | * 안전한 해시 함수(SHA2 이상) 사용 95 | * Random 값을 생성한 후에 Salt 첨가. 96 | 97 | ### PBKDF 98 | 99 | PBKDF(Password-Based Key Derivation Function) 는 사용자에게 문자열을 입력받아서 random 길이의 salt 를 추가하고 정해진 반복횟수만큼의 해시 함수를 돌려서 일정한 길이의 결과를 내주는 함수입니다. 100 | 101 | 단방향 함수이므로 결과로 부터 원문을 유추할 수 없으므로 사용자 암호에 적용하기 좋은 알고리즘입니다. 102 | 103 | PHP 는 [hash_pbkdf2](http://php.net/manual/en/function.hash-pbkdf2.php) 를 사용하면 됩니다. 104 | 105 | 106 | ```php 107 | [!NOTE] 128 | > PHP 5.5 이상은 기본 값이 bcrypt 이므로 *PASSWORD_DEFAULT* 로 설정해도 됩니다. 129 | 130 | ```php 131 | **Tip** 기본 JDK 의 JCE 정책은 미국외에서는 강력한 키 길이(AES256, RSA2048)를 사용할 수 없어서 *"java.lang.SecurityException: Unsupported keysize or algorithm parameters"* 또는 *"java.security.InvalidKeyException: Illegal key size"* 에러가 발생합니다. 206 | 207 | 이를 해결하려면 Oracle 에서 "Unlimited Strength Jurisdiction Policy Files" 파일을 다운로드 받아서 $JRE/lib/security/ 에 복사해야 합니다. 208 | 209 | **AES 256 암호화** 210 | 211 | ```java 212 | String input = "Hello World"; 213 | 214 | KeyGenerator gen = KeyGenerator.getInstance("AES"); 215 | gen.init(256); // 256 key length 216 | 217 | // 대칭키 생성 218 | SecretKey key = gen.generateKey(); 219 | 220 | // Initial Vector 생성 221 | SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); 222 | byte[] iv = new byte[16]; 223 | sr.nextBytes(iv); 224 | 225 | // 암호화 226 | Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 227 | 228 | IvParameterSpec spec = new IvParameterSpec(iv); 229 | 230 | c.init(Cipher.ENCRYPT_MODE, key, spec); 231 | 232 | // 암호화된 데이타 233 | byte[] encData = c.doFinal(input.getBytes()); 234 | 235 | // 복호화 236 | Cipher d = Cipher.getInstance("AES/CBC/PKCS5Padding"); 237 | 238 | d.init(Cipher.DECRYPT_MODE, key, spec); 239 | byte[] decData = d.doFinal(encData); 240 | 241 | System.out.println("ORG: " + input); 242 | System.out.println("DEC: " + new String(decData)); 243 | 244 | ``` 245 | 246 | 247 | 248 | ### PHP 249 | 250 | PHP는 openssl extension 을 사용하여 대칭키 암호를 할 수 있습니다. (mcrypt 는 오래됐고 버그가 많아서 PHP 7 부터 지원이 중지됩니다.) 251 | 252 | ```php 253 | 3 | 4 | ## 비무장지대(DMZ) 5 | 6 | 네트워크에서 비무장지대는 조직의 내부 네트워크와 (일반적으로 인터넷인) 외부 네트워크 사이에 위치한 서브넷을 의미합니다. 7 | 8 | 9 | 외부에 서비스를 제공하면서 내부 네트워크를 보호해야 하는 임무를 띄고 있으며 웹 서버나 메일 서버같이 Well Known 포트를 사용하는 서비스가 위치하게 됩니다. 10 | 11 | ![DMZ](https://cloud.githubusercontent.com/assets/404534/14389496/db118380-fded-11e5-8af7-9c2e0b2baec4.png "DMZ") 12 | 13 | DMZ 영역의 서버는 외부에서 바로 연결 가능하므로 SELinux, 침입 탐지, 방화벽등으로 보호해야 하며 DMZ 서버가 해킹 당했을 경우를 가정해서 내부 네트워크로 연결은 허용된 정책(WAS 에 Reverse Proxy 로 연결등)만 빼고 모두 차단해야 합니다. 14 | 15 | 특히 2차 방화벽은 내부 네트워크를 보호해야 하므로 1차 방화벽과 다른 제품을 사용해야 침입자를 곤란하게 할 수 있습니다. 16 | 17 | > **Caution** DMZ와 내부 네트워크를 분리하고 1, 2차 방화벽을 적용해도 동일 제품이라면 하나의 방화벽과 큰 차이가 없으며 같은 자물쇠를 2개 끼워 놓은 것은 1개보다 별로 안전하지 않은 것과 마찬가지입니다. 18 | 19 | ## iptables 20 | 21 | Linux 커널 2.2 에는 ipchains 이라는 패킷 필터링/방화벽 프레임워크가 구현되어 있었고 2.4부터는 더 유연하고 다양한 기능을 가진 netfilter 로 기능이 교체 되었습니다. 22 | 23 | iptables 는 netfilter 프레임워크의 최상단에 위치하는 사용자 레벨의 프로그램으로 관리자는 이를 이용하여 리눅스 서버로 들어오고 나가는 패킷을 필터링하거나 포트 포워딩을 설정할 수 있으며 방화벽으로도 사용할 수 있다. 24 | 25 | ## firewalld 26 | 27 | RHEL 7 부터는 방화벽을 관리하는 데몬이 firewalld 로 변경되었고 방화벽 설정은 iptables 명령어대신 firewall-cmd (콘솔), firewall-config(X-Windows) 명령어를 사용해야 합니다. 28 | 29 | ## 웹 서버 방화벽 설정 30 | 31 | ### RHEL/CentOS 6 32 | 33 | root 로 실행해야 합니다. 34 | 35 | 1. /etc/sysconfig/iptables 에 다음과 같이 80, 443 정책을 추가합니다. 36 | 37 | ``` 38 | -A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT 39 | -A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT 40 | ``` 41 | 42 | 1. 또는 lokkit 유틸리티로 다음 내용을 추가합니다. 43 | 44 | ``` 45 | lokkit -s ssh -s http -s https 46 | ``` 47 | 48 | 1. 정책을 다시 로드합니다. 49 | 50 | ``` 51 | service iptables restart 52 | ``` 53 | 54 | ### RHEL/CentOS 7 55 | 56 | 1. firewall 명령어로 웹 서버가 사용하는 포트를 방화벽 정책에 등록합니다. 57 | 58 | ``` 59 | firewall-cmd --permanent --zone=public --add-service=http 60 | firewall-cmd --permanent --zone=public --add-service=https 61 | ``` 62 | 63 | 1. 정책을 다시 로드합니다. 64 | 65 | ``` 66 | firewall-cmd --reload 67 | ``` -------------------------------------------------------------------------------- /linux.md: -------------------------------------------------------------------------------- 1 | # 리눅스 설치와 패키지 관리 2 | 3 | 4 | ## 패키지 최소 설치 5 | 6 | 리눅스를 설치할 때 보안을 위해 고려해야 할 사항은 **운영 체제의 사용 용도**(웹 서버, 애플리케이션 서버, DB 서버등)에 맞게 필요한 패키지만 설치하는 것입니다. 7 | 8 | 예로 DB 전용 서버라면 Web Server 나 Samba 서버같은 사용하지 않는 패키지를 설치하지 않도록 해야 합니다. 9 | 10 | FTP나 Samba, Bind 같이 사용하지 않는 서버 프로세스를 구동하는 것은 보안상 문제를 일으킬 수 있으며 운영체제 업그레이드나 패치가 힘들어 지며 재부팅시 오래 걸리는 문제가 있습니다. 11 | 12 | 특히 웹 서버나 메일 서버같이 DMZ 에 설치되는 서버는 최소 설치후 해당 패키지만 설치하는 것이 좋습니다. 13 | 14 | ![CentOS 최소 설치](https://cloud.githubusercontent.com/assets/404534/12508730/fe394fa8-c140-11e5-914c-ffc30fa64078.png) 15 | 16 | **같이 보기** 17 | 18 | * [CentOS minimal 설치후 추가 패키지 설치](https://www.lesstif.com/pages/viewpage.action?pageId=6979710) 19 | 20 | ## 패키지 매니저 사용 21 | 22 | 패키지를 설치할 때 source 를 다운 받아서 컴파일 해서 설치하는 것은 리눅스의 빌드 환경을 이해할 수 있고 최신 버전의 패키지를 사용할 수 있다는 장점이 있지만 버그나 보안 패치를 적용하기 어렵고 특히 서버의 대수가 늘어나면 많은 시간이 소요되는 단점이 있습니다. 23 | 24 | 새로운 패키지가 필요하거나 높은 버전의 패키지가 필요하다면 외부 저장소에서 먼저 찾아보는 것을 권장합니다. 25 | 26 | 저장소를 찾을 때는 제조사에서 직접 제공하는 저장소가 있는지 확인한 후에 없을 경우 3rd party 저장소를 찾는 것을 권장합니다. 27 | 28 | 예로 nginx 나 [MySQL](https://dev.mysql.com/downloads/mysql/) 은 RHEL/CentOS 와 Ubuntu용 저장소를 제공합니다. 29 | 30 | RHEL/CentOS 는 다음 저장소가 유명합니다. 31 | 32 | * [EPEL - Extra Packages for Enterprise Linux](http://fedoraproject.org/wiki/EPEL) 33 | * [REMI](http://rpms.remirepo.net/) 34 | * [Web Tatic](http://www.webtatic.com/) 35 | 36 | Ubuntu는 [PPA - Personal Package Archives](https://launchpad.net/ubuntu/+ppas) 에서 패키지를 설치하면 됩니다. 37 | 38 | 39 | **같이 보기** 40 | 41 | * [RHEL에 EPEL 과 Remi/WebTatic Repository 설치하기](https://www.lesstif.com/pages/viewpage.action?pageId=6979743) 42 | * [nginx 패키지로 설치](https://www.lesstif.com/pages/viewpage.action?pageId=25100304#RHEL/CentOS에nginx설치-NginxRepo에서설치(추천)) 43 | 44 | ## 미사용 패키지 삭제 45 | 46 | 이미 설치해서 사용중인 시스템이라면 사용하지 않는 패키지를 삭제하는 것이 좋으며 다음 명령어로 현재 설치된 패키지의 목록을 확인할 수 있습니다. 47 | 48 | **RHEL/CentOS** 49 | 50 | ```sh 51 | yum list installed 52 | ``` 53 | 54 | **Ubuntu** 55 | 56 | ```sh 57 | apt --installed list 58 | ``` 59 | 60 | 특히 운영 시스템은 보안을 위해서 컴파일러등의 개발 도구를 삭제하는 것이 좋습니다. 61 | 62 | **RHEL/CentOS** 63 | ```sh 64 | yum groupremove "Development tools" 65 | ``` 66 | 67 | **Ubuntu** 68 | ```bash 69 | sudo apt-get purge gcc g++ gdb 70 | ``` 71 | 72 | 73 | ## X Windows 삭제 74 | 75 | 이미 설치되어 운영을 하고 있는 서버라면 X-Windows 가 설치되어 있는지 확인하고 설치되어 있다면 삭제하는 게 좋습니다. 76 | 77 | X-Windows 는 용량이 크고 의존성 있는 패키지가 많으므로 잦은 업데이트가 발생하게 되며 XDM 은 보안상 문제가 발생할 수 있습니다. 78 | 79 | 만약 X-Windows 가 없으면 명령어를 입력하지 못하는 운영자라면 계속 같이 일하는 것을 고민해 보기 바랍니다. 80 | 81 | X-Windows 가 설치되어 있다면 먼저 *telinit 3* 명령어로 런 레벨을 3으로 변경한 후에 삭제해야 합니다. 82 | 83 | 우분투 서버는 X Windows 를 포함하고 있지 않으며 CentOS 에서는 다음 명령어로 X-Windows 패키지를 삭제할 수 있습니다. 84 | 85 | ```sh 86 | yum groupremove "X Window System" 87 | ``` 88 | 89 | **같이 보기** 90 | 91 | * [yum 주요 사용법 및 고급 사용법 (history 관리, plugin 사용, 트랜잭션 undo 등)](https://www.lesstif.com/pages/viewpage.action?pageId=6979667) 92 | 93 | 94 | ## 구동 프로세스 최소화 95 | 96 | 부팅시 자동 구동되는 데몬 프로세스중 사용하지 않는 프로세스는 자동 구동을 끄는 것이 좋습니다. 97 | 98 | 배포판 및 버전마다 자동 구동 프로세스 관리 방법이 다르므로 아래 내용을 참고해서 사용하는 배포판에 맞게 적용하십시요. 99 | 100 | ### RHEL/CentOS 6 101 | 102 | RHEL/CentOS 6 에서는 chkconfig 명령으로 서비스를 제어할 수 있습니다. 103 | 104 | ```bash 105 | # chkconfig --list 106 | 107 | auditd 0:off 1:off 2:on 3:on 4:on 5:on 6:off 108 | blk-availability 0:off 1:on 2:on 3:on 4:on 5:on 6:off 109 | cgconfig 0:off 1:off 2:off 3:off 4:off 5:off 6:off 110 | cgred 0:off 1:off 2:off 3:off 4:off 5:off 6:off 111 | crond 0:off 1:off 2:on 3:on 4:on 5:on 6:off 112 | exim 0:off 1:off 2:on 3:on 4:on 5:on 6:off 113 | ``` 114 | 115 | *0:off 1:off 2:on 3:on 4:on 5:on 6:off* 의 의미는 run level 이 0, 1 일때는 구동되지 않고 2,3,4,5 일때는 구동된다는 의미입니다. 116 | 117 | 자동 시작을 끄려면 *chkconfig* 명령어 뒤에 서비스 명을 입력하고 off 옵션을 주면 됩니다. 118 | 119 | ```sh 120 | chkconfig mysqld off 121 | ``` 122 | 123 | ### RHEL/CentOS 7, Ubutun 15+ 124 | 125 | RHEL/CentOS 7 과 ubunt 15, 16 은 systemd 관리 명령어인 *systemctl* 를 사용하여 서비스 목록을 확인할 수 있습니다. 126 | 127 | ```bash 128 | # systemctl list-unit-files 129 | 130 | UNIT FILE STATE 131 | proc-sys-fs-binfmt_misc.automount static 132 | dev-hugepages.mount static 133 | dev-mqueue.mount static 134 | proc-fs-nfsd.mount static 135 | proc-sys-fs-binfmt_misc.mount static 136 | sys-fs-fuse-connections.mount static 137 | sys-kernel-config.mount static 138 | sys-kernel-debug.mount static 139 | tmp.mount masked 140 | var-lib-nfs-rpc_pipefs.mount static 141 | brandbot.path disabled 142 | systemd-ask-password-console.path static 143 | systemd-ask-password-wall.path static 144 | session-1.scope static 145 | session-3.scope static 146 | arp-ethers.service disabled 147 | auditd.service enabled 148 | ``` 149 | 150 | 불필요한 서비스는 *systemctl disable 서비스명* 을 사용하여 자동 시작을 중지할 수 있으며 활성화할 경우 *disable* 대신 *enable* 을 사용하면 됩니다. 151 | 152 | 예로 다음 명령어는 mariadb 서비스를 자동 실행하지 않습니다. 153 | 154 | ```bash 155 | systemctl disable mariadb 156 | ``` 157 | 158 | 특정 서비스의 자동 실행 여부는 *systemctl is-enabled 서비스명* 을 사용하면 되며 다음 명령어는 nginx 웹 서버의 자동 실행 여부를 출력합니다. 159 | 160 | ```bash 161 | > systemctl is-enabled nginx 162 | 163 | disabled 164 | ``` 165 | 166 | ### Ubutun 14 167 | 168 | 우분투 14.04 는 *sysv-rc-conf* 명령어로 프로세스를 확인할 수 있습니다 169 | 170 | ```bash 171 | $ sudo sysv-rc-conf --list 172 | apparmor S:on 173 | beanstalkd 0:off 1:off 2:on 3:on 4:on 5:on 6:off 174 | blackfire-ag 0:off 1:off 2:on 3:on 4:on 5:on 6:off 175 | console-setu 176 | cron 177 | cryptdisks 178 | cryptdisks-e 0:on 6:on S:on 179 | ``` 180 | 181 | 자동 실행 여부는 *sudo update-rc.d SERVICENAME defaults* 를 사용하면 되며 다음은 nginx 를 부팅시 자동 실행하도록 설정합니다. 182 | 183 | ```bash 184 | sudo update-rc.d nginx defaults 185 | 186 | Adding system startup for /etc/init.d/nginx ... 187 | /etc/rc0.d/K20nginx -> ../init.d/nginx 188 | /etc/rc1.d/K20nginx -> ../init.d/nginx 189 | /etc/rc6.d/K20nginx -> ../init.d/nginx 190 | /etc/rc2.d/S20nginx -> ../init.d/nginx 191 | /etc/rc3.d/S20nginx -> ../init.d/nginx 192 | /etc/rc4.d/S20nginx -> ../init.d/nginx 193 | /etc/rc5.d/S20nginx -> ../init.d/nginx 194 | ``` 195 | 196 | 자동 실행 제외는 *sudo update-rc.d -f SERVICENAME remove* 를 사용하면 되며 다음은 nginx 를 자동 실행에서 제외합니다. 197 | 198 | ```bash 199 | sudo update-rc.d -f nginx remove 200 | 201 | Removing any system startup links for /etc/init.d/nginx ... 202 | /etc/rc0.d/K20nginx 203 | /etc/rc1.d/K20nginx 204 | /etc/rc2.d/S20nginx 205 | /etc/rc3.d/S20nginx 206 | /etc/rc4.d/S20nginx 207 | /etc/rc5.d/S20nginx 208 | /etc/rc6.d/K20nginx 209 | ``` 210 | 211 | ## 시스템을 최신 상태로 유지 212 | 213 | 패키지 관리자를 사용하여 시스템을 상시 업데이트하여 최신 상태로 유지해야 보안 취약점을 통한 공격에 대응할 수 있습니다. 214 | 215 | RHEL은 yum 으로 시스템을 최신 상태로 유지할 수 있습니다. 216 | 217 | ```bash 218 | yum update 219 | ``` 220 | 221 | 우분투는 아래 명령으로 패키지를 업데이트 할 수 있습니다. 222 | 223 | ```bash 224 | apt-get update 225 | apt-get upgrade 226 | ``` 227 | 228 | 229 | ## 참고 자료 230 | 231 | * [20 Linux Server Hardening Security Tips](http://www.cyberciti.biz/tips/linux-security.html) 232 | -------------------------------------------------------------------------------- /php-security-checklist.md: -------------------------------------------------------------------------------- 1 | # PHP 보안 강화하기 2 | 3 | 4 | 5 | > [!NOTE] 6 | > 많은 웹 서비스들이 PHP 로 개발되었지만 긴급한 비즈니스의 요구사항에 밀려서 보안을 고려하지 못한 경우가 많고 이로 인해 여러 취약점을 갖고 있을수 있습니다. 7 | > 이제 보안은 **"하면 좋고 안해도 그만"**인 선택 기능이 아니라 고객과 비즈니스를 보호하기 위해서 우선적으로 고려해야 할 핵심 구성 요소입니다. 8 | > 이를 위해 PHP 로 웹 서비스를 만들고 운영할 때 알아두어야 할 보안 요구 사항을 정리해 봅니다. 9 | 10 | 11 | ## PHP version update & patch 12 | 13 | PHP 는 웹 개발에 필요한 기능을 제공하는 도구로 출발했기 때문에 실용성이 높지만 언어의 일관성이 부족하고 보안에 취약한 문제가 있었습니다. 14 | 15 | 최신 버전은 성능 개선과 함께 언어적 결함과 발견된 보안 취약점을 해결했으므로 주기적으로 최신 버전으로 갱신 및 패치를 해주는 것이 좋습니다. 16 | 17 | 이를 손쉽게 하기 위해서는 PHP 소스를 직접 컴파일해서 설치하는 것보다는 yum 이나 apt 같은 패키지 매니저를 활용하여 설치하는 방법을 권장합니다. 18 | 19 | RHEL이나 CentOS 6 의 경우 탑재된 PHP 버전이 낮으므로 외부 패키지 저장소를 권장하며 아래에 설명할 AddHandler 문제때문에 WebTatic 이 아닌 Remi 저장소를 추천합니다. 20 | 21 | ## register_globals 사용 금지 22 | 23 | PHP 의 register_globals 기능은 오래전부터 심각한 보안 취약점을 갖고 있는 기능으로 악명이 높았으며 5.4 부터는 제거되었습니다. 24 | 25 | register_globals 을 사용하고 있다면 소스의 해당 부분을 수정하고 PHP 버전을 업그레이드 하세요. 26 | 27 | 28 | ## Apache 의 AddHandler 설정 29 | 30 | > **Danger** 매우 치명적인 보안 취약점이므로 설정을 확인하고 바로 수정하세요. 31 | > 32 | 33 | php 를 Apache http 모듈(mod_php)로 설치할 경우 아파치의 설정에 php 스크립트를 구동하기 위한 설정을 추가해야 합니다. 34 | 추가하는 방법중 AddHandler 지시자를 사용하는 방법이 있습니다. 35 | 36 | ``` 37 | AddHandler php5-script .php 38 | AddType text/html .php 39 | ``` 40 | 41 | 위와 같이 설정할 경우 파일명에 php 가 있을 경우 실행해 버리는 문제가 있으므로 공격자는 원격지에서 임의의 php 코드를 실행할 수 있습니다. 42 | 예로 웹 서버의 DocumentRoot 에 *info.php.txt* 있을 경우를 생각해 봅시다. 43 | 44 | ```sh 45 | echo " /var/www/html/info.php.txt 46 | ``` 47 | 48 | 이제 브라우저에서 info.php.txt 를 호출하면 확장자가 .txt 이지만 php code 가 실행되는 것을 볼 수 있습니다. 49 | 만약 서비스에 파일 업로드 기능이 있다면 공격자는 이 기능을 사용하여 임의의 php 를 올리고 원격에서 실행할 수 있습니다. 50 | 51 | 좋은 해결책은 mod_php 를 사용하지 않고 **fastcgi 모듈인 php-fpm 을 사용하여 web server 의 스크립트 실행 기능을 차단**하는 것입니다. 52 | mod_php 를 계속 사용해야 한다면 *AddHandler* 지시자를 삭제하고 *FilesMatch* 지시자와 *SetHandler* 를 사용해야 합니다. 53 | 54 | ``` 55 | 56 | SetHandler application/x-httpd-php 57 | 58 | ``` 59 | 60 | 외부 저장소에서 PHP 를 설치할 경우 [WebTatic](https://webtatic.com/) 은 AddHandler 문제가 있기때문에 대신 [remi 저장소](http://rpms.famillecollet.com/) 사용을 권장합니다. 61 | 62 | * [RHEL/CentOS 5,6,7 에 EPEL 과 Remi/WebTatic Repository 설치하기](https://www.lesstif.com/pages/viewpage.action?pageId=6979743#RHEL/CentOS5,6,7에EPEL과Remi/WebTaticRepository설치하기-Remirepository설치) 참고 63 | 64 | ## Database 65 | 66 | 웹 서비스의 많은 기능들이 DBMS 와 상호 작용을 통해 이루어 집니다. PHP 로 DBMS 를 다룰 경우 보안을 위해 고려해야 할 사항을 정리합니다. 67 | 68 | ### Prepared Statements 와 Bound Parameters 사용 69 | 70 | client 에서 넘어온 파라미터를 바로 SQL 에 사용하면 SQL injection 취약점이 생길 수 있습니다. 이를 방지하려면 코드 작성시 client 가 전달한 파라미터를 binding 해서 71 | SQL 로 해석되지 않도록 해야 합니다. 72 | 73 | ```php 74 | // prepare and bind 75 | $stmt = $conn->prepare("INSERT INTO users (firstname, lastname, email) VALUES (?, ?, ?)"); 76 | $stmt->bind_param("sss", $firstname, $lastname, $email); 77 | ``` 78 | 79 | ### PDO 사용 80 | 81 | PHP 에서 DBMS 를 다루기 위한 범용 인터페이스인 PDO 를 사용하면 기본적으로 bind parameter 를 사용하므로 실수에 의한 SQL Injection 취약점이 적어집니다. 82 | 83 | ## Framework 도입 84 | 85 | 이미 개발된 서비스는 어렵겠지만 신규로 개발시 프레임워크를 도입하는 것을 고려하는 것이 좋습니다. 86 | Laravel 등 PHP 프레임워크는 SQL Injection, XSS, CSRF 등 잘 알려진 보안 취약점에 대응하여 설계되었으므로 사용하는 것이 좋습니다. 87 | 88 | ## 같이 읽기 89 | 90 | * [OWASP's PHP Security Cheat Sheet](https://www.owasp.org/index.php/PHP_Security_Cheat_Sheet) 91 | * [Survive The Deep End: PHP Security](https://github.com/padraic/phpsecurity) 92 | -------------------------------------------------------------------------------- /public-key-encryption.md: -------------------------------------------------------------------------------- 1 | # 공개키 암호화 2 | 3 | 4 | 5 | ### 공개키 암호화 6 | 7 | 공개키 방식(Public Key)의 암호는 암호학적으로 연관된 두 개의 키를 만들어서 하나는 자기가 안전하게 보관하고 다른 하나는 상대방에게 공개하는 식으로 이루어집니다. 8 | 9 | 이때 본인만 갖고 있는 키를 개인키(Private Key)라고 하며 상대방에게 공개하는 키는 공개키(Public Key) 라고 합니다. 10 | 11 | 암호화하는 키와 복호화하는 키가 다르므로 공개키 방식 암호화는 비대칭키(Asymmetric Key) 방식 암호화라고도 부릅니다. 12 | 13 | 대표적인 공개키 알고리즘으로는 전 세계적으로 많이 사용되는 RSA, Elgamal 등이 있으며 전자서명에 사용하는 DSA(Digital Signature Algorithm), KCDSA 등도 공개키 알고리즘에 해당합니다. (DSA, KCDSA 는 암호기능이 없고 전자 서명만 가능합니다.) 14 | 15 | 송신자는 상대방과 공개 키 기반의 암호화된 통신을 할 경우 다음 그림과 같은 절차를 거치게 됩니다. 16 | 17 | ![공개키 방식 암호화](https://www.lesstif.com/download/attachments/18219486/image2014-7-29%2023%3A33%3A40.png?version=1&modificationDate=1406644244000&api=v2 "공개키 방식 암호화") 18 | 19 | 1. 송신자는 수신자의 공개키를 구한다. 20 | 21 | 2. 송신자는 수신자의 공개키로 평문을 암호화 한다. 22 | 23 | 3. 송신자는 암호화된 메시지를 상대방에게 전달한다. 메시지는 암호화되어 있으므로 전달 도중에 유출되거나 도청되도 암호문으로부터 원문을 알아내기가 어렵다. 24 | 25 | 4. 수신자는 자신의 비밀키로 암호화된 메시지를 해독하여 평문을 얻는다. 26 | 27 | 28 | 29 | 공개키로 암호화한 메시지는 수신자의 개인키로만 해독할 수 있으므로 보안 채널을 구성할 때 안전하게 상대방에게 키를 전달할 수 있는 엄청난 장점이 있습니다. 30 | 31 | 하지만 공개키 암호화는 대칭키에 비해 큰 단점이 있습니다. 32 | 33 | 그것은 대칭키 방식에 비해 속도가 비교가 되지 않을 정도로 느리다는 점입니다. 34 | 35 | 그래서 전자 서명이나 간단한 메시지 암호화에는 사용할 수 있지만 실시간 암호화 통신등에는 속도때문에 사용이 힘듭니다. 36 | 37 | 이로 인해 암호화 통신 프로토콜에서는 암호화에 사용할 대칭키(해당 세션에서만 사용하므로 세션 키 라고도 합니다.)를 상대방의 공개키로 암호화해서 안전하게 전달하는 용도로 사용하며, 실제 암호화 통신은 안전하게 전달받은 세션키를 통해 이루어 집니다. 38 | 39 | SSL/TLS 또한 이같은 방식으로 동작합니다. 40 | 41 | ### 키 교환(Key Exchange) 42 | 43 | 암호화 통신을 하기 위한 키를 상대방과 교환하는 것은 공개키 방식 암호화가 발명되기 전에는 매우 어려운 일이었습니다. 44 | 45 | HTTPS 를 사용할 경우 웹 서버에는 RSA 기반의 공개키 인증서를 설치합니다. 46 | 47 | SSL/TLS 에서는 상대방과 키를 교환하기 위한 몇 개의 알고리즘이 있고 가장 간단한 RSA 인증서 기반 키 교환은 다음과 같이 동작합니다. 48 | 49 | 1. 암호화 통신에 사용할 알고리즘 결정 50 | 51 | 1. 클라이언트는 암호화 통신에 세션 키를 랜덤 함수를 사용하여 생성 52 | 53 | 1. 클라이언트는 서버의 인증서에서 공개키를 추출 54 | 55 | 1. 서버의 공개키를 사용하여 2에서 생성한 세션 키를 암호화하여 서버에 전달 56 | 57 | 1. 서버는 개인키를 사용하여 데이타를 해독한 후 세션 키를 추출 58 | 59 | 1. HTTPS 통신 시작 60 | 61 | ![인증서내 공개키](https://cloud.githubusercontent.com/assets/404534/14382662/68894a3c-fdca-11e5-8976-f6e0de604d76.png "인증서내 공개키") 62 | 63 | 64 | ### 암호화 해시 함수 65 | 66 | 암호화 해시 함수(Cryptographic hash function)는 해시 함수의 일종으로 해시 값으로부터 원래 값을 유추하기 어렵게 설계된 함수입니다. 67 | 68 | 임의이 입력이 있을 경우 정해진 길이의 출력을 하는 일방향 함수(One Way function)를 의미하며 동일한 입력에 대해 동일한 출력을 내야 하며 다른 값인데 동일한 출력을 내는 경우(해시 충돌)가 극히 드물어야 합니다. 69 | 70 | 유명한 알고리즘으로는 MD5(보안 문제때문에 암호용으로는 사용 되지 않음), SHA1(현재는 안전하지 않음), SHA-2(SHA256, SHA384, SHA512) 등이 있습니다. 71 | 72 | ## 비밀번호 암호화 73 | 74 | 비밀 번호 암호화는 일반적인 암호화와는 달리 **복호화를 할 필요가 없습**니다. 75 | 76 | 비밀 번호 암호화는 PBKDF 같은 함수를 사용하거나 또는 bcrypt 메서드를 사용하면 됩니다, 77 | 78 | 직접 비밀번호 암호화하는 기능을 구현할 수 있겠지만 검증된 라이브러리를 사용하는 것을 권장합니다. 79 | 80 | 직접 만들어 쓸 경우 다음 사항을 꼭 지키십시요. 81 | 82 | * 안전한 해시 함수(SHA2 이상) 사용 83 | * Random 값을 생성한 후에 Salt 첨가. 84 | 85 | ### PBKDF 86 | 87 | PBKDF(Password-Based Key Derivation Function) 는 사용자에게 문자열을 입력받아서 random 길이의 salt 를 추가하고 정해진 반복횟수만큼의 해시 함수를 돌려서 일정한 길이의 결과를 내주는 함수입니다. 88 | 89 | 단방향 함수이므로 결과로 부터 원문을 유추할 수 없으므로 사용자 암호에 적용하기 좋은 알고리즘입니다. 90 | 91 | PHP 는 hash_pbkdf2 를 사용하면 됩니다. 92 | 93 | 94 | ```php 95 | 96 | 97 | ``` 98 | 99 | ### bcrypt 100 | 101 | bcrypt 는 비밀번호 해시에 사용하기 위해 만들어진 알고리즘으로 OpenBSD 에 기본 탑재되어 있습니다. 102 | 103 | PHP 는 password_hash() 의 두 번째 파라미터를 PASSWORD_BCRYPT 로 지정하면 됩니다. 104 | 105 | ```php 106 | **Tip** 기본 JDK 의 JCE 정책은 미국외에서는 강력한 키 길이(AES256, RSA2048)를 사용할 수 없어서 *"java.lang.SecurityException: Unsupported keysize or algorithm parameters"* 또는 *"java.security.InvalidKeyException: Illegal key size"* 에러가 발생합니다. 161 | 162 | 이를 해결하려면 Oracle 에서 "Unlimited Strength Jurisdiction Policy Files" 파일을 다운로드 받아서 $JRE/lib/security/ 에 복사해야 합니다. 163 | 164 | **AES 256 암호화** 165 | 166 | ```java 167 | String input = "Hello World"; 168 | 169 | KeyGenerator gen = KeyGenerator.getInstance("AES"); 170 | gen.init(256); 171 | 172 | // 사용할 대칭키 173 | SecretKey key = gen.generateKey(); 174 | 175 | Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC"); 176 | 177 | IvParameterSpec spec = new IvParameterSpec(iv); 178 | 179 | c.init(Cipher.ENCRYPT_MODE, key, spec); 180 | 181 | // 암호화된 데이타 182 | byte[] encData = c.doFinal(input.getBytes()); 183 | 184 | ``` 185 | 186 | 187 | 188 | ### PHP 189 | 190 | PHP는 mcrypt 를 사용하여 대칭키 암호를 할 수 있습니다. 191 | 192 | * [KISA 암호 키 관리 안내서](http://seed.kisa.or.kr/iwt/ko/guide/EgovGuideDetail.do?bbsId=BBSMSTR_000000000011&nttId=83&pageIndex=1&searchCnd=&searchWrd=) 193 | -------------------------------------------------------------------------------- /secure-coding.md: -------------------------------------------------------------------------------- 1 | # 시큐어 코딩 2 | 3 | 4 | 5 | > [!NOTE] 6 | > 안전한 SW 를 만들려면 시큐어 코딩을 꼭 익혀야 하며 이를 실무에 손쉽게 적용하기 위해서는 프레임워크 도입을 권장합니다. 7 | 8 | ## SQL Injection 9 | 10 | OWASP 에서 1위로 선정한 위협으로 막기가 어렵지 않고 공격 당했을 경우 파급력이 크므로 꼭 고려해야 할 취약점입니다. 11 | 12 | 만약 사용자 id와 암호를 입력받아서 로그인을 처리하는 페이지가 있을 경우 SQL Injection 을 고려하지 않을 경우 다음과 같이 코딩하게 됩니다. 13 | 14 | ```php 15 | $sql="SELECT * FROM users WHERE userid='$userid' and password='$password'"; 16 | 17 | ``` 18 | 19 | 자바의 경우 20 | 21 | ```java 22 | 23 | String sql = "SELECT * FROM users WHERE userid='" + request.getParameter("id") + "'" + " and password='" + request.getParameter("password") + "'";; 24 | ``` 25 | 26 | 위와 같은 코드는 클라이언트가 보낸 문자열을 검증없이 사용하므로 공격자가 password 에 **' or '1' = '1** 라는 문자열을 넣으면 or 뒤의 문장이 참이므로 id와 암호를 몰라도 관리자로 로그인이 가능해져 버립니다. 27 | 28 | ![SQL Injection](https://www.lesstif.com/download/attachments/24445746/image2016-4-10%2011%3A47%3A0.png?version=1&modificationDate=1460255964000&api=v2 "SQL Injection") 29 | 30 | 31 | 이 공격을 막으려면 위와 같이 동적 쿼리를 사용하지 말고 준비된 문장(Prepared Statement)와 매개변수 바인딩(Bound Parameter) 을 사용해야 합니다. 32 | 33 | PHP 의 경우 mysql_* 같은 직접 데이타베이스에 연결하는 함수보다는 PDO 를 사용하는 게 좋으며 Java 의 경우 JDBC 직접 사용보다는 MyBatis나 Hibernate 같은 프레임워크를 사용하는 것이 좋습니다. 34 | 35 | PHP 라라벨의 경우 쿼리 빌더를 사용하여 아래와 같은 쿼리를 입력한 경우 36 | 37 | ``` 38 | DB::select('select * from users where id = :userid and password = :password', ['userid' => 'myid', 'password' => 'mypasswd']); 39 | ``` 40 | 41 | 다음과 같이 변환되므로 안전하게 실행됩니다. 42 | 43 | 44 | ```php 45 | $s = $dbh->prepare('SELECT * FROM users WHERE userid = :userid and password = :password') ; 46 | $s->bindParam(':userid ', $userid ); 47 | $s->bindParam(':password', $password); 48 | ``` 49 | 50 | ## 인증 및 세션 관리 취약점 51 | 52 | 인증이나 세션은 적절하게 보호되어야 하며 다음과 같이 관리되면 안 됩니다. 53 | 54 | 1. 사용자 인증 정보를 해시나 암호화로 보호하지 않음 55 | 56 | 1. 세션 ID 를 GET 파라미터로 전달하여 URL 에 노출 57 | 58 | ``` 59 | http://example.com/user/login;jsessionId=39ju9jcsejcj3r 60 | ``` 61 | 1. 세션 타임 아웃이 설정되지 않았거나 너무 긴 경우 62 | 63 | 1. 세션에 추가되는 정보가 유출 가능한 경우. 예로 관리자로 로그인했을 경우 admin=1 이 설정 64 | 65 | 위 문제를 해결하려면 HTTPS 를 적용하고 세션 데이타를 암호화 하는 게 좋습니다. 66 | 67 | 라라벨의 경우 다음과 같이 자동으로 세션 값을 암호화해서 전달합니다. 68 | 69 | ``` 70 | HTTP/1.1 200 OK 71 | Server: nginx/1.9.9 72 | Content-Type: text/html; charset=UTF-8 73 | Connection: keep-alive 74 | Cache-Control: no-cache 75 | Date: Fri, 08 Apr 2016 17:40:42 GMT 76 | Set-Cookie: XSRF-TOKEN=eyJpdiI6IlpONm1OKzBkSGw5WVBOeHZFRzc0WGc9PSIsInZhbHVlIjoiSzBnc01DVXlwVFwvUjc1NnA5YW93NllST3MrYUtYenZMSE5ERG9Ha0hcL2tFWEhMS3d5bjVtb1NBN296RW9EeG5EZ2t1RGdGUTVGZjZXQ2l5bm1wOUFTZz09IiwibWFjIjoiOWY2MjhlZGFlMDFkODYzMGZhMWQwM2Y3MTFiYTBkNGVhMGNhMzQzMmViOWVjNjY3ZmYwMWNmOTFlNTA3NGFjMyJ9; expires=Fri, 08-Apr-2016 19:40:42 GMT; Max-Age=7200; path=/ 77 | Set-Cookie: laravel_session=eyJpdiI6IldzcTFBbEpTTk9sZXJyUkRjbW9GblE9PSIsInZhbHVlIjoiNlp6RnVmdUx1eFwvVXVwY2xwVEE1bjN5eW9lbm9cL3BqXC9GVUNESTA1bmdcL1NHWkhYRDJ5U0JRbGtqTEFYV09QcVZuYUNTVFZzME56bkVZdUErNDlCNnZnPT0iLCJtYWMiOiIyODkyYzk5YmQ1MjBjNDQ2YTRlYWU3Y2I1Yjk1OTdhOWU5MWFmNmRlYzQ3ZWM1MDEwNTdhNWIzMjA1NGI0ZTBjIn0%3D; expires=Fri, 08-Apr-2016 19:40:42 GMT; Max-Age=7200; path=/; HttpOnly 78 | ``` 79 | 80 | ## 크로스 사이트 스크립팅(XSS; Cross Site Scripting) 81 | 82 | XSS 는 사용자의 입력값을 사용하여 동적인 내용을 표시하는 웹 페이지에서 외부 입력값에 대한 검증 없이 사용자에게 출력으로 내보내는 경우에 발생합니다. 83 | 84 | 즉 사용자가 글 쓰기가 가능한 게시판을 만들 경우 다음과 같이 게시글을 입력했을 때 이 글을 클릭한 사용자에게 바로 출력할 경우 자바스크립트가 실행되게 됩니다. 85 | 86 | ``` 87 | 88 | ``` 89 | 90 | 위와 같은 취약점을 XSS 라 하며 이를 사용하여 공격자는 악성코드를 유포하거나 사용자의 쿠키를 갈취할 수 있습니다. 91 | 92 | 사용자의 입력은 꼭 소독해서 사용해야 합니다. 93 | 94 | ![소독]( 95 | https://www.lesstif.com/download/attachments/24445478/image2015-6-13%2012%3A3%3A20.png?version=1&modificationDate=1434163785000&api=v2 "소독") 96 | 97 | PHP 는 htmlentities() 함수를 사용하여 입력 값을 소독할 수 있으며 Java 의 경우 OWASP 의 [java encoder](https://github.com/OWASP/owasp-java-encoder) 라이브러리나 네이버의 [lucy-xss-filter](https://github.com/naver/lucy-xss-filter)를 사용하면 됩니다. 98 | 99 | ## 취약한 직접 객체 참조 100 | 101 | 얼마전 모 서점에서 주문 번호만 입력하면 다른 이의 배송 정보와 결재 정보를 그대로 볼 수 있는 취약점이 있었습니다. [1](#footnote1) 102 | 103 | 이런 문제를 해결하려면 사용자가 직접 입력한 객체의 소유자를 검증하고 인증 여부를 확인해야 합니다. 104 | 105 | ```php 106 | 119 | ``` 120 | 121 | 위와 같은 코드를 app 로직마다 추가해야 하는데 실수로 빼 먹으면 보안 문제가 발생하므로 프레임워크를 도입하고 필터나 미들웨어같이 app 로직 전에 수행되는 기능을 사용하여 처리하는 게 좋습니다. 122 | 123 | ![라라벨 미들웨어 구성도](https://www.lesstif.com/download/attachments/24445339/image2015-11-13%2013%3A13%3A7.png?version=1&modificationDate=1447387938000&api=v2 "라라벨 미들웨어 구성도") 124 | 125 | ## 크로스 사이트 요청 변조(CSRF) 126 | 127 | CSRF 는 인증된 사용자를 통해 공격자가 원하는 명령을 수행하게 하는 기법입니다. 128 | 129 | 공격자가 메일이나 사이트 게시글로 이미지나 링크를 전송하여 물품구매를 수행하거나 사이트 글을 변조하는 등의 작업을 수행할 수 있습니다. 130 | 131 | CSRF 를 막기 위해서는 인증된 사용자라 하더라도 중요한 작업을 수행할 경우 다시 검증을 수행하도록 하는 방법입니다. 132 | 133 | 즉 POST/PUT/DELETE 등의 HTTP Method 수행시 FORM 등에 추측이 불가능한 임시 토큰을 삽입한 후에 서버에서 검증하면 됩니다. 134 | 135 | 프레임워크가 없이 이 공격을 막으려면 app 소스를 많이 수정해야 하므로 꼭 프레임워크 도입을 권장합니다. 136 | 137 | 138 | 라라벨의 경우 POST/PUT/DELETE HTTP Method 를 수행하면 자동으로 서버에서 CSRF 토큰을 검증하고 에러가 발생시 예외 처리하도록 동작하고 있으며 app\Http\Kernel.php 에 VerifyCsrfToken 미들웨어가 이 처리를 담당합니다. 139 | 140 | ```php 141 | protected $middlewareGroups = [ 142 | 'web' => [ 143 | \App\Http\Middleware\EncryptCookies::class, 144 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, 145 | \Illuminate\Session\Middleware\StartSession::class, 146 | \Illuminate\View\Middleware\ShareErrorsFromSession::class, 147 | \App\Http\Middleware\VerifyCsrfToken::class, 148 | \App\Http\Middleware\OwnerCheck::class, 149 | ], 150 | ]; 151 | ``` 152 | 153 | 154 | 155 | ## 컴포넌트의 취약점 점검 156 | 157 | "알려진 취약점이 있는 컴포넌트 사용(Using Components with Known Vulnerabilities)" 은 158 | OWASP 의 2013년 Top 중 9위인 위협으로 컴포넌트, 라이브러리, 프레임워크등의 보안 취약점을 악용하여 공격에 노출되게 됩니다. 159 | 160 | * [Spring Remote Code Execution](https://gist.github.com/benelog/4582041) 161 | * [struts 2 xwork remote](http://blog.o0o.nu/2010/07/cve-2010-1870-struts2xwork-remote.html) 162 | * [PHP 7 Format string vulnerability](http://www.cvedetails.com/cve/CVE-2015-8617/) 163 | 164 | Linux 는 yum 이나 apt-get 같은 패키지 관리자 기능을 제공하므로 운영 체제나 인프라 SW의 패치가 매우 쉬워졌지만 app 에서 사용하는 library 나 컴포넌트는 변경시 영향도때문에 교체가 쉽지 않습니다. 165 | 166 | 이 취약점을 예방하기 하려면 다음과 같은 방법이 있습니다. 167 | 168 | 1. 개발시 외부 컴포넌트는 패키지 관리자를 사용하여 관리합니다. 패키지 관리자는 사용하는 언어나 프레임워크에 따라 다르며 자바의 경우 maven, gradle, PHP 는 composer, ruby 는 bundle, python 는 pip 등이 있습니다. 169 | 패키지 관리자를 사용하면 특정 컴포넌트를 교체하기가 용이해 집니다. 170 | 171 | 1. 내/외부 컴포넌트들의 버전 및 의존성을 식별할 수 있도록 관리합니다. 빌드된 바이너리의 버전 관리는 [유의적 버전(Semantic Versioning)](http://semver.org/lang/ko/)을 따르는 것이 좋습니다. 172 | 173 | 1. 인터넷진흥원의 [보호나라 & KrCERT](https://www.krcert.or.kr/krcert/secNoticeList.do)나 [CVE 취약점 데이타베이스](https://cve.mitre.org/) 및 보안 메일링리스트등을 구독하여 최신 동향을 파악합니다. 174 | 175 | 1. 상용 repository manager(Sonatype nexus등) 에서는 repos 에 있는 라이브러리를 스캔하여 취약점 보고서를 생성하는 기능 제공하는 경우가 있으므로 여건이 된다면 이런 제품을 사용하는 것이 좋습니다. 176 | ![Sonatype nexus 의 취약점 보고서](https://www.lesstif.com/download/attachments/20775149/image2014-8-21%2023%3A53%3A3.png?version=1&modificationDate=1408632775000&api=v2 "Sonatype nexus 의 취약점 보고서") 177 | 178 | 179 | 180 | 181 | [1] http://www.insight.co.kr/newsRead.php?ArtNo=56987 182 | -------------------------------------------------------------------------------- /security-basic.md: -------------------------------------------------------------------------------- 1 | ## 보안을 강화 하기 위한 접근법 2 | 3 | 4 | 5 | ### Deny All, Permit Some 정책 6 | 7 | "모든 걸 차단하고 필요한 것만 허용"[1](#footnote1)하는 정책은 단순하면서 강력한 정보 보호를 위한 최선의 정책입니다. 8 | 9 | 방화벽의 기본 정책도 이런 접근 방식을 취하고 있으며 만약 새로운 방화벽 정책을 추가한다면 허용된 ip와 포트를 명시적으로 지정하고 나머지는 차단하는 것이 필요합니다. 10 | 11 | 12 | ### whitelist 기반 정책 13 | 14 | 위의 연장선으로 보안 정책을 만들 때 blacklist 정책보다는 whitelist 기반의 정책을 만드는 것이 좋습니다. 15 | 16 | 예로 관리자 기능이 있다면 관리자로 로그인할 수 있는 ip를 화이트리스트 방식으로 관리하는 것이 로그인할 수 없는 ip를 지정하는 블랙리스트 방식보다 안전합니다. 17 | 18 | 웹 애플리케이션을 만들고 있고 첨부 파일 업로드 기능이 있다면 마찬가지의 접근을 하여 허용된 확장자의 파일만 명시적으로 업로드를 허용하고 나머지는 다 차단해야 합니다. 19 | 20 | ### 최악의 상황을 가정하고 시스템 설계 21 | 22 | 우리가 만들고 구축하는 시스템은 최선을 다해서 구현하고 보안에도 신경을 쓴 신뢰성 있는 시스템이겠지만 이것이 끝은 아닙니다. 23 | 24 | 인터넷에는 늘 악의적인 사용자가 존재하고 이들은 우리 시스템을 해킹하여 직접적인 금전적인 이득을 얻거나 2차 경유지로 악용하려고 합니다. 25 | 26 | 이들은 영리하고 부지런하기까지 하므로 우리의 방어를 뚫고 침입할 수가 있으며 시스템을 설계할 때 해킹 당할수 있다고 가정하고 피해를 최소화할 수 있도록 구현해야 합니다. 27 | 28 | 가장 간단하고 효과적인 방법은 웹 서버같이 비무장지대(DMZ; DeMilitarized Zone)에 위치하는 서버는 네트워크를 사내 네트워크와 분리하고 웹 서버의 권한을 낮추고 접근할 수 있는 네트워크를 최소화 하는 것입니다. 29 | 30 | ### 서비스에 필요하지 않은 정보 노출 최소화 31 | 32 | 웹 서비스에 사용중인 운영체제, 웹 서버, WAS, 프레임워크, DBMS를 노출하면 공격자에게 유용한 정보를 제공하며 CVE(Common Vulnerabilities and Exposures) 같은 보안 취약점 DB 를 조회하여 공격할 수 있습니다. 33 | 34 | 위와 같이 서비스에 필요하지 않은 정보는 노출을 최소화하여야 하며 특히 예외가 발생할 경우 Stack Trace 정보를 사용자의 화면에 남기지 않아야 합니다. 35 | 36 | ### 루트 권한 최소화 37 | 38 | 치명적인 사고를 방지할 수 있는 좋은 작업 습관중에 하나는 루트로 실행하는 것을 최소화하는 것입니다. 39 | 40 | 정말 필요한 작업이 아니라면 루트로 로그인 자체를 하지 말고 꼭 sudo 로 해당 작업만 루트 권한으로 실행하는 것이 좋습니다. 41 | 42 | ### 암호화 전에 보유 필요 검토 43 | 44 | 유출되면 민감한 정보라 암호화를 고려하고 있다면 이 정보를 꼭 보유해야 하는지를 먼저 검토해 보십시요. 45 | 46 | 말장난같지만 개인 정보 유출에 가장 확실한 대비책은 암호화가 아니라 불필요하거나 과다한 정보를 보유하지 않는 것입니다. 47 | 48 | 혹시 나중에 어떻게든 비즈니스에 활용할 수 있을 것 같다는 막연한 느낌에 수집하고 있다면 보유 여부를 고민해 본 후에 적절한 암호화 방법을 선택하십시요. 49 | 50 | ### secure coding 을 익히세요. 51 | 52 | 여기서 다루는 [웹 서버 보안](web-server.md) 이나 [SELinux](selinux.md), [iptables/firewalld 방화벽](firewall.md) 은 프로젝트의 중간이나 종료후에도 크게 어렵지 않게 적용할 수 있습니다. 53 | 54 | 하지만 보안을 고려하지 않고 작성된 애플리케이션은 수정하려면 엄청난 시간과 비용이 발생하고 비즈니스를 한 순간에 무너뜨릴수 있습니다. 55 | 56 | 57 | 관리자와 사장님을 설득해서 프로젝트 초기에 시큐어 코딩을 익히고 개발 단계에서 이를 반영하는 것이 장기적으로는 비용이 더 싸게 먹히며 지속적이고 안정적인 비지니스를 수행할 수 있습니다. 58 | 59 | 60 | 61 | **주석** 62 | 63 | * [1]: 외국의 유명 보안 회사중에 [denyall.com](http://denyall.com) 이라는 회사도 있습니다. -------------------------------------------------------------------------------- /seealso.md: -------------------------------------------------------------------------------- 1 | # 같이 보기 2 | 3 | 4 | ### OWASP 10 대 취약점 5 | 6 | 오픈소스 웹 애플리케이션 보안 프로젝트인 OWASP(The Open Web Application Security Project) 에서 주기적으로 발표하는 10 취약점 7 | 8 | * [2013 10대 취약점](https://www.owasp.org/index.php/Top10#OWASP_Top_10_for_2013) 9 | * [한글 PDF](https://www.owasp.org/images/2/2c/OWASP_Top_10_-_2013_Final_-_Korean.pdf) 10 | 11 | ### 행정안전부 자료 12 | 13 | 행안부 시큐어 코딩 및 보안 관련 정책 자료 링크 14 | 15 | * [공개SW를 활용한 소프트웨어 개발보안 점검가이드](http://www.moi.go.kr/frt/bbs/type001/commonSelectBoardArticle.do?bbsId=BBSMSTR_000000000012&nttId=48386) 16 | * [소프트웨어 개발보안가이드(2013.11) -1](http://www.moi.go.kr/frt/bbs/type001/commonSelectBoardArticle.do?bbsId=BBSMSTR_000000000012&nttId=42148) -------------------------------------------------------------------------------- /selinux.md: -------------------------------------------------------------------------------- 1 | # 보안 강화 리눅스(Security-Enhanced Linux) 2 | 3 | 4 | 5 | **한 줄 요약** 6 | 7 | > [!TIP] 8 | > SELinux 는 여러분을 불편하게 하지만 여러분의 시스템을 장악해서 악의적인 용도로 사용하려는 크래커들에게는 큰 좌절을 맛보게 하니 꼭 사용하세요. 9 | 10 | 11 | SELinux 는 RHEL/CentOS 사용자들이 가장 증오하는 기능일 겁니다. 12 | 설치후 제일 먼저 하는 일이 SELinux 를 끄는 것이고 검색 엔진에서도 SElinux 를 치면 끄기가 자동 완성되니까요. 13 | 14 | 심지어 "리눅스에서 서비스를 운영하려면 SELinux를 꺼야 한다" 는 잘못된 지식이 널리 퍼져있습니다. 15 | 16 | 이것은 우리나라만이 아니라 외국도 마찬가지이며 SELinux 의 장점과 중단했을 때의 문제점을 널리 알리기 위한 [stop disabling SELinux](http://stopdisablingselinux.com/) 사이트도 생겨났습니다. 17 | 18 | 이 글은 널리 퍼져있는 SELinux 에 대한 잘못된 정보를 바로 잡고 사용을 장려하기 위해서 작성했습니다. 19 | 20 | 그러면 먼저 SELinux 를 이해하기 위해 필수적인 지식인 접근 통제에 대해서 알아 봅시다. 21 | 22 | ## 접근 통제 23 | 24 | 운영체제에서 접근 통제(Access Control)은 디렉터리나 파일, 네트워크 소켓 같은 시스템 자원을 적절한 권한을 가진 사용자나 그룹이 접근하고 사용할 수 있게 통제하는 것을 의미합니다. 25 | 26 | 접근 통제에서는 시스템 자원을 객체(Object)라고 하며 자원에 접근하는 사용자나 프로세스는 주체(Subject)라고 합니다. 27 | 28 | 즉 */etc/passwd* 파일은 객체이고 이 파일에 접근해서 암호를 변경하는 *passwd* 라는 명령어는 주체입니다. 29 | 30 | ### 임의 접근 통제 31 | 32 | **임의 접근 통제**(DAC;Discretionary Access Control)는 시스템 객체에 대한 접근을 사용자나 또는 그룹의 신분을 기준으로 제한하는 방법입니다. 33 | 34 | 사용자나 그룹이 객체의 소유자라면 다른 주체에 대해 이 객체에 대한 접근 권한을 설정할 수 있습니다. 35 | 36 | 여기서 임의적이라는 말은 소유자는 자신의 판단에 의해서 권한을 줄 수 있다는 의미이며 구현이 용이하고 사용이 간편하기 때문에 전통적으로 유닉스나 윈도우등 대부분의 운영체제의 기본 접근 통제 모델로 사용되고 있습니다. 37 | 38 | 임의적 접근 통제는 사용자가 임의로 접근 권한을 지정하므로 사용자의 권한을 탈취당하면 사용자가 소유하고 있는 모든 객체의 접근 권한을 가질 수 있게 되는 치명적인 문제가 있습니다. 39 | 40 | 특히 **root** 계정은 모든 권한을 갖고 있으므로 root 권한을 탈취하면 시스템을 완벽하게 장악할 수 있으며 권한 탈취시 많이 사용되는 것은 아래에서 설명할 유닉스의 구조적인 2가지 보안 취약점입니다. 41 | 42 | 43 | #### setuid/setgid 문제 44 | 45 | 사용자들의 암호는 /etc/shadow 에 저장되어 있으며 루트만 읽고 쓸수 있습니다. 46 | 47 | 하지만 사용자들은 *passwd* 명령어를 실행하여 자신의 암호를 변경할 수 있고 이때 */etc/shadow* 파일이 수정됩니다. 48 | 49 | 상대방 호스트가 동작하는지 확인하기 위해 사용하는 *ping* 은 ICMP(Internet Control Message Protocol) 패킷을 사용하므로 루트 권한이 필요하지만 일반 사용자도 ping 명령어를 사용하여 상대 호스트의 이상 여부를 확인할 수 있습니다. 50 | 51 | 유닉스 계열의 운영 체제는 실행 파일의 속성에 setuid(**set** **u**ser **ID** upon execution)또는 setgid(**set** **g**roup **ID** upon execution) 비트라는 것을 설정할 수 있으며 이 비트가 설정되어 있을 경우 해당 프로그램을 실행하면 실행 시점에 설정된 사용자(setuid), 또는 그룹(setgid) 권한으로 동작합니다. 52 | 53 | 즉 passwd 나 ping 같이 실행시 루트 권한이 필요한 프로그램은 소유자를 root 로 하고 setuid를 설정하면 실행 시점에 root 사용자로 전환되므로 root 만 가능한 동작을 수행할 수 있습니다. 54 | 55 | ``` 56 | ls -l /bin/ping/ /usr/bin/passwd 57 | ``` 58 | 59 | ![setuid 비트](https://www.lesstif.com/download/attachments/18219472/image2014-10-22%2023%3A28%3A26.png?version=1&modificationDate=1413987928000&api=v2 "setuid 비트") 60 | 61 | 파일의 사용자 퍼미션 부분의 's' 표시는 setuid 비트이며 그룹 퍼미션 부분에 's' 표시가 있을 경우 setgid 비트로 *passwd* 와 *ping* 은 setuid 비트만 설정되어 있는 것을 알수 있습니다. 62 | 63 | 64 | 하지만 setuid 비트가 붙은 프로그램에 보안 취약점이 있을 경우 공격자는 손쉽게 루트 권한을 획득할 수 있는 문제가 있습니다. 65 | 66 | 이 때문에 setuid 비트는 필요하지만 유닉스 시스템의 주요 보안 취약점이었으며 시스템 관리자의 골칫 덩어리였습니다. 67 | 68 | 시스템에 있는 setuid 비트가 붙은 프로그램은 다음 명령어로 찾을 수 있습니다. 69 | 70 | ```sh 71 | find /bin /usr/bin /sbin -perm -4000 -exec ls -ldb {} \; 72 | ``` 73 | 74 | #### 잘 알려진 포트 daemon 문제 75 | 76 | 잘 알려진 포트(well-known port) 는 특정한 쓰임새를 위해서 IANA에서 할당한 TCP 및 UDP 포트 번호의 일부로 1024 미만의 포트 번호를 갖게 됩니다. 77 | 78 | 예로 웹에 사용되는 http(80), 메일 전송에 사용되는 smtp(25), 파일 전송에 사용되는 ftp(20, 21) 등이 잘 알려진 포트입니다. 79 | 80 | 전통적으로 잘 알려진 포트는 루트만이 사용할 수 있으므로 데몬 서비스는 모두 루트의 권한으로 기동됩니다. 81 | 82 | 보안 문제는 여기에서 발생하며 루트로 구동되었으므로 만약 서비스 데몬이 보안 취약점이 있거나 잘못된 설정이 있을 경우 서비스 데몬을 통해서 공격자는 루트 권한을 획득하게 되며 시스템의 모든 자원에 접근이 가능해 집니다. 83 | 84 | ### 강제 접근 통제 85 | 86 | 강제 접근 통제(MAC; Mandatory Access Control)는 미리 정해진 정책과 보안 등급에 의거하여 주체에게 허용된 접근 권한과 객체에게 부여된 허용 등급을 비교하여 접근을 통제하는 모델입니다. 87 | 88 | 높은 보안을 요구하는 정보는 낮은 보안 수준의 주체가 접근할 수 없으며 소유자라고 할 지라도 정책에 어긋나면 객체에 접근할 수 없으므로 강력한 보안을 제공합니다. 89 | 90 | MAC 정책에서는 루트로 구동한 http 서버라도 접근 가능한 파일과 포트가 제한됩니다. 91 | 즉 취약점을 이용하여 httpd 의 권한을 획득했어도 */var/www/html*, */etc/httpd* 등의 사전에 허용한 폴더에만 접근 가능하며 80, 443, 8080 등의 포트만 접근이 허용되므로 ssh로 다른 서버로 접근을 시도하는등 이차 피해가 최소화 됩니다. 92 | 93 | 단점으로는 구현이 복잡하고 어려우며 모든 주체와 객체에 대해서 보안 등급과 허용 등급을 부여하여야 하므로 설정이 복잡하고 시스템 관리자가 접근 통제 모델에 대해 잘 이해하고 있어야 합니다. 94 | 95 | ## SELinux 란 96 | 97 | SELinux는 미국 국가 안보국(NSA; National Security Agency; 또는 농담으로 No Such Agency 라고도 합니다.)에서 개발한 플라스크(Flask)라는 MAC 기반의 보안 커널을 리눅스에 이식한 커널 레벨의 보안 모듈입니다. 98 | 99 | NSA는 구현한 소스를 리눅스 커뮤니티에 기증해서 2.6 버전부터 커널에 공식 포함되었습니다. [1](#footnote1) 100 | 101 | RHEL 기반의 배포판에는 4 버전부터 공식적으로 포함되었으며 이제는 다양한 제품들이 SELinux를 지원하고 있으므로 기본적인 SELinux 개념과 설정을 알고 있다면 사용하는게 크게 어렵지는 않습니다. 102 | 103 | > [!NOTE] 104 | > Android도 보안 문제가 대두되자 최신 버전부터는 SELinux 를 포팅한 SE Android 가 기본 탑재되어 있습니다. 105 | 106 | **SELinux 는 기존 접근 통제 규칙보다 먼저 동작**하므로 SELinux 의 보안 정책에 맞지 않을 경우 차단해 버리게 됩니다. 107 | 108 | 이런 사실을 모를 경우 분명 파일의 소유자가 맞는데 *Permission Denied* 가 나거나 파일이 존재하는데 *File not Found* 등의 에러가 나는 원인을 찾지 못하고 SELinux 를 끄게 되고 정상 동작하는 것을 확인하고 SELinux 를 쓰면 안 된다는 생각을 갖게 됩니다. 109 | 110 | 이는 자동차 주행중에 경고등에 불이 들어왔다고 전구를 빼는 것과 비슷한 행동입니다. 111 | 실제 자동차에 문제가 생겼을 수도 있고 또는 경고등 자체가 고장났을수 있지만 가장 좋은 해결책은 원인을 찾고 적절한 조치를 취하는 것입니다. 112 | 113 | 그동안 SELinux 에 대한 정보와 자료 부족때문에 그런 오해가 있었다면 이 글을 읽고 SELinux 에 대한 인식을 재고하고 왜 서비스가 SELinux 에 차단당했는지 원인을 찾고 적절한 조치를 취할 수 있는 계기가 되었으면 합니다. 114 | 115 | ### 장점 116 | 117 | SELinux 를 사용할 경우 다음과 같은 장점이 있습니다. 118 | 119 | 120 | 1. **사전 정의된 접근 통제 정책 탑재** 121 | 122 | SELinux를 사용하면 사용자, 역할, 타입, 레벨등의 다양한 정보를 조합하여 어떤 프로세스가 어떤 파일, 디렉터리, 포트등에 접근 가능한지에 대해 사전에 잘 정의된 접근 통제 정책이 제공됩니다. 123 | 124 | 그래서 MAC 적용을 위해 시스템 관리자가 할 일이 대폭 줄었고 애플리케이션의 변경없이 setuid와 1024 이하 포트를 사용하는 데몬을 안전하게 사용할 수 있습니다. 125 | 126 | 1. **"Deny All, Permit Some" 정책으로 잘못된 설정 최소화** 127 | 128 | 서두에 설명했듯 "모든 걸 차단하고 필요한 것만 허용"하는 정책은 단순하면서 강력한 정보 보호를 위한 최선의 정책입니다. SELinux 의 보안 정책도 이 방식으로 사전에 설정되어 있으므로 잘못된 설정이 기본 포함돼 있을 여지가 적습니다. 129 | 130 | 1. **권한 상승 공격에 의한 취약점 감소** 131 | 132 | setuid 비트가 켜져 있거나 루트로 실행되는 프로세스처럼 위험한 프로그램들은 샌드박스안에서 별도의 도메인으로 격리되어 실행되므로 루트 권한을 탈취해도 해당 도메인에만 영향을 미치고 전체 시스템에 미치는 영향이 최소화됩니다. 133 | 134 | 예로 아파치 httpd 서버의 보안 취약점을 통해 권한을 획득했어도 아파치같은 서버 데몬은 낮은 등급의 권한을 부여 받으므로 공격자는 일반 사용자의 홈 디렉터리를 읽을 수 없고 /tmp 임시 디렉터리에 파일을 쓸 수가 없습니다. 135 | 136 | 1. **잘못된 설정과 버그로부터 시스템 보호** 137 | 138 | 잘못된 설정이나 신뢰할 수 없는 입력을 악용한 공격에서 프로세스를 보호할 수 있습니다. 139 | 140 | 예로 버퍼의 입력 길이등을 제대로 체크하지 않아서 발생하는 버퍼 오버 플로 공격(buffer overflow attack)의 경우 SELinux 는 어플리케이션이 메모리에 있는 코드를 실행할 수 없게 통제하므로 데몬 프로그램에 버퍼 오버 플로 버그가 있어도 쉘을 얻을 수가 없습니다. 141 | 142 | 143 | ### SELinux 의 한계 144 | 145 | SELinux 의 주요 목표는 잘못된 설정이나 프로그램의 보안 버그로 인해 시스템이 공격 당해도 시스템과 데이타를 보호하고 2차 피해를 막는 것입니다. 146 | 147 | SELinux 는 여러 가지 보안 요소중에 하나이며 SELinux 로 모든 보안 요건이 충족되지는 않습니다. 148 | 149 | SELinux 는 침입 차단 시스템(IPS; Intrusion Protection System), 침입 탐지 시스템(IDS; Intrusion Detection System)이나 바이러스 백신이 아니므로 여러 보안 요소와 혼용하여 사용해야 합니다. 150 | 151 | ## SELinux 사용하기 152 | 153 | ### 동작 모드 154 | 155 | SELinux는 그림과 같이 커널의 기본 기능으로 동작하며 모든 시스템 콜이 보안 정책을 확인해 접근 허용 여부를 판단하며 빠르게 처리하기 위해 보안 정책은 AVC(Access Vector Cache)라는 이름으로 커널 내부에서 캐싱합니다. 156 | 157 | ![SELinux 아키텍처](https://cloud.githubusercontent.com/assets/404534/12506805/d187db34-c134-11e5-85e3-76a71fd3ea9a.png "SELinux 아키텍처") 158 | 159 | 160 | SELinux의 사용 여부는 *sestatus* 명령어를 통해 *Current mode* 에 enforcing 이 있는지 여부로 확인할 수 있습니다. 161 | 162 | ``` 163 | # sestatus 164 | 165 | ELinux status: enabled 166 | SELinuxfs mount: /sys/fs/selinux 167 | SELinux root directory: /etc/selinux 168 | Loaded policy name: targeted 169 | Current mode: enforcing 170 | Mode from config file: enforcing 171 | Policy MLS status: enabled 172 | Policy deny_unknown status: allowed 173 | Max kernel policy version: 28 174 | 175 | ``` 176 | 177 | SELinux 는 enforce, permissive, disable 3 가지 모드가 있으며 기본 설정은 *enforce mode*이고 보안 정책에 위배되는 모든 액션이 차단됩니다. 178 | 179 | *permissive mode* 는 경고 메시지를 내고 차단하지는 않습니다. 180 | 181 | 모드의 전환은 *setenforce * 명령어를 사용하면 되며 옵션으로 1을 주면 **enforce mode** 로 0 일 경우 **permissive mode** 로 전환할 수 있습니다. 182 | 183 | ``` 184 | setenforce 0 185 | ``` 186 | 187 | ![setenforce 0 결과](https://cloud.githubusercontent.com/assets/404534/14386181/be161840-fdde-11e5-86f8-603fccf0a14a.png "setenforce 0 결과") 188 | 189 | 마지막으로 disabled 는 SELinux 를 아예 사용하지 않도록 하는 설정입니다. 190 | 191 | > **Danger** SELinux 를 끄는 것은 절대 권장하지 않지만 /etc/selinux/config 의 SELinux 항목을 아래와 같이 설정하고 재부팅을 하면 됩니다. 192 | 193 | ``` 194 | SELINUX=disabled 195 | ``` 196 | 197 | 198 | > [!WARNING] 199 | > SELinux 를 껐다가 다시 활성화하려면 재부팅이 필요하며 모든 리소스에 대해 보안 레이블을 추가해야 하므로 부팅 시간이 오래 걸릴 수 있습니다. 200 | 201 | 202 | ### Security Context 203 | 204 | SELinux 는 모든 프로세스와 객체마다 보안 컨텍스트(Security Context)[2](#footnote2)이라고 부르는 정보를 부여하여 관리하고 있습니다. 205 | 206 | 이 정보는 접근 권한을 확인하는데 사용되고 있으며 SELinux 를 이해하기 위한 핵심 요소이며 다음 4가지 구성 요소로 이루어져 있습니다. 207 | 208 | | 요소 | 설명 | 209 | | -- | -- | 210 | | 사용자 | 시스템의 사용자와는 별도의 SELinux 사용자이며 역할이나 레벨과 연계하여 접근 권한을 관리하는데 사용. | 211 | | 역할(Role) | 하나 혹은 그 이상의 타입과 연결되어 SELinux 의 사용자의 접근을 허용할 지 결정하는데 사용 | 212 | | 타입(Type) | Type Enforcement의 속성중 하나로 프로세스의 도메인이나 파일의 타입을 지정하고 이를 기반으로 접근 통제를 수행 | 213 | | 레벨(Level) | 레벨은 MLS(Multi Level System)에 필요하며 강제 접근 통제보다 더 강력한 보안이 필요할 때 사용하는 기능. 필자도 모릅니다.| 214 | 215 | 여기서 가장 중요하고 꼭 알아야 할 부분은 타입이며 이는 보안 컨텍스트의 핵심 부분으로 SELinux 를 활성화하면 파일이나 디렉터리등의 객체마다 보안 컨텍스트를 부여하고 있습니다. 216 | 217 | SELinux 가 도입되면서 ls, ps, cp, mv 등의 유틸리티에는 *-Z, --context* 옵션이 추가되었습니다. 218 | 219 | 어떤 용도인지 알아보기 위해 *ls -ldz* 명령어를 실행해 봅시다. 220 | 221 | ``` 222 | ls -ldZ /var/www 223 | ``` 224 | 225 | ![ls -ldZ 결과](https://cloud.githubusercontent.com/assets/404534/14387708/9a47acba-fde5-11e5-8c54-192db13c0fb0.png "ls -ldZ 결과") 226 | 227 | *-Z* 를 붙이면 위에서 설명한 컨텍스트를 확인할 수 있으며 특히 타입(httpd_sys_content_t) 항목을 눈여겨 봅시다. 228 | 229 | 이제 *ps -Z|grep httpd*(또는 httpd 대신 nginx) 명령어를 실행해서 프로세스의 컨텍스트를 확인합시다. 230 | 231 | ``` 232 | ps -Z|grep httpd 233 | ``` 234 | 235 | ![ps -Z 결과](https://cloud.githubusercontent.com/assets/404534/14387980/ccfcd81e-fde6-11e5-81f7-4660d3bdc8b4.png "ps -Z 결과") 236 | 237 | httpd 프로세스는 httpd_t 라는 컨텍스트를 갖고 실행되는 것을 알 수 있습니다. 238 | 239 | ### Type Enforcement 240 | 241 | TE(Type Enforcement)는 SELinux 의 기본적인 접근 통제를 처리하는 매커니즘으로 주체가 객체에 접근하려고 할 때 주체에 부여된 보안 컨텍스트가 객체에 접근할 권한이 있는지 판단하는 역할을 수행합니다. 242 | 243 | 위의 예에서 아파치 웹 서버(httpd)가 /var/www/html/ 에 접근하려고 할 때 아파치 웹 서버는 주체가 되며 /var/www/html/ 는 객체가 되며 아파치 웹 서버에 부여된 보안 컨텍스트는 *httpd\_t* 가 됩니다. 244 | 245 | 객체에 부여된 보안 컨텍스트는 위에서 *ls -Z* 로 보았듯이 *httpd\_sys\_content\_t* 이며 httpd_t 는 httpd_sys_content_t 보안 컨텍스트가 부여된 객체에 접근이 허용되므로 아파치 웹 서버는 /var/www/html/ 디렉터리에 있는 컨텐츠를 읽을 수 있습니다. 246 | 247 | SELinux 에 대한 오해는 바로 위의 Security Context 와 Type Enforcement 때문에 일어납니다. 248 | 249 | 즉 사전에 탑재된 httpd 에 대한 정책에 의하면 *httpd\_sys\_content\_t* 가 붙은 컨텐츠만 읽을 수 있고 /var/www 아래에 파일을 생성하면 자동으로 *httpd\_sys\_content\_t* 가 붙게 됩니다. 250 | 251 | 그러므로 /data/myweb-app 폴더를 만들고 이 안에 웹 서비스할 파일을 넣고 httpd 에 DocumentRoot 를 설정해도 SELinux 는 미리 허용된 경로가 아니므로 차단시켜서 *permission denied* 에러만 만나게 됩니다. 252 | 253 | 마찬가지로 httpd 가 접근할 수 있게 사전에 허용된 context 는 http_port_t 이며 *semanage* 명령어로 해당 포트 목록을 조회할 수 있습니다. 254 | 255 | ```sh 256 | semanage port -l|grep http_port_t 257 | ``` 258 | 259 | 기본적으로 허용된 포트는 아래와 같이 80, 443, 9000, 8000 등입니다. 260 | ``` 261 | http_port_t tcp 9004, 8000, 8080, 10080, 8001, 80, 81, 443, 488, 8008, 8009, 8443, 9000 262 | ``` 263 | 264 | Type Enforcement로 인해 공격자가 "제로 데이 취약점"을 사용하여 httpd 의 권한을 획득해도 다른 서버로 ssh 연결을 할 수 없습니다. 22번 포트는 허용되지 않았기 때문이며 이로 인해 2차 피해를 최소화할 수 있습니다. 265 | 266 | 또 다른 예로 파일 공유 서비스인 삼바와 mysql DBMS 가 같이 구동되는 서버에서 삼바를 공격하여 권한을 탈취했다고 가정해 봅시다. 267 | 268 | 이제 공격자는 삼바를 통해 mysql 데이타베이스 파일을 가져가려고 시도할 수 있습니다. 269 | 270 | ```sh 271 | ls -lZ /var/lib/mysql/ 272 | 273 | -rw-rw----. mysql mysql system_u:object_r:mysqld_db_t:s0 ib_logfile0 274 | -rw-rw----. mysql mysql system_u:object_r:mysqld_db_t:s0 ib_logfile1 275 | -rw-rw----. mysql mysql system_u:object_r:mysqld_db_t:s0 ibdata1 276 | ``` 277 | 278 | SELinux 하에서는 삼바와 mysql 은 별도의 도메인으로 격리되어 동작하며 mysql 데이타는 *mysqld_db_t* context 가 설정되어 있고 삼바는 해당 객체에 접근할 수 없습니다 279 | 280 | 하지만 위 정책으로 인해 웹 서버와 php-fpm 을 연동하는데 포트를 기본 fpm 포트(9000)가 아닌 9001을 사용했거나 톰캣과 연동하는데 8009, 8000이 아닌 포트를 사용했다면 SELinux 가 차단해서 서비스가 되지 않습니다. 281 | 282 | ### context 정보 얻기 283 | 284 | SELinux의 문제를 해결하기 위해서는 context 정보를 확인하고 이를 맞춰주는게 매우 중요합니다. 285 | 이 작업을 용이하게 하기 위해 context 정보를 확인할 수 있는 유틸리티 사용법을 알아 봅시다. 286 | 287 | 먼저 다음 패키지를 설치합니다. 288 | 289 | ``` 290 | yum install setools-console 291 | ``` 292 | 293 | #### seinfo 294 | 295 | seinfo 는 policy를 조회하고 출력해주는 유틸리티입니다. 296 | 297 | 다음 명령어로 현재 정책을 요약해서 볼 수 있습니다. 298 | 299 | ``` 300 | seinfo 301 | ``` 302 | 303 | -a 옵션으로 조회할 속성을 지정할 수 있으며 다음 예제는 전체 도메인을 출력합니다. 304 | 305 | ``` 306 | seinfo -adomain -x 307 | ``` 308 | 309 | #### sesearch 310 | 311 | *sesearch* 는 정책에서 지정한 룰을 조회할 수 있는 유틸리티입니다. 312 | 313 | 다음 명령어는 httpd_sys_content_t 객체에 접근할 수 있는 롤 그룹을 표시합니다. 314 | 315 | ``` 316 | sesearch --role_allow -t httpd_sys_content_t 317 | ``` 318 | 319 | --allow 옵션을 사용하면 특정 context에 허용된 액션을 알 수 있습니다. 320 | 321 | ``` 322 | sesearch --allow -s httpd_t 323 | ``` 324 | 325 | 첫 번째 라인의 출력의 의미는 다음과 같습니다. 326 | httpd_t 는 httpd_sys_content_t 가 설정된 파일에 대해 *ioctl, read, getattr, lock, open* system call 이 가능합니다. 327 | 즉 httpd_t 는 httpd_sys_content_t 컨텐츠를 읽을 수가 있습니다. 328 | 329 | ``` 330 | allow httpd_t httpd_sys_content_t : file { ioctl read getattr lock open } ; 331 | allow httpd_t zoneminder_log_t : file { ioctl getattr lock append open } ; 332 | allow httpd_t httpd_sys_content_t : dir { ioctl read getattr lock search open } ; 333 | allow httpd_t zoneminder_log_t : dir { getattr search open } ; 334 | ``` 335 | 336 | SELinux 는 위와 같이 잘 정의된 사전 정책을 탑재하고 있으므로 꼭 사용하는 것이 좋습니다. 337 | 338 | ### semanage 339 | 340 | SELinux 에서 서비스가 안 도는 것은 보안 정책에 어긋나서이고 위에서 설명한 *seinfo, sesearch* 로 정책을 조회한 후에 조치해야 합니다. 341 | 342 | 예로 mysql을 3307 로 구동했다면 허용된 포트가 아니므로 web 서버가 mysql에 연결할 수 없으며 허용된 포트를 사용하거나 정책을 수정하는 [semanage](https://www.lesstif.com/pages/viewpage.action?pageId=18219476#SELinux사용하기-semange패키지) 명령어로 변경된 정보를 SELinux 에게 알려주면 됩니다. 343 | 344 | *semanage*는 SELinux 의 보안 정책을 조회하고 추가/변경/삭제할 수 있는 명령행 기반의 유틸리티입니다. 345 | 346 | 보안 컨텍스트는 파일이나 네트워크 포트, 네트워크 인터페이스등이며 이중에서 서비스 데몬이 SELinux 에서문제없이 동작하려면 꼭 알아 두어야 할 것이 파일과 네트워크 포트 컨텍스트입니다. 347 | 348 | semanage 는 아래 패키지를 설치해야 사용할 수 있습니다. 349 | 350 | ``` 351 | yum install -y policycoreutils-python 352 | ``` 353 | 354 | **포트 컨텍스트** 355 | 356 | 먼저 포트 컨텍스트 사용법을 익히기 위해 httpd 가 연결할 수 있는 포트 정보를 알아 보겠습니다. 357 | 358 | *semanage* 명령어의 첫 번째는 컨텍스트 이름이 와야 하며 이 경우 port 가 되며 오브젝트 리스트를 보는 -l 옵션을 추가하면 됩니다. 359 | 360 | ``` 361 | # semanage port -l|grep http_port_t 362 | 363 | http_port_t tcp 8081, 8080, 8090, 80, 81, 443, 488, 8008, 8009, 8443, 9000 364 | ``` 365 | 366 | http_port_t 라는 컨텍스트는 tcp 프로토콜로 8080, 80 등의 포트에 연결이 허용된 것을 알수 있습니다. 367 | 368 | 만약 WAS 가 9876 포트를 사용할 경우 등록되지 않았으므로 SELinux 는 웹 서버가 9876 포트에 연결하지 못 하도록 차단하므로 정상적으로 서비스를 할 수 없습니다. 369 | 370 | 이 때 SELinux 를 끄지 말고 semanage 명령어로 포트를 추가해 주면 웹 서버가 9876 포트에 연결할 수 있습니다. 371 | 372 | ``` 373 | semanage port -a -p tcp -t http_port_t 9876 374 | ``` 375 | 376 | 시스템에 따라 다음과 같은 에러가 발생하는 경우가 있습니다. 377 | 378 | ``` 379 | /usr/sbin/semanage: tcp/9876에 대한 포트가 이미 지정되었습니다 380 | ``` 381 | 382 | 이는 9876 에 이미 할당된 context 가 있는 것이므로 포트를 추가하는 *-a* 대신 변경하는 *-m* 옵션을 사용해야 합니다. 383 | 384 | ``` 385 | semanage port -m -p tcp -t http_port_t 9876 386 | ``` 387 | 388 | 포트를 삭제할 경우 *-d* 옵션을 사용하여 삭제할 프로토콜과 컨텍스트, 포트 번호를 명시해 주면 됩니다. 389 | 390 | ``` 391 | semanage port -d -p tcp -t http_port_t 9876 392 | ``` 393 | 394 | **파일 컨텍스트** 395 | 396 | 파일 컨텍스트는 fcontext 아규먼트를 추가하면 되며 웹 서버의 컨텐츠에 지정하는 httpd_sys_content_t가 할당된 파일 경로를 찾아 봅시다. 397 | 398 | ``` 399 | # semanage fcontext -l|grep httpd_sys_content_t 400 | 401 | /var/lib/htdig(/.*)? all files system_u:object_r:httpd_sys_content_t:s0 402 | /var/lib/trac(/.*)? all files system_u:object_r:httpd_sys_content_t:s0 403 | /var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0 404 | /var/www/icons(/.*)? all files system_u:object_r:httpd_sys_content_t:s0 405 | /var/www/svn/conf(/.*)? all files system_u:object_r:httpd_sys_content_t:s0 406 | 407 | ``` 408 | 409 | 410 | 아래에서 3번째 줄을 보면 */var/www(/.*)?* 로 지정되어 있는데 의미는 /var/www 하단에 생성되는 모든 파일과 폴더는 *httpd_sys_content_t* 를 붙이라는 의미입니다. 411 | 412 | 그러므로 웹 서버 컨텐츠가 /opt/mycontent 에 있다면 이후에 이 폴더에 생성한 모든 파일과 폴더에 *httpd_sys_content_t* 를 붙여야 정상적으로 서비스가 가능하므로 다음 semanage 로 fcontext 를 지정하면 됩니다. 413 | 414 | ``` 415 | semanage fcontext -a -t httpd_sys_content_t "/opt/mycontent(/.*)?" 416 | 417 | ``` 418 | 419 | ## 문제 해결 420 | 421 | SELinux 를 사용하려고 할 때 가장 큰 어려움중 하나는 왜 문제가 발생하는지 로그는 어디에 쌓이는지 잘 모르고 로그 메시지 해석이 어려운 점입니다. 422 | 423 | SELinux 관련 로그는 */var/log/audit/audit.log* 에 저장되며 root 만 볼수 있습니다. 424 | 425 | ![SELinux 문제해결 절차](https://cloud.githubusercontent.com/assets/404534/15462125/69f01cee-20fb-11e6-993e-ff5e2047116e.png "SELinux 문제해결 절차") 426 | 427 | 이번 장에서는 로그를 검색하고 원인을 찾도록 도와주는 유틸리티에 대해서 알아 보겠습니다. 428 | 429 | 430 | ### audit2why 431 | 432 | SELinux 의 audit 로그를 왜 차단되었는지 설명과 함께 출력하는 유틸리티입니다. 433 | 434 | 사용은 다음과 같이 옵션으로 audit 파일의 경로를 입력해 주면 됩니다. 435 | 436 | ``` 437 | audit2why < /var/log/audit/audit.log 438 | ``` 439 | 440 | 결과는 아래와 같이 차단된 process 와 원인, 간단한 해결책을 같이 표시합니다. 441 | 442 | ``` 443 | type=AVC msg=audit(1463625135.602:32366): avc: denied { name_connect } for pid=25862 comm="nginx" dest=9001 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=system_u:object_r:tor_port_t:s0 tclass=tcp_socket 444 | 445 | Was caused by: 446 | The boolean httpd_can_network_connect was set incorrectly. 447 | Description: 448 | Allow HTTPD scripts and modules to connect to the network using TCP. 449 | 450 | Allow access by executing: 451 | # setsebool -P httpd_can_network_connect 1 452 | ``` 453 | 454 | ### ausearch 455 | 456 | audit2why 는 모든 로그를 보여주므로 로그가 많이 쌓 여있을 경우 보기가 어렵습니다. 457 | 커널 로그를 검색할 수 있는 ausearch 명령어를 사용하면 기간별, 타입별로 로그를 조회할 수 있습니다. 458 | 459 | > [!NOTE] 460 | > ausearch 를 사용하려면 아래 패키지를 설치해야 합니다. 461 | 462 | ``` 463 | yum install audit 464 | ``` 465 | 466 | *-m* 옵션으로 메시지 타입을 지정할 수 있으며 콤마를 구분자로 여러 개를 지정할 수도 있습니다. 467 | *-ts, --start* 옵션으로 날자를 지정하면 해당 일 이후에 발생한 이벤트를 출력합니다. 468 | 469 | 다음 명령어는 2016년 5월 13일 이후에 발생한 이벤트만 표시합니다. 470 | 471 | ``` 472 | ausearch -m AVC,USER_AVC -ts 05/13/2016 473 | ``` 474 | 475 | > [!NOTE] 476 | > 날자 지정 포맷은 locale 설정에 따라 달라지며 위와 같이 Month/Day/Year 형식으로 지정하려면 en_US 로 설정되어 있어야 합니다. 477 | 478 | ``` 479 | export LANG=en_US.utf8 480 | ``` 481 | 482 | 또는 날자대신 now, recent, today, this-week, this-month, this-year 같은 문자열을 지정할 수 있습니다. 483 | 484 | 다음 명령어는 이번 달에 발생한 이벤트 로그를 출력합니다. 485 | 486 | ``` 487 | ausearch -m AVC,USER_AVC -ts this-month 488 | ``` 489 | 490 | ### sealert 491 | 492 | audit2why 나 ausearch 가 출력하는 이벤트 로그는 익숙하지 않으면 해석과 조치가 어렵습니다. *sealert* 는 이를 쉽게 번역해 주는 유틸리티로 아래 패키지를 설치해야 사용할 수 있습니다. 493 | 494 | ``` 495 | yum install setroubleshoot-server 496 | ``` 497 | 498 | 먼저 *-a* 옵션으로 현재 이벤트 로그를 분석할 수 있습니다. 499 | 500 | ``` 501 | sealert -a /var/log/audit/audit.log 502 | ``` 503 | 504 | 분석이 끝나면 아래와 같이 차단 원인과 조치를 위한 명령어를 이해하기 쉽게 출력해 줍니다. 505 | 506 | ``` 507 | SELinux is preventing /usr/sbin/nginx from name_connect access on the tcp_socket . 508 | 509 | ***** Plugin catchall_boolean (89.3 confidence) suggests ******************* 510 | 511 | If you want to allow HTTPD scripts and modules to connect to the network using TCP. 512 | Then you must tell SELinux about this by enabling the 'httpd_can_network_connect'boolean. 513 | Do 514 | setsebool -P httpd_can_network_connect 1 515 | ``` 516 | 517 | ## 요약 518 | 519 | - 웹 서버같이 [DMZ](firewall.html#비무장지대dmz)에 위치하는 서버는 꼭 SELinux 를 사용하는게 좋습니다. 520 | - SELinux 보안 정책의 복잡도를 줄이기 위해 웹 서버는 static content 전송이나 Reverse Proxy 로만 사용하는 것을 권장하며 이럴 경우 SELinux 정책 수정이 거의 없이(필요시 port 컨텍스트 추가 정도) 사용할 수 있습니다. 521 | - 서비스에 문제가 있고 SELinux 가 원인으로 짐작될 경우 끄는 것보다는 *setenforce 0* 로 잠시 허용 모드로 돌려 놓고 *audit2why, ausearch, sealert* 등을 활용하여 원인을 찾는 것이 좋습니다. 522 | 523 | 524 | ## 같이 읽기 525 | 526 | * [SELinux 로 리눅스를 견고하게 하기](https://www.lesstif.com/pages/viewpage.action?pageId=18219470) 527 | * [SELinux 에러 메시지 및 문제 해결](https://www.lesstif.com/pages/viewpage.action?pageId=12943496) - 자주 발생하는 에러와 해결책 모음 528 | * [Basic SELinux Troubleshooting in CLI](https://access.redhat.com/articles/2191331) - Redhat Customer Portal 529 | * [cp/mv 와 SELinux security context](https://www.lesstif.com/pages/viewpage.action?pageId=14090283) - cp 와 mv 명령어가 SELinux 에서 어떻게 동작하는지 설명 530 | 531 | [1] NSA 는 전 세계적인 감청망을 운용하고 있는 것으로 드러나서 SELinux 에 어떤 백도어가 있는 것 아니냐는 우려가 있을수 있지만 다행히 소스를 기증했기 때고 레드햇사와 리눅스 커뮤니티에서 오랫 기간 검증을 거쳐서 백도어 우려는 안 해도 됩니다. 532 | 533 | [2] 보안 레이블(Security label) 이라고도 합니다. 534 | -------------------------------------------------------------------------------- /ssh-server.md: -------------------------------------------------------------------------------- 1 | # 시큐어 셸(SSH;Secure SHell) 2 | 3 | 4 | 5 | 원격지 연결에 사용하는 텔넷(telnet) 이나 rlogin 프로토콜은 오래되었고 보안상 취약한 프로토콜로 이제는 사용하면 안 됩니다. 6 | 7 | ssh 는 암호화를 사용하여 세션을 보호하고 암호, 공개키, 챌린지 응답등 다양한 인증 방식을 제공하고 있으며 포트 포워딩을 제공하므로 X-Windows가 필요해도 ssh 포트만 열어도 사용할 수 있습니다. 8 | 9 | 많은 리눅스 배포판들이 오래전부터 텔넷 서버를 제거하고 ssh 서버를 기본 원격 로그인 서버로 사용했습니다. 10 | 11 | 그러면 sshd를 더 안전하게 사용할 수 있는 방법을 알아 봅시다. 12 | 13 | ## 방화벽으로 ssh 포트 보호 14 | 15 | ssh의 기본 포트인 22 번 포트는 모든 IP에 대해서 열지 말고 허용된 곳에서만 연결을 허용하도록 해야 합니다. 16 | 17 | 다음은 192.168.10 대역에서 ssh 연결을 허용하는 iptables 의 설정 예제입니다. 18 | 19 | ``` 20 | -A INPUT -s 192.168.10.0/24 -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT 21 | ``` 22 | 23 | ## fail2ban 사용 24 | 25 | Brute force Attack을 막기 위해 일정 횟수용 이상 접근을 시도한 IP 는 [fail2ban](http://www.fail2ban.org/) 을 사용하여 차단할 수 있습니다. 26 | 27 | fail2ban은 침입 방지 시스템(IPS; Intrusion prevention system) 으로 */var/log/auth.log* 나 */var/log/apache/access.log* 를 모니터링하여 로그인을 시도한 IP 를 차단합니다. 28 | 29 | 다른 솔루션과 차이점은 iptables 같은 커널 레벨의 방화벽을 사용하여 로우 레벨에서 차단하는 점이며 ssh외에 http 나 기타 프로토콜에도 사용할 수 있습니다. 30 | 31 | ## 강화된 인증 사용 32 | 33 | id와 비밀번호같은 **지식 기반의 인증**은 인증 정보가 유출되면 무력화되므로 사용자가 인증 요소를 소유하고 있어야 하는 **소유 기반의 인증**(공개키, OTP, 보안 카드등)을 사용하는 것이 좋습니다. 34 | 35 | 비밀번호 인증을 사용하지 않으려면 */etc/ssh/sshd_config* 에 다음 항목을 설정하고 *service sshd restart* 를 실행하면 됩니다. 36 | 37 | ``` 38 | PasswordAuthentication no 39 | ``` 40 | 41 | **암호 인증을 중지하기전에 공개키나 2단계 인증을 설정**해 두어야 합니다. 42 | 43 | ### 공개키 인증 44 | 45 | 공개키 방식의 인증은 키 쌍을 보유하고 있어야 하므로 암호 방식보다 안전합니다. 46 | */etc/ssh/sshd_config* 에 다음과 같이 설정되어 있으면 공개키 인증이 가능합니다.(기본 설정) 47 | 48 | 49 | ``` 50 | PubkeyAuthentication yes 51 | ``` 52 | 53 | 접근이 허용된 공개키는 원격 사이트의 *~/.ssh/authorized_keys* 에 저장되어 있습니다. 54 | 55 | *ssh-copy-id* 명령어를 사용하면 원격지에 사용할 공개키를 등록할 수 있습니다. 56 | 57 | ``` 58 | ssh-copy-id -i ~/.ssh/id_rsa.pub myloginid@myhost.com 59 | ``` 60 | 61 | myloginid 는 로그인하려는 id 이고 myhost.com은 로그인하려는 서버의 주소입니다. 62 | 63 | 64 | 공개키 방식의 인증도 키쌍이 유출되면 다른 이가 접근 가능하며 더욱 견고하게 하려면 *~/.ssh/authorized_keys* 에 다음과 같이 *from* 키워드에 해당 공개키로 접근 가능한 client IP를 기술해 주면 됩니다. 65 | 66 | ``` 67 | from="192.168.10.2" ssh-rsa AAAAB3NzaC1yc2EA...AQWqz myemail@myhost.com 68 | ``` 69 | 70 | ### 2단계 인증 71 | 72 | 2단계 인증(2 factor authentication)을 사용하면 OTP(One Time Password)같이 추가 인증 수단을 통해 ssh 로 로그인할 수 있으므로 더욱 안전합니다. 73 | 74 | 제일 쉽게 사용할 수 있는 OTP는 [google authenticator](https://github.com/google/google-authenticator) 이며 ssh 서버에 설치한 후에 스마트 폰에 app을 설치하여 휴대폰에서 생성된 1회용 비밀번호로 원격지에 로그인할 수 있습니다. 75 | 76 | 더 자세한 내용은 [google authenticator 를 사용하여 Linux ssh 에 OTP 적용하기](https://www.lesstif.com/pages/viewpage.action?pageId=24444948) 을 참고하세요. 77 | 78 | ## 베스천 호스트 사용 79 | 80 | Bastion 호스트는 보호된 네트워크에서 유일하게 외부에 노출되는 내외부 네트워크의 연결 호스트를 의미합니다. 81 | 82 | 운영등의 이유로 외부에서 내부 서버에 ssh 로 연결해야 할 필요가 있을 경우 바로 들어갈 수 있도록 하지 말고 베스천 호스트를 구성한 후에 이 서버를 통해서만 내부에 접근할 수 있도록 구성해야 합니다. 83 | 84 | ![베스천 호스트](http://cloudacademy.com/blog/wp-content/uploads/2015/11/aws-bastion-host-1.png "베스천 호스트") 85 | 86 | 특히 AWS 등의 크라우드를 사용하는 경우 베스천 호스트를 통해 private network 에 위치한 서버에 연결하는 것을 권장합니다. 87 | 88 | 89 | ## 참고 자료 90 | * [fail2ban](http://www.fail2ban.org/wiki/index.php/Main_Page) 91 | * [Hardening ssh Servers](https://feeding.cloud.geek.nz/posts/hardening-ssh-servers/) 92 | * [AWS Security: Bastion Host, NAT instances and VPC Peering](http://cloudacademy.com/blog/aws-bastion-host-nat-instances-vpc-peering-security/) -------------------------------------------------------------------------------- /ssl-tls-https.md: -------------------------------------------------------------------------------- 1 | # SSL-TLS HTTPS 적용 2 | 3 | 4 | 5 | ## SSL-TLS 와 HTTPS 6 | 7 | TLS(Transport Layer Security)는 인터넷 상에서 통신할 때 주고받는 데이터를 보호하기 위한 표준화된 암호화 프로토콜입니다. 8 | 9 | TLS는 넷스케이프사에 의해 개발된 SSL(Secure Socket Layer) 3.0 버전을 기반으로 하며, 현재는 [2018년 8월에 발표된 TLS버전 1.3](https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_1.3)이 최종 버전입니다. 10 | (이전 버전인 1.2는 2008년에 발표되었으므로 10년만에 업그레이드 되었습니다.) 11 | 12 | TLS는 전송계층(Transport Layer)의 암호화 방식이기 때문에 HTTP뿐만 아니라 FTP, XMPP등 13 | 응용 계층(Application Layer)프로토콜의 종류에 상관없이 사용할 수 있다는 장점이 있으며 기본적 14 | 으로 인증(Authentication), 암호화(Encryption), 무결성(Integrity)을 지원합니다. 15 | 16 | ![TLS 아키텍처](https://www.lesstif.com/download/attachments/18219486/image2014-7-30%2023%3A29%3A18.png?version=1&modificationDate=1406730379000&api=v2 "TLS 아키텍처") 17 | 18 | SSL에서 TLS로 이름이 변경된 지 오래됐지만 아직도 사람들은 TLS대신 SSL이라는 표현을 더 많이 19 | 사용하고 있으며 실제로 SSL/TLS의 오픈소스 구현체 프로젝트의 명칭은 아직도 OpenSSL이기도 합니다. 20 | 21 | 또한 SSL/TLS의 가장 주된 적용 대상이 HTTPS다 보니 SSL/TLS를 HTTPS와 혼용하는 경우도 많습니다. 22 | 23 | 이 문서에서는 SSL 이라고 할 경우 SSL 프로토콜, TLS 는 TLS 프로토콜을 의미하며, 보안이 적용된 HTTP는 HTTPS로 지칭하겠습니다. 24 | 25 | SSL/TLS 를 사용하면 [중간자 공격](GLOSSARY.md)[1](#footnote1)과 Packet Spoofing 을 통한 도감청을 막을 수 있으며 통신하는 상대방이 맞는지 인증할 수 있습니다. [2](#footnote2) 26 | 27 | 28 | ## TLS HandShake 29 | 30 | SSL/TLS 세션은 다음 핸드셰이크 과정을 거친 후에 구축됩니다. 31 | 32 | ![TLS 핸드셰이크 절차](https://www.lesstif.com/download/attachments/18219486/image2014-10-24%2013%3A9%3A16.png?version=1&modificationDate=1414123436000&api=v2 "TLS 핸드셰이크 절차") 33 | 34 | 35 | 36 | 1. 클라이언트와 서버는 헬로 메시지로 기본적인 정보를 송수신 (1, 2) 37 | 38 | 1. 서버는 서버가 사용하는 SSL/TLS 인증서를 전달 (3, 4) 39 | 40 | 1. 클라이언트는 암호화 통신에 사용할 대칭키를 생성하고 사이를 서버에 전달(5). 이 과정을 키 교환(Key Exchange) 라고 하며 디피-헬만 키 교환(Diffie–Hellman key exchange) 또는 RSA 를 많이 사용. 41 | 42 | 1. 클라이언트는 암호화 통신에 사용 가능한 암호 알고리즘과 해시 알고리즘 목록을 서버에 전달. (6, 7) 43 | 44 | 1. 서버도 알고리즘 목록을 교환후 핸드셰이크가 종료되며 이제 클라이언트와 서버는 암호화 통신에 필요한 대칭키를 서로 보유.(8, 9) 45 | 46 | 위 과정이 끝나면 SSL 세션이 구축되며 실제 암호화 통신을 시작할 수 있습니다. 47 | 48 | **openssl 로 TLS 정보 엿보기** 49 | 50 | 다음 openssl 명령어를 사용하면 현재 tls 의 자세한 정보를 볼 수 있습니다. 51 | 52 | ```bash 53 | openssl s_client -connect google.com:443 54 | ``` 55 | 56 | 출력 결과를 보면 프로토콜 버전, 암호 알고리즘, 세션 정보등 상세한 정보를 확인할 수 있습니다. 57 | ``` 58 | subject=/C=US/ST=California/L=Mountain View/O=Google Inc/CN=*.google.com 59 | issuer=/C=US/O=Google Inc/CN=Google Internet Authority G2 60 | --- 61 | No client certificate CA names sent 62 | Peer signing digest: SHA256 63 | Server Temp Key: ECDH, P-256, 256 bits 64 | --- 65 | SSL handshake has read 4548 bytes and written 443 bytes 66 | --- 67 | New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256 68 | Server public key is 2048 bit 69 | Secure Renegotiation IS supported 70 | Compression: NONE 71 | Expansion: NONE 72 | No ALPN negotiated 73 | SSL-Session: 74 | Protocol : TLSv1.2 75 | Cipher : ECDHE-RSA-AES128-GCM-SHA256 76 | Session-ID: 43AF49BE4F199E128046F88C1A591732E45AB2B87898F4D80FD232D1F52B46E2 77 | Session-ID-ctx: 78 | Master-Key: 4A20442D07BB216DE4A61E7BD78350B5D1B92AEEDECE5E07229B267BA555D68543BF1EAC08D19F47E98AB3DB270D5438 79 | Key-Arg : None 80 | PSK identity: None 81 | PSK identity hint: None 82 | SRP username: None 83 | TLS session ticket lifetime hint: 100800 (seconds) 84 | TLS session ticket: 85 | ``` 86 | 87 | 88 | 89 | ## 인증서 발급 받기 90 | 91 | HTTPS 용 인증서를 발급받으려면 Verisign 이나 thawte, Comodo 같은 인증서 발급 기관을 통해서 절차에 따라 사이트 인증을 마친 후에 발급받아야 하며 이 과정에서 일정한 비용이 발생합니다. 92 | 93 | 개인 사이트라면 무료로 인증서를 발급하는 프로젝트인 [Lets' Encrypt](https://letsencrypt.org/) 를 통해 3개월 유효기간 인증서를 받을 수 있습니다. 94 | 95 | * [Lets' Encrypt 에서 무료로 발급받기](https://blog.outsider.ne.kr/1178) 96 | 97 | AWS 를 사용한다면 [AWS Certificate Manager](https://aws.amazon.com/ko/certificate-manager/) 를 사용하여 무료로 인증서를 발급받을 수 있습니다. 98 | 99 | > [!NOTE] 100 | > 현재 US East (Northern Virginia)리전에서만 사용 가능합니다. 101 | 102 | ### CSR 생성과 발급 103 | 104 | 공개키 기반(PKI) 은 공개키와 개인키 두 개의 키 쌍을 사용하며 개인키는 절대 유출되어서는 안되며 사용자만 소유하고 있어야 합니다. 105 | 106 | 인증서 발급 기관에 HTTPS 용 인증서를 신청하는 프로세스와 명령어(openssl 사용)는 일반적으로 다음과 같습니다. 107 | 108 | 1. HTTPS 를 적용하려는 서버 또는 신청자의 PC 에서 키쌍을 생성 109 | ``` 110 | openssl genrsa -aes256 -out /etc/pki/tls/private/example.com.key 2048 111 | ``` 112 | 113 | 1. 생성한 공개키를 넣어서 인증서 발급 요청(CSR; Certificate Signing Request) 파일을 만들고 개인키로 전자서명 114 | ``` 115 | openssl req -new -key /etc/pki/tls/private/example.com.key -out /etc/pki/tls/certs/example.com.csr 116 | ``` 117 | 118 | 1. CSR 을 인증서 발급기관에 전송 119 | 120 | 1. 인증서 발급기관은 CSR 에 있는 사용자가 보낸 전자서명을 CSR 에 포함된 공개키로 서명 검증 121 | 122 | 1. 사용자 공개키 검증이 끝났으면 사용자의 공개키와 추가 정보(도메인, 이메일 주소등)를 추가하여 SSL 인증서 발급(정확히는 발급기관의 개인키로 전자서명) 123 | 124 | 1. 생성된 인증서를 웹 서버에 적용 125 | 126 | PKI 에 익숙하지 않은 경우 국내의 HTTPS 인증서 대행 기관들이 키 쌍 생성도 대행해서 인증서를 발급해 주지만 개인키 유출 우려가 있으므로 직접 키 쌍을 생성하고 CSR 을 만들어서 발급 받는 것을 권장합니다. 127 | 128 | 129 | ### 서버에 인증서 올리기 130 | 131 | 인증서 발급이 완료되면 인증서 파일(확장자: .crt, .cert 등)을 서버에 전송하고 웹 서버 설정을 수정하면 됩니다. 132 | 133 | RHEL/CentOS 는 인증서와 개인키를 저장하는 폴더가 다음으로 정해져 있습니다. 134 | 135 | * 인증서 : */etc/pki/tls/certs/* 136 | * 개인키 : */etc/pki/tls/private/* 137 | 138 | 특히 SELinux 를 사용한다면 cert_t 라는 context 가 부여되어 있어야 웹 서버가 읽을 수 있습니다. 139 | 만약 인증서와 개인키가 있는데 웹 서버가 못 읽는다면 *chcon* 명령어로 context 를 변경하면 됩니다. 140 | 141 | ``` 142 | chcon -t cert_t /etc/pki/tls/certs/* 143 | chcon -t cert_t /etc/pki/tls/private/* 144 | ``` 145 | 146 | ### 개인키 passphrase 제거 147 | 148 | 일반적으로 개인키는 유출되어도 안전하도록 [PBKDF2](/encryption.html#pbkdf) 기반으로 암호화되어 저장합니다. 149 | 그래서 웹 서버를 구동하려면 개인키의 암호를 입력해야 하는데 서비스를 운영할 때 문제가 될 수 있습니다. 150 | 151 | 암호화된 개인키는 *openssl rsa in* 명령어를 사용하여 해독할 수 있으며 해독된 개인키는 보안을 위해 *chmod 600* 으로 모드를 변경하는 게 좋습니다. 152 | 153 | ``` 154 | cp /etc/pki/tls/private/example.com.key /etc/pki/tls/private/example.com.key.enc 155 | openssl rsa -in /etc/pki/tls/private/example.com.key.enc -out /etc/pki/tls/private/example.com.key 156 | ``` 157 | 158 | ## 웹 서버 설정하기 159 | 160 | 인증서 발급이 끝났다면 이제 웹 서버에 적용할 순서입니다. 161 | 162 | ### nginx 웹 서버 163 | 164 | nginx 의 가상 호스트에 다음과 같이 ssl 설정을 추가해 주면 됩니다. 165 | 166 | ``` 167 | server { 168 | listen 80; 169 | listen 443 ssl; 170 | server_name example.com 171 | root html; 172 | index index.html index.htm index.php; 173 | 174 | charset utf-8; 175 | 176 | ssl on; 177 | ssl_certificate /etc/pki/tls/certs/example.com.crt; 178 | ssl_certificate_key /etc/pki/tls/private/example.com.key; 179 | ssl_session_timeout 5m; 180 | # TLS1, SSLv2, SSLv3는 보안에 취약하므로 사용하지 마세요 181 | ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1; 182 | # 사용하지 않을 암호 알고리즘은 !로 명시적으로 지정할 수 있습니다.(블랙리스트 방식) 183 | ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; 184 | ssl_prefer_server_ciphers on; 185 | location ~ /\.ht { 186 | deny all; 187 | } 188 | } 189 | ``` 190 | 191 | **중요 키워드** 192 | 193 | - *listen 443 ssl* : SSL-TLS 서비스를 제공할 포트를 지정합니다. 194 | - *ssl on* : SSL-TLS 를 켭니다. 195 | - *ssl_protocols* : 사용할 SSL-TLS 버전을 지정합니다. 196 | - *ssl_ciphers* : 사용할 암호 알고리즘을 지정합니다. 197 | - *ssl_prefer_server_ciphers* : SSL-TLS 협상 과정에서 서버에 설정한 암호 알고리즘을 우선하며 off 일 경우 알고리즘을 약화시켜서 공격할 수가 있으므로 on 으로 설정합니다. 198 | 199 | > [!NOTE] 200 | > 최신 버전의 nginx 에서는 *ssl on* 키워드가 deprecated 되어서 아래와 같은 경고가 나오므로 *listen 443 ssl* 만 사용하면 됩니다. 201 | > nginx: nginx: [warn] the "ssl" directive is deprecated, use the "listen ... ssl" directive instead in 202 | 203 | ### apache 웹 서버 204 | 205 | RHEL/CentOS 의 아파치 웹 서버는 /etc/httpd/conf.d/ssl.conf 에 다음과 같이 가상호스트를 설정합니다. 206 | 207 | ``` 208 | 209 | ServerName example.com 210 | ServerAlias www.example.com 211 | SSLEngine on 212 | # Dropping SSLv2, SSLv3, ref: POODLE 213 | SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 214 | 215 | SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW 216 | #SSLCipherSuite HIGH:!aNULL:!MD5 217 | SSLHonorCipherOrder on 218 | 219 | SSLCertificateFile /etc/pki/tls/certs/example.com.crt 220 | SSLCertificateKeyFile /etc/pki/tls/private/example.com.key 221 | #SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt 222 | 223 | ErrorLog logs/example.com-ssl-error_log 224 | TransferLog logs/example.com-ssl-access_log 225 | CustomLog logs/example.com-ssl_request_log \ 226 | "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" 227 | 228 | 229 | ``` 230 | 231 | **중요 키워드** 232 | 233 | - *VirtualHost *:443* : SSL-TLS 서비스를 제공할 포트를 지정합니다. 234 | - *SSLEngine on* : SSL-TLS 를 켭니다. 235 | - *SSLProtocol* : 사용할 SSL-TLS 버전을 지정합니다. 236 | - *SSLCipherSuite* : 사용할 암호 알고리즘을 지정합니다. 237 | - *SSLHonorCipherOrder on* : nginx 의 ssl_prefer_server_ciphers 와 동일한 의미입니다. 238 | 239 | ### 인증서 경로 구성 240 | 241 | 모든 인증서는 상위 발급 기관과 최상위 인증 기관(CA)이 있으며 verisign 같은 유명 CA 들의 인증서는 브라우저에 내장되어 있으므로 브라우저는 SSL-TLS 통신시 사이트의 인증서가 신뢰하는 CA에서 발급받았고 위변조 되지 않았는지 확인할 수 있습니다. 242 | 243 | ![인증 경로](https://cloud.githubusercontent.com/assets/404534/14711049/d5ddcc28-0812-11e6-975b-1c998d94320a.png "인증 경로") 244 | 245 | 246 | 만약 인증서 발급 기관의 인증서가 브라우저에 기본으로 포함되지 않아서 인증서 경로를 찾지 못했다면 브라우저가 SSL-TLS 연결을 하지 못하는 경우가 있습니다. 247 | 예로 [AlphaSSL](https://www.alphassl.com/support/install-root-certificate.html) 인증서는 모바일 chrome 에서 문제가 발생할 수 있습니다. 248 | 249 | 250 | 이런 문제를 해결하기 위해서는 Intermediate CA certificate 라고 하는 인증 기관 인증서 체인 파일을 웹 서버에 설정해 주어야 합니다. 251 | 252 | 아파치 httpd 에는 [SSLCACertificateFile](http://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcacertificatefile) 라는 지시자가 있으며 여기에 PEM 형식으로 인코딩된 SSL 인증서 발급 기관의 인증서 체인 파일을 설정하면 됩니다. 253 | 254 | ``` 255 | SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt 256 | ``` 257 | 258 | nginx 는 해당 지시자가 없으므로 다음과 같이 사이트의 SSL 인증서와 CA 인증서를 하나의 파일로 만들어 주면 됩니다. 259 | 260 | ```sh 261 | cat example.com.crt example-ca.crt example-rootca.crt > /etc/pki/tls/certs/example.com.chained.crt 262 | ``` 263 | 264 | 합쳐진 인증서 파일을 nginx 의 ssl_certificate 에 지정해 주면 SSL chain 을 구성할 수 있습니다. 265 | 266 | ``` 267 | ssl_certificate /etc/pki/tls/certs/example.com.chained.crt; 268 | ``` 269 | 270 | > **Danger** nginx 는 chain 파일의 첫 번째 인증서를 개인키와 일치한다고 가정하므로 체인의 첫 번째 인증서가 사이트의 SSL 인증서가 아닐 경우 다음과 같이 "*key values mismatch*" 에러가 발생하며 구동이 안 됩니다. 271 | 272 | ``` 273 | SSL_CTX_use_PrivateKey_file(" ... /example.com.key") failed 274 | (SSL: error:0B080074:x509 certificate routines: 275 | X509_check_private_key:key values mismatch) 276 | ``` 277 | 278 | ### http -> https 전환 279 | 280 | 로그인이나 개인 정보 변경등의 일부 기능에만 https 를 적용하는 것 보다는 사이트 전체에 적용하는 것이 관리도 용이하고 보안도 강화됩니다. 281 | 282 | 일반 사용자는 사이트에 연결시에 주소창에 https 를 명시하지 않고 연결하는 경우가 많으므로 http 로 연결했을 경우 https 로 redirect 하도록 설정하는 게 필요합니다. 283 | 284 | 애플리케이션 프레임워크에서 필터나 미들웨어 방식으로 이런 기능을 제공하지만 웹 서버의 redirection 기능을 사용하는 것이 쉽습니다. 285 | 286 | *apache http* 는 다음과 같이 설정합니다. 287 | 288 | ``` 289 | 290 | ServerName example.com 291 | 292 | RewriteEngine on 293 | RewriteCond %{HTTPS} !on 294 | RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R,L] 295 | 296 | ``` 297 | 298 | *nginx* 는 다음과 같이 설정합니다. 299 | 300 | ``` 301 | server { 302 | listen 80; 303 | server_name example.com; 304 | return 301 https://$http_host$request_uri; 305 | } 306 | ``` 307 | 308 | ### SSL 가상호스트와 SNI 309 | 310 | 웹 서버의 멋진 기능중에 하나는 가상 호스트(Virtual Host)로 이 기능을 사용하여 추가 장비없이 새로운 웹 서비스를 시작할 수 있습니다. 311 | 312 | 웹 서버는 브라우저의 HTTP Request Header 에서 Host 헤더를 통해 어떤 가상 호스트의 컨텐츠를 서비스할지 결정합니다. 313 | 314 | 즉 다음과 같이 curl을 수행했을 경우 315 | 316 | ```sh 317 | $ curl -v google.com 318 | ``` 319 | 320 | 아래와 같이 **Host: google.com** 을 보내므로 웹 서버는 정확한 가상 호스트를 결정할 수 있습니다. 321 | 322 | ```sh 323 | GET / HTTP/1.1 324 | Host: google.com 325 | User-Agent: curl/7.43.0 326 | Accept: */* 327 | ``` 328 | 329 | 하지만 SSL/TLS의 경우 HTTP보다 먼저 수행되므로 브라우저가 Host Header 를 보내기 전에 SSL handshaking이 이루어 지고 웹 서버는 첫 번째 SSL 가상 호스트에 설정된 서버 인증서를 전송합니다. 330 | 그래서 SSL/TLS 기반으로 여러 개의 가상 호스트를 설정했을 경우 브라우저에서 SSL 인증서 검증시 인증서와 HostName 이 다르다는 에러가 발생할 수 있습니다. 331 | 332 | 이런 문제를 해결하기 위해 "서버 이름 표시([SNI;Server Name Indication - RFC 4366](http://tools.ietf.org/html/rfc4366#page-9))" 규격이 있으며 대부분의 브라우저, 웹 서버, HTTPS 구현 라이브러리가 SNI 를 지원하므로 SSL/TLS에서도 가상 호스트를 사용할 수 있습니다. 333 | 334 | > [!NOTE] 335 | > Windows XP 와 JDK 6 은 SNI 를 지원하지 않으며 전체 목록은 [위키피디아의 SNI](https://en.wikipedia.org/wiki/Server_Name_Indication#Support)에서 확인할 수 있습니다. 336 | 337 | 338 | ## SSL/TLS 보안 강화하기 339 | 340 | 사이트에 적용한 SSL/TLS 를 더 견고하게 하기 위한 권장 설정. 341 | 342 | ### 최신 버전의 TLS 사용 343 | 344 | SSL 은 보안 취약점이 있으므로 사용하지 말고 TLS 를 사용해야 하며 TLS 도 최신 버전(TLS 1.3)을 사용하는 것이 좋습니다. 345 | 346 | 만약 예전 Browser 를 사용하는 고객이 많아서 최신 버전을 강제하기 곤란하다면 다음과 같이 v1, v1.1, v1.2, v1.3 을 다 사용하도록 하면 브라우저의 지원 여부에 따라 자동으로 적절한 TLS 버전을 사용하여 세션이 구성됩니다. 347 | 348 | nginx 는 아래와 같이 사용할 버전을 지정할 수 있습니다. 349 | 350 | ``` 351 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; 352 | ``` 353 | 354 | 최신 브라우저만 지원하거나 [ssllabs](https://ssllabs.com) 에서 점수를 높게 받고 싶으면 1.2 이상만 지원하도록 설정합니다. 355 | ``` 356 | ssl_protocols TLSv1.2 TLSv1.3; 357 | ``` 358 | 359 | > [!NOTE] 360 | > TLS1.3 은 [nginx 1.13.0](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols) 이상과 [OpenSSL 1.1.1](https://www.openssl.org/news/openssl-1.1.1-notes.html) 이상이 필요합니다. 361 | 362 | **apache httpd** 는 아래와 같이 사용할 버전을 지정할 수 있습니다. 363 | 364 | ``` 365 | # Dropping SSLv2, SSLv3, ref: POODLE 366 | SSLProtocol all -SSLv2 -SSLv3 367 | ``` 368 | 369 | 이보다 좋은 방법은 사용할 프로토콜 버전을 명시적으로 지정하는 것입니다. 370 | 371 | ``` 372 | SSLProtocol TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 373 | ``` 374 | > [!NOTE] 375 | > TLS1.3 은 [apache 2.4.36](https://github.com/apache/httpd/blob/2.4.36/CHANGES) 이상과 [OpenSSL 1.1.1](https://www.openssl.org/news/openssl-1.1.1-notes.html) 이상이 필요합니다. 376 | 377 | ### 강력한 알고리즘 사용 378 | 379 | TLS 는 암호화 통신을 위해 사용할 알고리즘을 협상후 결정하는데 RC4 나 Triple DES 같은 오래된 알고리즘을 사용하면 암호화 통신을 하는 이유가 반감됩니다. 380 | 381 | 크롬의 경우 TLS V1.3 를 사용하더라도 예전 알고리즘이 사용 가능하면 사이트 정보 보기에서 아래와 같은 메시지를 출력하게 됩니다. 382 | 383 | ![약한 알고리즘 사용 경고](https://cloud.githubusercontent.com/assets/404534/12735884/cd7c5eae-c98e-11e5-84d5-315927f8147b.png "약한 알고리즘 사용 경고") 384 | 385 | 알고리즘 설정은 사용하지 않을 취약한 알고리즘을 명시적으로 지정하는 블랙리스트 방식보다는 사용할 강력한 알고리즘을 지정하는 화이트리스트 방식을 권장합니다. 386 | 387 | **nginx** 는 아래와 같이 설정할 경우 강력한 알고리즘을 사용하게 됩니다. 388 | 389 | ``` 390 | ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; 391 | ``` 392 | 393 | **apache httpd** 는 아래와 같이 설정하면 강력한 알고리즘을 사용하게 됩니다. 394 | 395 | ``` 396 | SSLHonorCipherOrder On 397 | SSLInsecureRenegotiation off 398 | SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH 399 | ``` 400 | 401 | ## HSTS(HTTP Strict Transport Security) 402 | 403 | 사이트에 최신 버전의 TLS와 강력한 알고리즘을 사용하여 HTTPS 를 적용하면 중간에서 통신을 가로채도 감청이 거의 불가능합니다. 404 | 405 | 하지만 [중간에 공격자가 끼어 들어 클라이언트의 HTTPS 요청을 HTTP 로 전환](http://noplanlife.com/?p=1418)할 수 있다면 감청이 가능해 집니다. 406 | 407 | ![SSL strip 공격](http://noplanlife.com/wp/wp-content/uploads/2016/03/FG0LEHk-640x387.png "SSL strip 공격 - http://noplanlife.com/wp/wp-content/uploads/2016/03/FG0LEHk-640x387.png") 408 | 409 | 이는 중간자 공격(Man in the middle attack)의 일종으로 **"SSL strip 공격"** 이라고 부릅니다. 410 | 411 | HSTS 는 이런 문제를 해결하기 위해 HTTP 헤더에 **"Strict-Transport-Security"** 가 있으면 브라우저는 무조건 HTTPS 로만 연결하도록 하여 "SSL strip 공격"을 방지하는 표준입니다. 412 | 413 | > [!Warning] 414 | > 사이트에 HSTS 를 적용하면 브라우저가 더 엄격하게 동작하므로 서버 설정에 주의를 기울여야 합니다. 예로 HSTS 사이트의 SSL 인증서가 잘못되었을 경우 "위험을 감수하고 연결" 옵션이 없어집니다. 415 | > ![image](https://cloud.githubusercontent.com/assets/404534/15269802/dca102ec-1a45-11e6-9b46-685bf60b7098.png "잘못된 인증서가 설정된 HSTS 사이트") 416 | 417 | HSTS 를 사용하려면 **"Strict-Transport-Security"** HTTP 헤더를 설정하면 되며 지시자로 세부적인 동작을 지정할 수 있습니다. 418 | 419 | - **max-age = delta-seconds** : 브라우저에게 delta-seconds 로 지정된 시간(단위 초)만큼 HTTPS 를 사용하라는 의미입니다. 개발 단계에서는 값을 아주 작게 설정하고 안정화되면 크게 주는게 좋습니다. 420 | - **includeSubdomains** : HSTS 를 서브 도메인도 적용합니다. 예로 www.example.com에서 *includeSubdomains* 설정이 포함된 HSTS 헤더를 전송했다면 사용자가 http://mail.exampl.com 같이 서브 도메인에 연결할 때도 브라우저는 자동으로 https 연결로 전환합니다. 421 | - **preload** : 브라우저가 해당 사이트를 HSTS 적용 preload list 에 추가합니다. 422 | 423 | 다음은 apache httpd 의 HSTS 설정으로 하루(86400)동안 HSTS를 유지하며 서브 도메인에도 HSTS를 적용합니다. 424 | 425 | ``` 426 | Header always set Strict-Transport-Security "max-age=86400; includeSubdomains; preload" 427 | ``` 428 | 429 | nginx 는 add_header 지시자로 HSTS 를 설정하면 됩니다. 430 | 431 | ``` 432 | add_header Strict-Transport-Security "max-age=86400; includeSubdomains; preload"; 433 | ``` 434 | 435 | ### HSTS 설정 해제 436 | 437 | preload 에 추가한 사이트는 max-age 기간동안 자동으로 https 로 연결하며 웹 서버의 HSTS 헤더를 삭제해도 사용자의 브라우저에는 설정이 유지됩니다. 438 | 439 | 여러 가지 이유로 해제가 필요하다면 사용자가 직접 브라우저의 설정을 수정해야 합니다. 440 | 441 | **Chrome** 442 | 443 | 크롬의 경우 해제하려면 다음 절차를 따르면 됩니다. 444 | 445 | 1. 주소창에 *chrome://net-internals/#hsts* 를 입력하여 설정에 들어갑니다. 446 | 1. **Delete Domain** 에 삭제할 주소를 입력하고 **Delete** 를 클릭합니다. 447 | 1. **Query Domain** 에 주소를 입력하고 **Query** 를 클릭해서 **Not Found** 가 나오는지 확인합니다. 448 | 449 | ![크롬 HSTS 해제](https://cloud.githubusercontent.com/assets/404534/14701735/bbf5749a-07e1-11e6-88ec-b172338c2d24.png "크롬 HSTS 해제") 450 | 451 | ## 요약 452 | 453 | - 위에서 설명한 내용과 추가 설정을 웹 서버별로 상세히 정리해서 제공하는 **[Strong Ciphers for Apache, nginx and Lighttpd](https://cipherli.st/)** 사이트를 참고해서 실제 서버에 적용하세요. 454 | - HSTS 는 일단 적용되면 **max-age 기간동안 자동 적용**되므로 테스트 환경에서 충분히 테스트를 거친 후에 운영 환경에 적용하세요. 455 | - 적용이 완료되었다면 [온라인 SSL-TLS 사이트 분석 서비스](https://www.ssllabs.com/ssltest/analyze.html) 를 통해 견고하게 설정되었는지 확인해 보세요. 456 | 457 | ## 참고 자료 458 | 459 | * [HTTP Strict Transport Security - OWASP](https://www.owasp.org/index.php/HTTP_Strict_Transport_Security) 460 | * [STS(Strict Transport Security) 및 보안 쿠키 설정](https://developers.google.com/web/fundamentals/security/encrypt-in-transit/turn-on-strict-transport-security-and-secure-cookies?hl=ko) 461 | * [The First Few Milliseconds of an HTTPS Connection](http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html) 462 | * [mod_ssl 로 보안 강화하기](https://www.lesstif.com/pages/viewpage.action?pageId=18219486) 463 | * [nginx에 HTTPS/SSL 적용하기](https://www.lesstif.com/pages/viewpage.action?pageId=27984443) 464 | 465 | 466 | [1]: ARP 스푸핑을 통한 피해 및 모범 대응 사례- http://blog.bandisoft.com/132 467 | [2]: 백신 프로그램은 HTTPS 패킷을 검사하기 위해 백신 회사가 발급한 root 인증 기관 인증서를 브라우저에 신뢰하는 인증기관으로 추가하고 TLS 인증서를 발급해서 HTTPS 를 통해 오가는 데이타를 검사합니다. 이 방식은 좋은 용도지만 중간자 공격과 동일합니다. 468 | 469 | -------------------------------------------------------------------------------- /styles/website.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Lora|Nanum+Gothic&subset=korean'); 2 | 3 | h1 { 4 | font-family: 'Lora', 'Nanum Gothic', sans-serif; 5 | text-align:center; 6 | } 7 | 8 | h2 { 9 | font-family: 'Lora', 'Nanum Gothic', sans-serif; 10 | background-color: #EEE; 11 | } 12 | 13 | h3 { 14 | font-family: 'Lora', 'Nanum Gothic', sans-serif; 15 | border-bottom: solid 1px #d9d9d9; 16 | } 17 | 18 | body, html{ 19 | font-family: 'Lora', 'Nanum Gothic', sans-serif; 20 | /* line-height: 200%; */ 21 | font-size: 0.9em; 22 | } 23 | 24 | .a {font-size: 1.0em;} 25 | .b {font-size: 1.2em;} 26 | .c {font-size: 1.4em;} 27 | -------------------------------------------------------------------------------- /web-app-server.md: -------------------------------------------------------------------------------- 1 | # Web Application Server 2 | 3 | WAS(Web Application Server) 보안 고려 사항 4 | 5 | 6 | 7 | ## 일반 사용자로 구동 8 | 9 | 일반적으로 WAS 는 웹 서버 기능도 내장하고 있는 경우가 많지만 웹 서버는 apache httpd 나 nginx 같은 제품을 사용하고 Reverse Proxy 로 WAS 와 연결하는 것이 보안과 확장성 측면에서 더 좋습니다. 10 | 11 | ![Reverse Proxy](https://cloud.githubusercontent.com/assets/404534/14357003/e18f6cee-fd21-11e5-89a0-b2e70b96a518.png "Reverse Proxy") 12 | 13 | WAS 는 웹 서버를 겸용하지 않을 경우 보통 1024 이후의 포트를 사용하므로 root 가 아닌 일반 사용자로 구동해야 합니다. 14 | 15 | ## apache tomcat 16 | 17 | ### 서버 정보 숨기기 18 | 19 | 아파치 톰캣은 기본적으로 아래와 같이 HTTP 헤더에 Server 정보를 전송합니다. 20 | 21 | ```sh 22 | $ curl -v -I localhost:8080 23 | 24 | Server: Apache-Coyote/1.1 25 | Content-Type: text/html;charset=UTF-8 26 | Transfer-Encoding: chunked 27 | ``` 28 | 29 | 이 정보를 가리려면 conf/server.xml 내의 Connector 에 Server 정보를 설정해 주고 재구동하면 됩니다. 30 | 31 | ```xml 32 | 36 | ``` 37 | 38 | 39 | 40 | 톰캣은 404 나 500 같은 에러 발생시 기본 에러 페이지에 다음과 같이 버전을 표시합니다. 41 | 42 | ![톰캣 버전 노출](https://user-images.githubusercontent.com/404534/27223549-e09af85c-52cb-11e7-98ad-1b7b561b65d3.png) 43 | 44 | 정보를 숨기려면 다음 명령을 실행합니다. 45 | 46 | ```sh 47 | cd $TOMCAT_HOME/lib 48 | jar xvf catalina.jar 49 | vi org/apache/catalina/util/ServerInfo.properties 50 | ``` 51 | 52 | 이제 프로퍼티에서 server.info 를 *Apache* 으로 변경하고 저장합니다. 53 | 54 | ``` 55 | server.info=Apache 56 | server.number=7.0.47.0 57 | server.built=Oct 18 2013 01:07:38 58 | ``` 59 | 60 | 톰캣을 다시 구동하고 에러 페이지에 가서 버전이 가려지는지 확인합니다. 61 | 62 | 63 | 64 | 사용중인 톰캣의 버전을 확인하려면 터미널에서 다음 명령어를 입력합니다. 65 | 66 | ```sh 67 | $ bin/version.sh 68 | 69 | Server version: Apache Tomcat/8.0.44 70 | Server built: May 10 2017 17:21:09 UTC 71 | Server number: 8.0.44.0 72 | OS Name: Linux 73 | OS Version: 4.9.27-14.31.amzn1.x86_64 74 | ``` 75 | 76 | 77 | 78 | ### manager 기능 접근 제어 79 | 80 | 톰캣에는 host-manager 라는 관리자 기능이 포함되어 있고 기본 설정은 비활성화입니다. 81 | 82 | 이 기능을 사용하면 웹에서 새로운 Context 를 deploy 할 수 있기 때문에 host-manager Context 를 활성화하는 경우가 있습니다. 83 | 84 | 사용할 경우 default Context name(manager) 을 변경하고 암호를 새로 설정해 사용합니다. 85 | 86 | conf/user.xml 87 | 88 | ```xml 89 | 90 | 91 | ``` 92 | 93 | 그리고 manager 권한으로 연결할 수 있는 IP 를 제한합니다. 94 | 95 | conf/Catalina/localhost/manager.xml 96 | 97 | ```xml 98 | 99 | 100 | 101 | 102 | ``` 103 | 104 | 기본 암호가 설정되어 있고 접근 IP 제한이 안 걸려 있을 경우 외부에서 악성코드를 war 에 담아서 deploy 해서 톰캣을 악성 코드 경유지로 사용할 우려가 있습니다. 105 | 이런 공격을 당했을 경우 tomcat 이 일반 사용자로 구동되었다면 해당 계정을 삭제하면 되지만 root 로 구동되었다면 운영체제부터 새로 설치해야 할 수 있습니다. 106 | 107 | 108 | ## PHP-FPM 109 | 110 | ### cgi.fix_pathinfo 설정 문제 111 | 112 | nginx 와 PHP-FPM 을 동일한 서버에서 같이 사용할 경우 PHP 가 PATH_INFO 환경 변수를 제대로 처리하지 못해서 서버에 hack.gif 라는 파일에 PHP 코드를 작성한 후에 이를 서버에 업로드하고 http://example.com/hack.gif/noexist.php 같이 호출하면 hack.gif 가 PHP 코드로 실행되는 심각한 보안 문제가 있었습니다. 113 | 114 | 최근 버전의 PHP-FPM 은 이 문제가 해결되었고 이를 확인하려면 PHP-FPM 의 pool 설정 파일(배포판마다 다르며 /etc/php/7.0/fpm/pool.d/www.conf, /etc/php-fpm.d/www.conf 등에 위치합니다.)에 다음과 같이 설정되어 있으면 됩니다. 115 | 116 | ``` 117 | security.limit_extensions = .php .php3 .php4 .php5 118 | ``` 119 | 120 | 만약 다음과 같이 빈 값이 설정되어 있다면 심각한 보안 문제가 발생할 수 있으므로 php-fpm 을 내리고 즉시 수정해야 합니다. 121 | 122 | ``` 123 | security.limit_extensions = 124 | ``` 125 | 126 | 이와 별개로 사용자 파일 업로드는 white-list 방식으로 파일 유형을 제한하고 임의의 경로에 업로드하지 못하도록 해야 합니다. 127 | 128 | 그리고 업로드 파일이나 기타 리소스는 URL 을 통해서 바로 접근하는 것을 차단하고 애플리케이션의 특정 기능을 통해서만 접근할 수 있도록 제한해야 합니다. 129 | 130 | 131 | 132 | ## 참고 자료 133 | * [Is the PHP option 'cgi.fix_pathinfo' really dangerous with Nginx + PHP-FPM?](http://serverfault.com/questions/627903/is-the-php-option-cgi-fix-pathinfo-really-dangerous-with-nginx-php-fpm) 134 | 135 | -------------------------------------------------------------------------------- /web-server.md: -------------------------------------------------------------------------------- 1 | # 웹 서버 견고하게 하기 2 | 3 | 4 | 5 | ## 3 Tier 아키텍처 6 | 7 | 웹 서버는 인터넷에서 가장 중요한 인프라 SW중 하나로 HTTP 를 기반으로 사용자가 요청한 컨텐츠를 보내주고 사용자의 입력을 받아서 처리하는 역할을 수행합니다. 8 | 9 | 웹이 발달하면서 동적 컨텐츠를 제공해야 하는 필요성이 대두되었고 이를 위해 다양한 웹용 언어와 프레임워크가 탄생하였고 이런 도구들은 웹 서버 위에서 바로 동작하는 경우가 많았습니다. 10 | 11 | 대표적으로 PHP를 처리하는 *mod_php*, Perl 스크립트를 처리하는 *mod_perl*, python 용 *mod_python* 등이 웹 서버에서 동작하는 확장 모듈입니다. 12 | 13 | 이런 모듈은 웹 서버를 바로 활용할 수 있으므로 설치와 설정이 쉽고 사용하기도 쉬운 장점이 있었지만 서비스의 규모가 커질 경우 확장이 힘들고 웹 서버가 해킹당하면 DBMS도 해킹 당하는 등의 문제점이 나타났습니다. 14 | 15 | 이런 문제를 해결하기 위해서는 HTTP를 처리하는 Web Server, 웹 애플리케이션을 실행하는 WAS(Web Application Server), 그리고 DBMS 로 각각의 계층으로 분리하는 **3 Tier** 방식의 아키텍처로 구성하고 웹 서버의 역할은 최소화하는 것이 좋습니다. 16 | 17 | ### 시스템 구성 18 | 19 | 웹 서버 [DMZ](firewall.html#비무장지대dmz)에 위치시키고 수시로 보안패치와 버그패치를 해주어야 합니다. 20 | 21 | ![DMZ](https://cloud.githubusercontent.com/assets/404534/14389496/db118380-fded-11e5-8af7-9c2e0b2baec4.png "DMZ") 22 | 23 | 특히 웹 서버는 설치된 패키지가 적어야 패치 속도가 빠르므로 [사용하는 패키지만 최소로 설치해하고 사용하지 않는 패키지는 삭제하는 것이 좋으며 특히 컴파일러 같은 개발도구와 X-Windows 관련 패키지는 반드시 삭제](linux.html#미사용-패키지-삭제)하는 것이 좋습니다. 24 | 25 | 웹 서비스 포트(80, 443)를 제외하고는 웹 서버의 모든 포트를 막도록 Inbound 방화벽 규칙을 설정합니다. 26 | 27 | 또 웹 서버는 내부망에 연결이 가능한 서버이므로 웹 서버에서 내부망으로 연결하는 방화벽 규칙은 WAS 의 서비스 포트만 연결 가능하도록 하는 것이 좋습니다. 28 | 29 | 웹 서버에는 [SELinux](selinux.html)나 AppArmor 같은 강제 접근 통제 기능을 적용하는 것이 필요하며 특히 SELinux 는 웹 서버에 대해서 엄격하게 통제하므로 웹 서버에는 꼭 적용하는 것이 좋습니다. 30 | 31 | 예로 SELinux는 웹 서버는 연결 가능한 포트를 미리 정해두고 있으며 허용하는 포트는 다음 명령어로 확인 가능합니다. 32 | 33 | ```bash 34 | # semanage port -l |grep http_port_t 35 | 36 | http_port_t tcp 9001, 9004, 8000, 8080, 10080, 8001, 80, 81, 443, 488, 8008, 8009, 8443, 9000 37 | ``` 38 | 위와 같은 제약을 통해 웹 서버가 해킹당해도 이를 통해 내부 네트워크에 ssh로 침입해서 2차 해킹을 시도하거나 또는 웹 서버를 경유지로 하여 스팸 메일을 보내거나 외부 서버에 연결하는 등의 피해를 최소화할 수 있습니다. 39 | 40 | ### Reverse Proxy 로 사용 41 | 42 | 이제 웹 서버는 HTML/CSS/JavaScript 같은 정적 컨텐츠를 전송하거나 또는 내부망에 있는 WAS 에 연결하여 웹 애플리케이션의 실행 결과를 받아서 사용자에게 전달하는 Reverse Proxy 로 역할을 제한하는 것이 필요합니다. 43 | 44 | ![Reverse Proxy]( 45 | https://www.lesstif.com/download/attachments/20776817/image2014-7-19%2022%3A39%3A39.png?version=1&modificationDate=1405777029000&api=v2 "Reverse Proxy") 46 | 47 | Reverse Proxy 로 사용할 경우 WAS가 사용하는 포트만 열어주면 되며 다른 포트는 모두 사용을 막는 것이 좋습니다. 48 | 49 | | WAS | Default Port | 개발 언어 | 50 | | --- | --- | --- | 51 | | tomcat | 8080(HTTP), 8009(AJP) | Java| 52 | | jetty | 8080(HTTP) | Java| 53 | | PHP-FPM | 9000 | PHP | 54 | | unicorn | 8080 | ruby | 55 | 56 | 만약 사용하는 WAS 의 포트가 SELinux에 등록되어 있지 않다면 차단되어 서비스가 불가능하므로 포트를 추가해야 하며 다음 명령어는 웹 서버가 9876 포트에 연결할 수 있도록 허용합니다. 57 | 58 | ```bas 59 | semanage port -a -p tcp -t http_port_t 9876 60 | ``` 61 | 62 | 만약 *ValueError: Port tcp/9876 already defineㅇ* 와 같은 에러가 발생한다면 SELinux 포트 규칙이 이미 정의되어 발생한 것이며 정책을 추가하는 *-a* 옵션대신 변경하는 *-m* 옵션을 사용하면 됩니다. 63 | 64 | 물리적인 구성에 대해서 알아보았으니 그러면 웹 서버를 견고하게 하기 위한 설정 방법을 알아 봅시다. 65 | 66 | ## 민감한 데이타 웹 서버에 올리지 않기 67 | 68 | 각종 설정 파일이나 민감한 파일(sql, shell script) 등은 웹 서버의 Document Root 에 올리지 않아야 합니다. 69 | 70 | 특히 디렉터리 목록이 활성화되어 있고 index 파일이 없을 경우 폴더의 전체 구조가 노출되므로 주의해야 합니다. 71 | 72 | ## 보안 강화 HTTP 헤더 사용 73 | 74 | HTTP 헤더에는 [보안을 강화하기 위한 여러 헤더](https://www.owasp.org/index.php/List_of_useful_HTTP_headers)가 있습니다. 75 | 76 | * **X-Frame-Options** : [클릭 하이재킹](http://blog.ahnlab.com/ahnlab/tag/1058)을 방지하기 위한 헤더로 *deny*로 설정하면 iframe 에서 렌더링을 하지 않습니다. *sameorigin* 은 origin이 일치하지 않을 경우 렌더링을 하지 않습니다. 77 | * **X-Content-Type-Options**: *"nosniff"* 만 설정할 수 있으며 [잘못된 MIME 형식이 포함된 응답을 거부](https://msdn.microsoft.com/ko-kr/library/gg622941\(v=vs.85\).aspx)합니다. 78 | * **X-XSS-Protection**: IE와 Chrome 브라우저가 지원하며 [특정 유형의 XSS(cross site script)공격](https://msdn.microsoft.com/ko-kr/library/dd565647\(v=vs.85\).aspx)을 차단해 줍니다. 79 | 80 | apache httpd 는 Header 지시자로 보안 관련 헤더를 설정하면 됩니다. 81 | 82 | ``` 83 | Header always set X-Frame-Options DENY 84 | Header always set X-Content-Type-Options nosniff 85 | Header set X-XSS-Protection "1; mode=block" 86 | ``` 87 | 88 | nginx 는 add_header 지시자로 설정합니다. 89 | 90 | ``` 91 | add_header X-Frame-Options DENY; 92 | add_header X-Content-Type-Options nosniff; 93 | add_header X-XSS-Protection "1; mode=block"; 94 | ``` 95 | 96 | >**Warning** 97 | iframe 을 사용하여 서비스하는 페이지가 있을 경우 "*X-Frame-Option: Deny*" 헤더가 설정되면 제대로 동작하지 않습니다. 98 | 99 | ## 웹서버 디렉터리 목록 비활성화 100 | 101 | 특정 배포판에 포함된 웹 서버의 기본 설정은 브라우저가 웹 서버에 요청한 리소스가 디렉터리이고 해당 디렉터에 인덱스 파일이 없을 경우 모든 파일과 디렉터리 목록을 보여 주게 됩니다. 102 | 103 | 이렇게 서버의 모든 자원을 보여주는 것은 보안상 취약할 수 있으므로 웹 서버에 디렉터리의 인덱스를 처리할 확장자를 명시적으로 지정하고 없을 경우 목록을 보여주지 않도록 설정하는게 좋습니다. 104 | 105 | apache httpd 의 경우 **DirectoryIndex** 에 인덱스 파일을 지정할 수 있습니다. 106 | 107 | **Directory** 지시자에 아래와 같이 Indexes 설정이 있을 경우 목록 출력이 가능하므로 삭제하는 게 좋습니다. 108 | 109 | ``` 110 | DirectoryIndex index index.php index.html index.html.var 111 | 112 | 113 | Options Indexes 114 | 115 | ``` 116 | 117 | nginx 는 **location** 지시자에 autoindex 가 on 일 경우 요청 리소스가 목록을 출력하며 기본 설정은 off 이므로 명시적으로 설정할 필요가 없습니다. 118 | 119 | ``` 120 | index index.php index.html index.htm; 121 | 122 | location / { 123 | autoindex off; 124 | } 125 | ``` 126 | 127 | ## server 정보 숨기기 128 | 129 | 운영하는 서버의 자세한 정보는 공격자에게 유용한 정보를 제공합니다. 다음과 같이 curl 명령어로 서버가 보내오는 HTTP 응답 헤더를 살펴 봅시다. 130 | 131 | ``` 132 | curl -I myhost.com 133 | ``` 134 | 135 | 136 | ``` 137 | HTTP/1.1 200 OK 138 | Date: Thu, 17 Mar 2016 05:54:33 GMT 139 | Server: Apache/2.2.24 (Unix) mod_ssl/2.2.24 OpenSSL/0.9.8e-fips-rhel5 PHP/5.2.17 mod_jk/1.2.41 140 | X-Powered-By: PHP/5.2.17 141 | Content-Length: 1861 142 | Content-Type: text/html 143 | ``` 144 | 145 | **Server** 헤더를 보면 현재 사용하는 서버 제품(Apache)과 버전(2.2.24), 모듈(mod_ssl, PHP, mod_jk ..)과 버전등 굉장히 상세한 정보를 보여주고 있으며 공격자는 해당 제품과 버전의 취약점을 찾아서 공격할 수 있습니다. 146 | 147 | >**Tip** 148 | [wappalyzer](https://wappalyzer.com/) 를 사용하면 크롬이나 파이어 폭스에서 서버에서 사용하는 기술을 확인할 수 있습니다. 149 | ![wappalyzer](https://cloud.githubusercontent.com/assets/404534/15269913/c64ac174-1a48-11e6-8dfd-0496bb2d3ec0.png "wappalyzer") 150 | 151 | ### Server Token Off 152 | 153 | apache httpd 는 httpd.conf 에 다음과 같이 설정하면 표시 정보를 최소화할 수 있습니다. 154 | 155 | ``` 156 | ServerTokens Prod 157 | ServerSignature Off 158 | ``` 159 | 160 | nginx 는 nginx.conf 의 http 항목에 다음 내용을 추가합니다. 161 | 162 | ``` 163 | http { 164 | server_tokens off; 165 | 166 | ``` 167 | 168 | ### X Powered By 헤더 제거 169 | 170 | *X-Powered-By* 헤더는 서버 사이드에서 사용하는 기술(ASP.NET, PHP, JBoss 등)을 표시하는 비표준 헤더로 공격자에게 유용한 정보를 제공하므로 숨기는게 좋습니다. 171 | 172 | 173 | apache httpd 의 경우 Header 지시자를 사용하면 브라우저로 전송하기 전에 모든 X-Powered-By 헤더를 삭제할 수 있습니다. 174 | 175 | ``` 176 | Header unset X-Powered-By 177 | ``` 178 | 179 | nginx 는 *fastcgi_hide_header* 모듈을 사용하여 숨길 헤더를 지정하면 됩니다. 180 | 181 | ``` 182 | http { 183 | fastcgi_hide_header X-Powered-By; 184 | ``` 185 | 186 | PHP 를 사용한다면 php.ini 의 다음 항목을 off 로 설정하면 됩니다. 187 | 188 | ``` 189 | expose_php = Off 190 | ``` 191 | 192 | ## UTF-8 charset 사용 193 | 194 | 특별한 문제가 없다면 charset은 utf-8로 선언하고 사용하는 것이 서비스의 확장성 측면과 보안성 측면(*"UTF-7 XSS"* 같은 취약점 방지)에 좋습니다. 195 | 196 | 웹 서버에 기본 인코딩을 설정하면 HTTP의 *Content-Type* 헤더에 *charset=UTF-8* 을 자동으로 추가하므로 개별 컨텐츠마다 다음과 같이 *meta* 태그로 인코딩을 지정하지 않아도 되는 장점이 있습니다. 197 | 198 | ```html 199 | 200 | ``` 201 | 202 | 웹 서버에 기본 인코딩을 설정하려면 **apache httpd**는 다음과 같이 *AddDefaultCharset* 지시자를 사용하여 설정하면 됩니다. 203 | 204 | ``` 205 | AddDefaultCharset utf-8 206 | ``` 207 | 208 | **nginx** 는 *charset* 키워드로 기본 인코딩을 설정할 수 있습니다. 209 | 210 | ``` 211 | http { 212 | charset utf-8; 213 | ``` 214 | 215 | > **Danger** 216 | 웹 서버에 인코딩을 설정한 경우 브라우저는 개별 컨텐츠에 *meta* 키워드로 설정한 인코딩은 무시하므로 다른 인코딩(예: EUC-KR)을 사용하는 컨텐츠가 있을 경우 오작동할 수 있습니다. 217 | 자세한 내용은 아래의 블로그를 참고하세요. 218 | 219 | * [Web Browser 가 Web Content 의 character set encoding 을 처리하는 순서 (HTTP Header charset과 meta charset)](https://www.lesstif.com/pages/viewpage.action?pageId=20775179) 220 | 221 | ## 폴더와 파일에 적절한 권한 부여 222 | 223 | 웹 컨텐츠 폴더와 파일은 적절한 권한이 부여해야 하며 폴더는 755(또는 775), 파일은 644(또는 664) 권한이 적절합니다. 224 | 225 | ``` 226 | chmod -R 755 /var/www/mycontents 227 | ``` 228 | 229 | 특히 파일에 실행 권한을 주는 경우 웹 서버를 통해 실행할 경우 문제가 될 수 있으므로 주의깊게 설정해야 합니다. 230 | 231 | > [!WARNING] 232 | > [SELinux](selinux.html)가 켜져 있을 경우 실행 속성을 주어도 *httpd_sys_script_exec_t* 컨텍스트가 설정되어 있지 않으면 웹 서버가 실행할 수 없습니다. 233 | 234 | 웹 서버는 루트로 구동해야 하므로 보안을 위해 구동후 별도의 계정으로 전환하며 배포판마다 전환하는 계정이 다릅니다. 235 | 그러므로 웹 서버가 쓰기 권한과 실행 권한을 가져야 하는 폴더와 파일은 웹 서버 구동후 전환 계정으로 설정하는 게 권한 문제를 방지할 수 있습니다. 236 | 237 | 예를 들어 ubuntu 에서 *PHP-FPM*을 도메인 소켓 방식으로 사용할 경우 nginx 구동 계정(www-data)의 소유이고 쓰기 권한이 있어야 정상적으로 서비스를 할 수 있습니다. 238 | 239 | ```sh 240 | $ ls -l /var/run/php/php7.0-fpm.sock 241 | srw-rw---- 1 www-data www-data 0 May 9 06:18 /var/run/php/php7.0-fpm.sock 242 | ``` 243 | 244 | RHEL/CentOS 의 apache의 경우 */etc/httpd/conf/httpd.conf*에 구동 계정이 정의되어 있습니다. 245 | 246 | ``` 247 | User apache 248 | Group apache 249 | ``` 250 | 251 | nginx 는 */etc/nginx/nginx.conf* 에 *user* 키워드로 구동 계정이 정의되어 있으며 ubuntu는 *www-data*를 RHEL/CentOS는 *nginx* 계정을 사용합니다. 252 | 253 | ``` 254 | user nginx; 255 | ``` 256 | 257 | ## 중요 파일 접근 차단 258 | 259 | 웹 서버의 컨텐츠 디렉터리에는 URL Re-writing 을 처리하는 *.htaccess* 나 워드프레스의 *wp-config.php*, git이나 subversion의 형상 관리 메타 정보(.git, .svn), 설정 파일(*.inc, *.ini, *.cfg, *.conf)등의 중요 정보가 있을 수 있습니다. 260 | 261 | 공격자는 이런 파일을 내려 받아서 서버의 정보를 파악할 수 있으므로 다음 설정으로 중요 파일을 보호할 수 있습니다. 262 | 263 | 264 | **httpd 2.2, 2.4** 265 | 266 | ``` 267 | 268 | Redirect 404 / 269 | 270 | 271 | 272 | Redirect 404 / 273 | 274 | 275 | 276 | Redirect 404 / 277 | 278 | 279 | ## java property 280 | 281 | Redirect 404 / 282 | 283 | 284 | ## laravel .env* 285 | 286 | Redirect 404 / 287 | 288 | ``` 289 | 290 | **nginx** 291 | 292 | ``` 293 | location ~ /\.(ht|git|svn) { 294 | return 404; 295 | } 296 | location ~ /wp-conf* { 297 | return 404; 298 | } 299 | location ~ /.*\.(inc|ini|conf|cfg)$ { 300 | return 404; 301 | } 302 | 303 | ## java property 304 | location ~ /.*\.(xml|properties)$ { 305 | return 404; 306 | } 307 | 308 | ## laravel .env* 309 | location ~ /\.(env)* { 310 | return 404; 311 | } 312 | ``` 313 | 314 | > [!WARNING] 315 | > *Require all denied*(apache) 나 *deny all*(nginx) 를 사용할 경우 HTTP 403 Forbidden 응답이 가게되며 공격자는 이 경우 해당 컨텐츠가 있으므로 적절한 권한을 얻기위해 추가 공격을 실행할 수 있습니다. 그러나 HTTP 404 Not Found 응답을 받으면 컨텐츠가 없다고 생각할 것이므로 더 적절한 설정입니다. 316 | 317 | ## 관리자 서비스 접근 제한 318 | 319 | 웹 애플리케이션에서 설정할수도 있지만 웹 서버에서도 관리자 영역등 특정 패턴의 url 을 차단할 수 있습니다. 320 | 321 | 다음은 아파치 톰캣의 관리자 context 인 /manager 에 127.0.0.1과 192.168.10.0 대역을 제외하고는 접근을 차단하는 예제입니다. 322 | 323 | **httpd 2.2** 324 | 325 | ``` 326 | 327 | Order deny,allow 328 | Deny from all 329 | Allow from 127.0.0.1 192.168.10.0/24 330 | 331 | ``` 332 | 333 | **httpd 2.4** 는 다음과 같이 설정합니다. 334 | 335 | ``` 336 | 337 | Require host 127.0.0.1 192.168.10.0/24 338 | 339 | ``` 340 | 341 | Proxy 를 사용하는 경우 342 | 343 | ``` 344 | SetEnvIF X-Forwarded-For "127.0.0.1" AllowIP 345 | SetEnvIF X-Forwarded-For "^192\.168\.10" AllowIP 346 | 347 | 348 | Require env AllowIP 349 | 350 | ``` 351 | 352 | **nginx** 353 | 354 | ``` 355 | location /manager { 356 | satisfy any; 357 | 358 | allow 192.168.0.0/24; 359 | deny all; 360 | } 361 | ``` 362 | 363 | ## 같이 읽기 364 | * [Ubuntu Server의 보안을 위해서 해야 할 것들] 365 | * [Part 1](https://davidhyk.github.io/blog/things-you-should-do-to-secure-ubuntu-part1) 366 | * [Part 2](https://davidhyk.github.io/blog/things-you-should-do-to-secure-ubuntu-part2) 367 | * [Ubuntu 서버 14.04 초기설정 가이드](http://www.shako.net/blog/ubuntu-server-14-04-initial-setup-guide/) 368 | 369 | ## 참고 자료 370 | * [apache ServerTokens Directive](https://httpd.apache.org/docs/2.4/mod/core.html#servertokens) 371 | * [nginx RESTRICTING ACCESS](https://www.nginx.com/resources/admin-guide/restricting-access/) 372 | * [Nginx Server Configs](https://github.com/h5bp/server-configs-nginx) - nginx 설정 모음 373 | * [Apache Server Configs](https://github.com/h5bp/server-configs-apache) - apache httpd 설정 모음 374 | * [htaccess](https://github.com/phanan/htaccess) - apache .htaccess Snippets 375 | * [Apache2 block / allow IP – simple guide](http://dryja.info/apache2-block-allow-ip-simple-guide/) 376 | --------------------------------------------------------------------------------