Gunicorn: ๊ธฐ๋ณธ SSL ์•”ํ˜ธ ์„ค์ •์€ ๊ฐ€์žฅ ์•ˆ์ „ํ•œ ์•”ํ˜ธ๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2019๋…„ 01์›” 23์ผ  ยท  3์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: benoitc/gunicorn

--ciphers ์˜ต์…˜์˜ ๊ธฐ๋ณธ๊ฐ’์€ TLSv1 ์ž…๋‹ˆ๋‹ค.

์ด ๊ฐ’์€ TLSv1.2์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ƒˆ๋กญ๊ณ  ๊ฐ•๋ ฅํ•œ ์•”ํ˜ธ๋ฅผ ์ ๊ทน์ ์œผ๋กœ ๋น„ํ™œ์„ฑํ™”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž˜๋ชป๋œ ๊ธฐ๋ณธ๊ฐ’์ž…๋‹ˆ๋‹ค. ํŠนํžˆ, ์ด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๊ตฌ์„ฑ๋œ ์•”ํ˜ธ ์„ธํŠธ์™€ OWASP์—์„œ A+ ๋“ฑ๊ธ‰์„ ๋ฐ›์€ ์•”ํ˜ธ ์‚ฌ์ด์—๋Š” ๊ต์ฐจ์ ์ด ์—†์Šต๋‹ˆ๋‹ค.

>>> import ssl
>>> ctx = ssl.SSLContext()
>>> ctx.set_ciphers('TLSv1')
>>> tlsv1 = {c['name'] for c in ctx.get_ciphers()}
>>> ctx.set_ciphers('DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256')  # From OWASP
>>> aplus = {c['name'] for c in ctx.get_ciphers()}
>>> aplus & tlsv1
set()

์ด๋กœ ์ธํ•ด ๋†’์€ ๋ณด์•ˆ ์„ค์ •์œผ๋กœ ๊ตฌ์„ฑ๋œ ํด๋ผ์ด์–ธํŠธ์— ๋Œ€ํ•ด OpenSSL NO_SHARED_CIPHER ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•œ ๊ฐ€์ง€ ํ•ด๊ฒฐ์ฑ…์€ ๊ธฐ๋ณธ๊ฐ’์„ 'TLSv1:TLSv1.2' ๋ฌธ์ž์—ด๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์„ธํŠธ์— TLSv1.2 ์•”ํ˜ธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๊ธฐ์กด gunicorn ์‚ฌ์šฉ์ž๋ฅผ ์†์ƒ์‹œํ‚ค์ง€ ์•Š๊ณ  ์—„๊ฒฉํ•œ ๋ณด์•ˆ ์„ค์ •์œผ๋กœ ๊ตฌ์„ฑ๋œ ํด๋ผ์ด์–ธํŠธ์— ๋Œ€ํ•œ ํ˜ธํ™˜์„ฑ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ง‘ํ•ฉ์— ์•ฝํ•œ ์•”ํ˜ธ๊ฐ€ ๋‚จ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋ณด๋‹ค ์•ˆ์ „ํ•œ ์˜ต์…˜์€ ์„ค๋ช…๋œ ํ˜ธํ™˜์„ฑ ์ˆ˜์ค€์„ ๊ธฐ๋ฐ˜์œผ๋กœ OWASP ๋ฌธ์ž์—ด ์ค‘ ํ•˜๋‚˜๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด OWASP Cipher String C ๋˜๋Š” C-๋Š” ์•Œ๋ ค์ง„ ์•ฝํ•œ ์•”ํ˜ธ๋ฅผ ๋ฐฐ์ œํ•˜๋ฉด์„œ ๊ด‘๋ฒ”์œ„ํ•œ ํ˜ธํ™˜์„ฑ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘ OWASP ํŽ˜์ด์ง€์— ๋Œ€ํ•œ ์ฐธ์กฐ ๋งํฌ๋ฅผ ํฌํ•จํ•˜์—ฌ ์˜ˆ์ƒ ์‚ฌ์šฉ์— ๋”ฐ๋ผ ๊ธฐ๋ณธ๊ฐ’์„ ๋” ๊ฐ•๋ ฅํ•œ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๊ต์ฒดํ•˜๋„๋ก ๊ถŒ์žฅํ•˜๋„๋ก ์„ค๋ช…์„œ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Feedback Requested FeaturSSL

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋˜ํ•œ Python์—๋Š” ์ž์ฒด ๊ธฐ๋ณธ ์•”ํ˜ธ ๋ชฉ๋ก์ด ์žˆ์Šต๋‹ˆ๋‹ค. OpenSSL์˜ ๊ธฐ๋ณธ๊ฐ’( DEFAULT:!aNULL:!eNULL ์ž„์ด ๋ถ„๋ช…ํ•จ)์œผ๋กœ ์ ˆ๋Œ€ ํด๋ฐฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‚ด๋ถ€/๋ฌธ์„œํ™”๋˜์ง€ ์•Š์•˜์ง€๋งŒ ssl._DEFAULT_CIPHERS ๋กœ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ธฐ๋ณธ๊ฐ’์— ์•ก์„ธ์Šคํ•˜๋Š” ๋ฐฉ๋ฒ•์€ SSLContext์—์„œ set_ciphers()๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Python 3.6์˜ ๊ฒฝ์šฐ _DEFAULT_CIPHERS ๋Š”

