ปิดใช้งานการถอดรหัส URL ในพร็อกซี nginx


21

เมื่อฉันเรียกดู URL นี้: http://localhost:8080/foo/%5B-%5Dเซิร์ฟเวอร์ ( nc -l 8080) ได้รับตามที่เป็นอยู่:

GET /foo/%5B-%5D HTTP/1.1

อย่างไรก็ตามเมื่อฉันพร็อกซีแอปพลิเคชันนี้ผ่าน nginx (1.1.19):

location /foo {
        proxy_pass    http://localhost:8080/foo;
}

คำขอเดียวกันที่ถูกส่งผ่านพอร์ต nginx ถูกส่งต่อด้วยเส้นทางที่ถอดรหัส:

GET /foo/[-] HTTP/1.1

วงเล็บเหลี่ยมที่ถอดรหัสในเส้นทาง GET ทำให้เกิดข้อผิดพลาดในเซิร์ฟเวอร์เป้าหมาย ( สถานะ HTTP 400 - อักขระผิดกฎหมายในเส้นทาง ... ) เมื่อพวกเขามาถึงยังไม่ได้หลบหนี

มีวิธีการปิดการใช้งานการถอดรหัส URL หรือเข้ารหัสมันกลับเพื่อให้เซิร์ฟเวอร์เป้าหมายได้รับเส้นทางเดียวกันเมื่อส่งผ่าน nginx หรือไม่ กฎการเขียน URL ที่ฉลาดขึ้นมาบ้าง?


รายงานข้อบกพร่องถึง nginx: trac.nginx.org/nginx/ticket/262
Tomasz Nurkiewicz

คำตอบ:


19

การอ้างถึงValentin V. Bartenev (ผู้ที่ควรได้รับเครดิตเต็มสำหรับคำตอบนี้):

คำพูดจากเอกสาร :

  • หาก proxy_pass ถูกระบุด้วย URIเมื่อผ่านการร้องขอไปยังเซิร์ฟเวอร์ส่วนหนึ่งของURI คำขอที่เป็นมาตรฐานที่ตรงกับตำแหน่งจะถูกแทนที่ด้วย URI ที่ระบุในคำสั่ง

  • หากproxy_passระบุไว้โดยไม่มี URIคำขอ URI จะถูกส่งไปยังเซิร์ฟเวอร์ในรูปแบบเดียวกับที่ลูกค้าส่งมาเมื่อประมวลผลคำขอต้นฉบับ

การกำหนดค่าที่ถูกต้องในกรณีของคุณคือ:

location /foo {
   proxy_pass http://localhost:8080;
}

8
ฉันต้องเปลี่ยนhttp://localhost:8080/เป็นhttp://localhost:8080ในกรณีที่ทุกคนมีสถานการณ์เดียวกันกับที่ฉันทำ
herrtim

4
ทำไม Nginx ถอดรหัส URI ก่อนส่งต่อไปยังเซิร์ฟเวอร์ส่วนหลัง มันจะไม่สมเหตุสมผลกว่านี้หรือเปล่า
ตุ่นปากเป็ด

@platypus จะไม่ถูกแตะต้องจนกว่าคุณจะเริ่มทำการทดแทนอย่างชัดเจน
cnst

2

โปรดทราบว่าการถอดรหัส URL ที่รู้จักกันทั่วไปว่า$uri"การทำให้เป็นมาตรฐาน"ภายในเอกสารของ nginx เกิดขึ้นก่อนที่ IFF แบ็กเอนด์:

  • URI ใด ๆ ที่ระบุไว้ภายในproxy_passตัวเองแม้ว่าจะเป็นเพียงแค่เครื่องหมายสแลชต่อท้ายเอง

  • หรือ URI rewriteมีการเปลี่ยนแปลงระหว่างการประมวลผลเช่นผ่าน


ทั้งสองเงื่อนไขมีการบันทึกไว้อย่างชัดเจนที่http://nginx.org/r/proxy_pass (เหมืองที่เน้น):

  • หากproxy_passมีการระบุคำสั่งด้วย URIดังนั้นเมื่อมีการส่งคำขอไปยังเซิร์ฟเวอร์ส่วนหนึ่งของคำขอมาตรฐานที่ตรงกับตำแหน่งจะถูกแทนที่ด้วย URI ที่ระบุไว้ในคำสั่ง

  • หากproxy_passมีการระบุโดยไม่ต้อง URI URI คำขอจะถูกส่งไปยังเซิร์ฟเวอร์ในส่วนรูปแบบเดียวกับที่ส่งมาจากลูกค้าเมื่อคำขอเดิมมีการประมวลผลหรือเต็มปกติ URI คำขอจะถูกส่งเมื่อการประมวลผลการเปลี่ยนแปลง URI


วิธีแก้ไขคือละเว้น URI ดังเช่นในกรณีของ OPs หรือใช้rewriteกฎที่ฉลาด:

# map `/foo` to `/foo`:
location /foo {
    proxy_pass  http://localhost:8080;  # no URI -- not even just a slash
}

# map `/foo` to `/bar`:
location /foo {
    rewrite  ^  $request_uri;            # get original URI
    rewrite  ^/foo(/.*)  /bar$1  break;  # drop /foo, put /bar
    return 400;   # if the second rewrite won't match
    proxy_pass    http://localhost:8080$uri;
}

คุณสามารถดูได้ในคำตอบ Stack Overflow ที่เกี่ยวข้องรวมถึงกลุ่มควบคุม


เอกสารกำลังสับสนที่นี่ ทั้งสองรูปแบบมี URI มันเป็นองค์ประกอบของเส้นทางที่มีอยู่ในหนึ่งและหายไปในอื่น ๆ
Michael Hampton

@MichaelHampton ฉันไม่เห็นด้วย - โดยทั่วไปเส้นทางนั้นเรียกว่า URI ดังนั้นสิ่งที่ไม่มีเส้นทางไม่มี URI
cnst

เส้นทางสัมพัทธ์เพียงอย่างเดียวอาจเป็น URL ที่ถูกต้องได้เช่นกัน ประเด็นคือส่วนที่เหลือเป็น URI ที่ถูกต้อง (เช่นhttp://localhost:8080) หากคุณไม่เห็นด้วยคุณสามารถนำมันขึ้นกับผู้เขียน RFC 3986
Michael Hampton

@MichaelHampton ไม่สบายใจดูเหมือนว่ารูปแบบและเส้นทางเป็นสิ่งจำเป็นสำหรับ URI, ผู้มีอำนาจ, ข้อโต้แย้ง, ส่วนที่ไม่จำเป็น
Norman Xu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.