Werkzeug: CORS ์ง€์›

์— ๋งŒ๋“  2011๋…„ 11์›” 03์ผ  ยท  16์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: pallets/werkzeug

CORS๋ฅผ ์ง€์›ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค [1][2]

์ •์  ํŒŒ์ผ๋„ ์ง€์›ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

[1] http://www.w3.org/TR/cors/
[2] http://enable-cors.org/

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

๋˜ํ•œ werkzeug์— ๋ชจ๋“  ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ถ”๊ฐ€ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์„ ๋‹ซ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

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

Werkzeug๋Š” WSGI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ์‘๋‹ต์— ํ—ค๋”๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ ์ด๋ฏธ CORS๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. Werkzeug ๋‚ด์—์„œ "CORS ์ง€์›"์„ ์œ„ํ•ด ๋ฌด์—‡์„ ํ•  ์ˆ˜ ์žˆ๊ฑฐ๋‚˜ ํ•ด์•ผ ํ•˜๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ž์„ธํžˆ ์„ค๋ช…ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

์ €๋Š” OP๊ฐ€ ์•„๋‹ˆ์ง€๋งŒ CORS๋Š” ๊ฑฐ์˜ ์ƒ์šฉ๊ตฌ ํ—ค๋” ์ฃผ์ž…์ด๋ฏ€๋กœ ์„ค์ •/๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ด ์Šค ๋‹ˆํŽซ ์˜ ๋ผ์ธ์„ ๋”ฐ๋ผ ๋ญ”๊ฐ€๊ฐ€ ์žˆ์ง€๋งŒ ์—ฌ๊ธฐ์—๋Š” ํŠนํžˆ OPTIONS๊ฐ€ ์—†์œผ๋ฏ€๋กœ preflight๊ฐ€ ๋ฌธ์ œ๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค ...

    if 'cors' in config and config['cors'] is True:
        response.headers.add('Access-Control-Allow-Origin', '*')
        response.headers.add('Access-Control-Allow-Methods',
                             'GET,PUT,POST,DELETE,PATCH')
        response.headers.add('Access-Control-Allow-Headers',
                             'Content-Type, Authorization')

...ํŒŒ์ด์ฌ ๋งŒํŠธ๋ผ "๋ฐฐํ„ฐ๋ฆฌ ํฌํ•จ"์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค... ;)

๊ทธ๋Ÿฌ๋‚˜ CORS๋Š” ํŠนํžˆ ์ฟ ํ‚ค์™€ ๋ฌผ๊ฑด์„ ์‚ฌ์šฉํ•  ๋•Œ * ํ—ˆ์šฉํ•˜๋Š” ๊ฒƒ ์ด์ƒ์ž…๋‹ˆ๋‹ค. ๊ฐœ์ธ์ ์œผ๋กœ ์ €๋Š” 20 LoC WSGI ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CORS ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๋น„ํ–‰ ์ „ ์š”์ฒญ์— ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ์–ด๋ ต์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@posativ - ์š”์ ์„ ๊ณต์œ ํ•˜๊ณ 

๋งค์šฐ ์ œํ•œ๋œ ํ˜•ํƒœ๋กœ:

from werkzeug.datastructures import Headers

class CORSMiddleware(object):
    """Add Cross-origin resource sharing headers to every request."""

    def __init__(self, app, origin):
        self.app = app
        self.origin = origin

    def __call__(self, environ, start_response):

        def add_cors_headers(status, headers, exc_info=None):
            headers = Headers(headers)
            headers.add("Access-Control-Allow-Origin", self.origin)
            headers.add("Access-Control-Allow-Headers", "Origin, ...")
            headers.add("Access-Control-Allow-Credentials", "true")
            headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, etc")
            headers.add("Access-Control-Expose-Headers", "...")
            return start_response(status, headers.to_list(), exc_info)

        if environ.get("REQUEST_METHOD") == "OPTIONS":
            add_cors_headers("200 Ok", [("Content-Type", "text/plain")])
            return [b'200 Ok']

        return self.app(environ, add_cors_headers)

# usage
app = CORSMiddleware(make_app(...), "http://example.org/")

์‚ฌ์šฉ์ž ์ •์˜ ํ—ค๋”, ์ž๊ฒฉ ์ฆ๋ช…, ์™€์ผ๋“œ ์นด๋“œ๋ฅผ ํฌํ•จํ•˜๋„๋ก ์ด ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@posativ - ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์ฃผ ์ดˆ์— ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ ์ž‘์—…์„ ์žฌ๊ฐœํ•  ๋•Œ ์‹œ๋„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. :)

์ด๊ฒƒ์„ ๋ถ€ํ’€๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ƒ๋Œ€์  ์งˆ๋ฌธ http://stackoverflow.com/questions/19382431/set-header-on-werkzeug-exception