'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:!aNULL:!eNULL:!MD5:!3DES'

ํŒŒ์ด์ฌ 3.7์—์„œ๋Š”

'DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK'

์™€ ์ฃผ์„์ด ๋งํ•˜๋Š” "DEFAULT :. OpenSSL์„์˜ ๊ธฐ๋ณธ ์•”ํ˜ธ ๋ชฉ๋ก์„ 1.0.2 ์ดํ›„ ๋ชฉ๋ก ํ•ฉ๋ฆฌ์ ์ธ ์ˆœ์„œ์ž…๋‹ˆ๋‹ค."

์ด๋Ÿฌํ•œ ์„ค์ •์€ OWASP์™€ ๋™์ผํ•œ ์ •์‹ ์œผ๋กœ ๋งค์šฐ ์—„๊ฒฉํ•ด ๋ณด์ž…๋‹ˆ๋‹ค.

์„œ๋ฒ„์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ _RESTRICTED_SERVER_CIPHERS ์žˆ์œผ๋ฉฐ(gunicorn์— ์ ํ•ฉ) ์ผ๋ถ€ Python ๋ฒ„์ „์—์„œ๋Š” _DEFAULT_CIPHERS ๋ณด๋‹ค ๋” ๋นก๋นกํ•˜์ง€๋งŒ ๋‚ด๋ถ€์šฉ์ด๋ฉฐ ์–ด๋””์—๋„ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ตœ์†Œ 3.4 ์ดํ›„๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์˜ต์…˜ 3์€ Python์˜ ๊ธฐ๋ณธ ์•”ํ˜ธ ์ œํ’ˆ๊ตฐ์— ๋Œ€ํ•œ ๊ธฐ๋ณธ๊ฐ’์ž…๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ฒŒ ํ•˜์ง€ ์•Š๋Š” ์œ ์ผํ•œ ์ด์œ ๋Š” ์ด์ „ Python ๋ฒ„์ „๋ณด๋‹ค ๋” ์•ˆ์ „ํ•œ ๋ชฉ๋ก์„ ์˜ค๋Š˜ ๋ฐฐ์†กํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ฑฐ๋‚˜ ๋” ์—„๊ฒฉํ•œ OWASP ์ˆ˜์ค€ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•˜๋ ค๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ์†”์งํžˆ ๋„ˆ๋ฌด ๋ฏธ๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  3 ๋Œ“๊ธ€

์ด ๋ฌธ์ œ๋ฅผ ์‹ ๊ณ ํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ์„ ์ ˆ๋Œ€์ ์œผ๋กœ ์ž˜ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Gunicorn์€ ํ•œ๋™์•ˆ SSL ์ง€์›์— ํฐ ๋ณ€ํ™”๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. #1933 ๋ฐ ํ•ด๋‹น ํ† ๋ก ์— ์—ฐ๊ฒฐ๋œ ๋ฌธ์ œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