๊ด€๋ จ ๋ผ์ธ: https://github.com/pallets/werkzeug/blob/master/werkzeug/debug/__init__.py#L297

start_response('500 INTERNAL SERVER ERROR', ๋Š” Access-Control-Allow-Origin: * ์™€ ๊ฐ™์€ CORS ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํฅ๋ฏธ๋กญ๊ฒŒ๋„ ์ด๋ฏธ X-XSS-Protection: 0 ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Flask-CORS๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ  Chrome์—์„œ ๊นŒ๋‹ค๋กœ์šด ์›๊ฒฉ Ajax API ํ˜ธ์ถœ์„ ๋””๋ฒ„๊น…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Flask์— ์˜ˆ์™ธ๊ฐ€ ์žˆ์„ ๋•Œ CORS๋กœ ์ธํ•ด ๋””๋ฒ„๊ทธ ๋‚ด์šฉ์„ ๋ณผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋””๋ฒ„๊ทธ ๋ชจ๋ธ์— ์ด ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

๋””๋ฒ„๊ฑฐ์—์„œ CORS ํ—ค๋”๋ฅผ ์„ค์ •ํ•˜๋ ค๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ๊ทธ๋ฆฌ๊ณ  ์™œ ๋ž˜ํ•‘ ๋ฏธ๋“ค์›จ์–ด๋กœ ๊ทธ๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๊นŒ?

๋˜ํ•œ werkzeug์— ๋ชจ๋“  ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ถ”๊ฐ€ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์„ ๋‹ซ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

@unititaker

๋””๋ฒ„๊ฑฐ์—์„œ CORS ํ—ค๋”๋ฅผ ์„ค์ •ํ•˜๋ ค๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๊ต์ฐจ ๋„๋ฉ”์ธ AJAX ์š”์ฒญ์— ์˜ํ•ด ํ˜ธ์ถœ๋˜๋Š” ๊นŒ๋‹ค๋กœ์šด ์‹œ๋‚˜๋ฆฌ์˜ค์ด๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ จ์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ด๋ฒคํŠธ ํŠธ๋ฆฌ๊ฑฐ ํ›„์—๋งŒ Chrome DevTool์—์„œ ๋””๋ฒ„๊ทธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. CORS ํ—ค๋”๊ฐ€ ์—†์œผ๋ฉด Chrome DevTool์€ ์ฝ˜ํ…์ธ ๋ฅผ ์ „ํ˜€ ํ‘œ์‹œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ณผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. 500 ์„œ๋ฒ„ ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๋นˆ ํŽ˜์ด์ง€๋งŒ ์‘์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์™œ ๋ž˜ํ•‘ ๋ฏธ๋“ค์›จ์–ด๋กœ ๊ทธ๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๊นŒ?

๋ž˜ํ•‘๋œ ๋ฏธ๋“ค์›จ์–ด๋Š” NORMAL (์ ์ ˆํ•˜๊ฒŒ ๋ฐ˜ํ™˜๋œ Flask ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ) ํŽ˜์ด์ง€์—์„œ๋งŒ ์ž‘๋™ํ•˜์ง€๋งŒ Werkzeug ์˜ˆ์™ธ ๋””๋ฒ„๊ฑฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ „ํ˜€ ์ž‘๋™ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

https://github.com/corydolphin/flask-cors/issues/67์—์„œ ์–ธ๊ธ‰ํ•˜๋Š” "๋ž˜ํ•‘ ๋ฏธ๋“ค์›จ์–ด"๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ฎ์€ ์ˆ˜์ค€์˜ Werkzeug ํ•ญ๋ชฉ์ด ์กฐ์ •๋˜์ง€ ์•Š๋Š” ํ•œ ์ด์— ๋Œ€ํ•ด ๋งŽ์€ ๊ฒƒ์„ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

@untitaker ๊ฐ€ ๋‚ด๊ฐ€ ๋งํฌํ•œ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ง€ ์•Š์•˜๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๊นŒ? ๋‹ค์Œ์€ ์Šคํฌ๋ฆฐ์ƒท์ž…๋‹ˆ๋‹ค.

qq20160420-3

@lambdaq ์ง€๊ธˆ ๋‹น์‹ ์€ ์‹ฌ์ˆ ๊ถ‚๊ฒŒ ๊ตด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค

๋””๋ฒ„๊ทธ ๋ž˜ํ•‘๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ž˜ํ•‘ํ•˜๋Š” ๋ชจ๋“  ๋ฏธ๋“ค์›จ์–ด๋Š” start_response ๋™์ž‘์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๋””๋ฒ„๊ฑฐ์˜ ๊ฒฝ์šฐ์—๋„). ๋”ฐ๋ผ์„œ ๋ถ™์—ฌ๋„ฃ๊ณ  ๋งํฌํ•œ ์ฝ”๋“œ๋Š” ์™„์ „ํžˆ ๊ด€๋ จ์ด ์—†๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค.
cors ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๊ฐ์‹ธ๋Š” ๊ฒƒ์€ ๊ทธ๊ฒƒ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—