๊ทธ๋™์•ˆ Gunicorn์ด Python 3.4 ์ด์ƒ๋งŒ ์ง€์›ํ•˜๋ฏ€๋กœ ๋” ํ•ฉ๋ฆฌ์ ์ธ ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๋‹ค๋ฉด pull request๋ฅผ ์ œ์ถœํ•ด ์ฃผ์‹œ๋ฉด ๊ธฐ๊บผ์ด ๋ณ‘ํ•ฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋˜ํ•œ ์ตœ์†Œ ๋ฒ„์ „์„ ๋†’์ด๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋” ์•ˆ์ „ํ•œ ๊ธฐ๋ณธ์„ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๋” ๋†’์€ ์ตœ์†Œ Python ๋ฒ„์ „์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋‹ค์Œ ๋ฆด๋ฆฌ์Šค ์ „์— ํ•ด๋‹น ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ๋Š” ๋ฐ ๋„์›€์ด ๋˜๋„๋ก ํ•ด๋‹น ์ •๋ณด๋ฅผ ์—ฌ๊ธฐ์— ๊ฒŒ์‹œํ•˜์‹ญ์‹œ์˜ค.

์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์•”ํ˜ธ(๋ฐ SSLContext.set_ciphers() ์— ๋Œ€ํ•œ ์ธ์ˆ˜ ํ•ด์„)๋Š” Python ๋ฒ„์ „์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์ง€๋งŒ Python์ด ๋นŒ๋“œํ•˜๋Š” OpenSSL/LibreSSL ๋ฒ„์ „์˜ ์˜ํ–ฅ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค. Linux์—์„œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐฐํฌํŒ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฒ„์ „์ž…๋‹ˆ๋‹ค.

๊ท€ํ•˜์˜ ์งˆ๋ฌธ์ด "Python 3.4 ์ด์ƒ์—์„œ ํ•ญ์ƒ ์ž‘๋™ํ•˜๋Š” ๋ณด๋‹ค ์•ˆ์ „ํ•œ ๊ธฐ๋ณธ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?"๋ผ๋ฉด ์—„๊ฒฉํ•œ ๋Œ€๋‹ต์€ "์•„๋‹ˆ์˜ค"์ž…๋‹ˆ๋‹ค. ์ด๋Š” Python์„ ์ปดํŒŒ์ผํ•œ ๋ฐฉ๋ฒ•์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ฐ•๋ ฅํ•œ ์•”ํ˜ธ๊ฐ€ ์—†๋Š” ์•„์ฃผ ์˜ค๋ž˜๋œ OpenSSL์— ๋Œ€ํ•ด ์ตœ์‹  Python์„ ์ปดํŒŒ์ผํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ gunicorn์— ๊ฐ•๋ ฅํ•œ ์•”ํ˜ธ๊ฐ€ ํ•„์š”ํ•˜๋ฉด SSL์ด ์ „ํ˜€ ์ž‘๋™ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” ๊ฝค ์•ˆ์ „ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. Python 3.4๋Š” 2014๋…„์— ์ถœ์‹œ๋˜์—ˆ์œผ๋ฉฐ TLSv1.2๋ฅผ ์ถ”๊ฐ€ํ•œ OpenSSL 1.0.1a๋Š” 2012๋…„ 4์›”์— ์ถœ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Python์˜ ๋ฐฐํฌํŒ ๋ฒ„์ „์—๋Š” ๊ฐ•๋ ฅํ•œ ์•”ํ˜ธ๊ฐ€ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ๊ฐ Python ๋ฆด๋ฆฌ์Šค์—๋Š” ํŠน์ • OpenSSL ๋ฒ„์ „์ด ํ•„์š”ํ•˜์ง€๋งŒ(์˜ˆ: Python 3.7์—๋Š” OpenSSL 1.0.2๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค), ์ •ํ™•ํžˆ ์–ด๋–ค Python ๋ฒ„์ „์— ๋ฌด์—‡์ด ํ•„์š”ํ•œ์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ „๋ฐ˜์ ์œผ๋กœ ๋‚˜๋Š” OWASP์˜ ์•ˆ๋‚ด๋ฅผ ๋ฐ›์•„์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋“ค์€ C-๊ฐ€ "์‹ค์ œ ์˜ค๋ž˜๋œ ๋ธŒ๋ผ์šฐ์ € ๋ฐ ๋ ˆ๊ฑฐ์‹œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•œ ๊ฐ€์žฅ ๊ด‘๋ฒ”์œ„ํ•œ ํ˜ธํ™˜์„ฑ"์ธ ๋ ˆ๊ฑฐ์‹œ๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. ์†Œ์Šค๋กœ ์ธ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ถ”๊ฐ€ ๋ณด๋„ˆ์Šค๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ Python์—๋Š” ์ž์ฒด ๊ธฐ๋ณธ ์•”ํ˜ธ ๋ชฉ๋ก์ด ์žˆ์Šต๋‹ˆ๋‹ค. OpenSSL์˜ ๊ธฐ๋ณธ๊ฐ’( DEFAULT:!aNULL:!eNULL ์ž„์ด ๋ถ„๋ช…ํ•จ)์œผ๋กœ ์ ˆ๋Œ€ ํด๋ฐฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‚ด๋ถ€/๋ฌธ์„œํ™”๋˜์ง€ ์•Š์•˜์ง€๋งŒ ssl._DEFAULT_CIPHERS ๋กœ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ธฐ๋ณธ๊ฐ’์— ์•ก์„ธ์Šคํ•˜๋Š” ๋ฐฉ๋ฒ•์€ SSLContext์—์„œ set_ciphers()๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Python 3.6์˜ ๊ฒฝ์šฐ _DEFAULT_CIPHERS ๋Š”