๋‹ค๋ฅธ ๊ด€์ /์ดํ•ด๋กœ ์ธํ•ด ์ •ํ™•ํ•œ ๋ฌธ์ œ๊ฐ€ ๋ˆ„๋ฝ๋˜์—ˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊นจ์ง„/๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋Š” ์ •ํ™•ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์‹ญ์‹œ์˜ค.

app.run์„ ํ˜ธ์ถœํ•  ๋•Œ ๋””๋ฒ„๊ฑฐ ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์ ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒํ™ฉ์ด ์•ฝ๊ฐ„ ๊นŒ๋‹ค๋กœ์›Œ์ง‘๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ CORS์™€ ๋””๋ฒ„๊ฑฐ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋ชจ๋‘ ์ˆ˜๋™์œผ๋กœ ์ ์šฉํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. IMO๋Š” ์ด๋Ÿฌํ•œ ์—ฃ์ง€ ์ผ€์ด์Šค์— ์ ํ•ฉํ•˜์ง€๋งŒ ๋ณด์•ˆ์„ ์†์ƒ์‹œํ‚ค์ง€ ์•Š๋Š” ๋ฐฉ์‹์œผ๋กœ ๋””๋ฒ„๊ฑฐ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌ์ฒด์ ์ธ ์ œ์•ˆ์ด ์žˆ์œผ๋ฉด ์ œ์•ˆํ•˜์‹ญ์‹œ์˜ค. ํ•˜์ง€๋งŒ ์‰ฌ์šด ๋ณ€ํ™”๋Š” ๋ชจ๋ฆ…๋‹ˆ๋‹ค.

๋˜ํ•œ Werkzeug์˜ ์ฝ”๋“œ๋ฅผ ์ž˜ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๊ทธ๊ฒƒ์„ ์œ ์ง€ํ•˜๋Š” ์ด์œ  ๊ฐ™์•„์š”?

์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ์ œ ์–ธ์–ด์— ๋Œ€ํ•ด ์‚ฌ๊ณผ๋“œ๋ฆฝ๋‹ˆ๋‹ค. Werkzeug๋Š” ๋ฉ‹์ง„ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.

์ด start_response() ํ–‰์„ ์›์ˆญ์ด ํŽธ์ง‘ํ•˜๊ณ  ๋‚ด ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์— CORS ํ—ค๋” ๋ฌด์ฐจ๋ณ„ ๋Œ€์ž…์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ„ธ์ด ๋ฏธ๋“ค์›จ์–ด๋ณด๋‹ค ๋” ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ 500 ์‘๋‹ต์ด ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒฝ์šฐ ๋””๋ฒ„๊ฑฐ๋กœ ๋ฆฌ๋””๋ ‰์…˜ํ•˜๋Š” ๋Œ€์‹  ์ถ”๊ฐ€ JS ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@untitaker JS ์ฝ”๋“œ๋Š” ์•„๋ฌด ๊ฒƒ๋„ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. 500 ์˜ค๋ฅ˜๊ฐ€ ์žˆ์œผ๋ฉด http ์ƒํƒœ ์ฝ”๋“œ๊ฐ€ 500์ด๊ณ  ๋””๋ฒ„๊ฑฐ ํŽ˜์ด์ง€์˜ ์—ญ์ถ”์ ๋งŒ

๋˜ํ•œ js ์ฝ”๋“œ๋Š” ๋‹ค๋ฅธ ํŒ€์—์„œ ๊ฐœ๋ฐœํ•œ ์ˆ˜๋งŽ์€ ํ•ญ๋ชฉ์œผ๋กœ ์ปดํŒŒ์ผ๋œ reactjs์ด๋ฉฐ npm run dev ๊นŒ์ง€ 300์ดˆ ์ด์ƒ ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค. ์•ฝ๊ฐ„์˜ ๋””๋ฒ„๊ทธ ๋ผ์ธ์„ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€ํ•˜๊ธฐ๊ฐ€ ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” #1699๊ฐ€ ์ด๊ฒƒ์„ ํ•ด๊ฒฐํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. Werkzeug๋Š” #1699์— ์žˆ๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ๋งŽ์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ (์บ์‹œ ์ œ์–ด์™€ ๊ฐ™์€) ์ ‘๊ทผ์ž ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์—ฌ๊ธฐ์— ๋นŒ๋“œ๋œ ๋„๊ตฌ(์˜ˆ: Flask-CORS ๋˜๋Š” Quart-CORS)๊ฐ€ CORS ๋…ผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋„๋ก ํ—ˆ์šฉํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

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