'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:!aNULL:!eNULL:!MD5:!3DES'

ํŒŒ์ด์ฌ 3.7์—์„œ๋Š”

'DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK'

์™€ ์ฃผ์„์ด ๋งํ•˜๋Š” "DEFAULT :. OpenSSL์„์˜ ๊ธฐ๋ณธ ์•”ํ˜ธ ๋ชฉ๋ก์„ 1.0.2 ์ดํ›„ ๋ชฉ๋ก ํ•ฉ๋ฆฌ์ ์ธ ์ˆœ์„œ์ž…๋‹ˆ๋‹ค."

์ด๋Ÿฌํ•œ ์„ค์ •์€ OWASP์™€ ๋™์ผํ•œ ์ •์‹ ์œผ๋กœ ๋งค์šฐ ์—„๊ฒฉํ•ด ๋ณด์ž…๋‹ˆ๋‹ค.

์„œ๋ฒ„์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ _RESTRICTED_SERVER_CIPHERS ์žˆ์œผ๋ฉฐ(gunicorn์— ์ ํ•ฉ) ์ผ๋ถ€ Python ๋ฒ„์ „์—์„œ๋Š” _DEFAULT_CIPHERS ๋ณด๋‹ค ๋” ๋นก๋นกํ•˜์ง€๋งŒ ๋‚ด๋ถ€์šฉ์ด๋ฉฐ ์–ด๋””์—๋„ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ตœ์†Œ 3.4 ์ดํ›„๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์˜ต์…˜ 3์€ Python์˜ ๊ธฐ๋ณธ ์•”ํ˜ธ ์ œํ’ˆ๊ตฐ์— ๋Œ€ํ•œ ๊ธฐ๋ณธ๊ฐ’์ž…๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ฒŒ ํ•˜์ง€ ์•Š๋Š” ์œ ์ผํ•œ ์ด์œ ๋Š” ์ด์ „ Python ๋ฒ„์ „๋ณด๋‹ค ๋” ์•ˆ์ „ํ•œ ๋ชฉ๋ก์„ ์˜ค๋Š˜ ๋ฐฐ์†กํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ฑฐ๋‚˜ ๋” ์—„๊ฒฉํ•œ OWASP ์ˆ˜์ค€ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•˜๋ ค๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ์†”์งํžˆ ๋„ˆ๋ฌด ๋ฏธ๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