วิธีการหลีกเลี่ยง Access-Control-Allow-Origin?


198

ฉันกำลังโทร ajax ไปยังเซิร์ฟเวอร์ของฉันเองบนแพลตฟอร์มที่พวกเขาตั้งป้องกันการโทร ajax เหล่านี้ (แต่ฉันต้องการเรียกข้อมูลจากเซิร์ฟเวอร์ของฉันเพื่อแสดงข้อมูลที่ดึงมาจากฐานข้อมูลของเซิร์ฟเวอร์ของฉัน) สคริปต์ ajax ของฉันทำงานได้สามารถส่งข้อมูลไปยังสคริปต์ php ของเซิร์ฟเวอร์ของฉันเพื่อให้สามารถประมวลผลได้ อย่างไรก็ตามไม่สามารถรับข้อมูลที่ประมวลผลกลับมาได้เนื่องจากถูกบล็อกโดย"Access-Control-Allow-Origin"

ฉันไม่สามารถเข้าถึงซอร์ส / คอร์ของแพลตฟอร์มนั้นได้ ดังนั้นฉันจึงไม่สามารถลบสคริปต์ที่ไม่อนุญาตให้ทำเช่นนั้น (P / SI ใช้คอนโซลของ Google Chrome และพบข้อผิดพลาดนี้)

รหัส Ajax ที่แสดงด้านล่าง:

 $.ajax({
     type: "GET",
     url: "http://example.com/retrieve.php",
     data: "id=" + id + "&url=" + url,
     dataType: 'json',   
     cache: false,
     success: function(data)
      {
        var friend = data[1];              
        var blog = data[2];           
        $('#user').html("<b>Friends: </b>"+friend+"<b><br> Blogs: </b>"+blog);

      } 
  });

หรือมีJSONรหัสเทียบเท่ากับสคริปต์ ajax ด้านบน? ฉันคิดว่าJSONได้รับอนุญาต

ฉันหวังว่าจะมีคนช่วยฉันออก


คำตอบสำหรับคำถามของคุณทั้งหมดได้อธิบายวิธีการเขียนรหัสเซิร์ฟเวอร์ของคุณใหม่เพื่อให้ Ajax ทำงานได้ ไม่มีสิ่งใดเกี่ยวกับการเลี่ยงผ่านได้ตามที่คุณถามในคำถามของคุณโดยเฉพาะ คุณคิดว่ายังเลี่ยงผ่านส่วนหัวนี้จริงหรือไม่? ฉันสงสัยจริงๆว่าจะมีอย่างใดอย่างหนึ่ง
Moradnejad

ไม่มีทางที่จะข้ามมันได้ แต่คุณสามารถวางไฟล์ลงบนแบ็กเอนด์ที่ดำเนินการตามคำขอได้ ดังนั้นคุณจึงเรียก ajax ต่อไฟล์บนเซิร์ฟเวอร์ของคุณเองไฟล์นั้นโหลดข้อมูลจาก restore.php และส่งกลับไปที่ javascript ของคุณ ในกรณีนี้ไม่มีกฎ CORS ที่บล็อกคุณ
Jona Paulus

คำตอบ:


367

วางสิ่งนี้ไว้ด้านบนของ restore.php:

header('Access-Control-Allow-Origin: *');  

โปรดทราบว่านี่เป็นการปิดการใช้งานการป้องกัน CORS อย่างมีประสิทธิภาพและทำให้ผู้ใช้ของคุณถูกโจมตี หากคุณไม่แน่ใจอย่างสมบูรณ์ว่าคุณต้องให้กำเนิดทั้งหมดคุณควรล็อคสิ่งนี้ลงไปในแหล่งกำเนิดที่เฉพาะเจาะจงมากขึ้น:

header('Access-Control-Allow-Origin: https://www.example.com')

โปรดอ้างอิงคำตอบสแต็คต่อไปนี้เพื่อความเข้าใจที่ดีขึ้น Access-Control-Allow-Origin

https://stackoverflow.com/a/10636765/413670


54
ค่อนข้างไม่ปลอดภัย ตรวจสอบคำตอบของฉันที่ด้านล่าง
Rob

3
tnx แต่คุณไม่ควรอนุญาตให้เข้าถึงต้นกำเนิดทั้งหมดตามที่ @RobQuist กล่าวถึงในความคิดเห็นของเขาและในคำตอบของเขาก็ให้แนวทางที่ดีกว่า
Rafay

2
ดังนั้นฉันจึงพบหน้านี้เพราะฉันต้องการ 'บายพาส' การควบคุมการเข้าถึงบนเซิร์ฟเวอร์ โซลูชันที่นี่ไม่ได้ข้ามอะไรเลย แต่เพียงแค่กำหนดค่าการควบคุมการเข้าถึงบนเซิร์ฟเวอร์ของเขาเองอย่างถูกต้อง ในกรณีที่มีใครต้องการหลีกเลี่ยงสิ่งเหล่านี้พวกเขาสามารถใช้ file_get_contents ของ PHP ($ remote_url); เห็นได้ชัดว่ามีหลายวิธีในการทำเช่นนี้ แต่นี่เป็นวิธีที่ฉันทำ
Shawn Whinnery

1
@ShawnWhinnery ที่เป็นพื้นฐานของ "proxying" ทางออกที่ดีถ้าคุณต้องการโหลดข้อมูลแบบไดนามิกจากเว็บไซต์อื่นที่คุณไม่สามารถควบคุมได้
Rob

1
ต้องการเรียกใช้สคริปต์ PHP จาก dotnet core - ย้าย php script ไปยัง URL อื่นของฉัน แต่ได้รับข้อผิดพลาดการเขียนสคริปต์ข้ามไซต์ เพิ่มรหัสที่คุณแสดงไว้ด้านบนของ PHP และทำงานได้อย่างสมบูรณ์ ขอบคุณ!
raddevus

291

โอเค แต่คุณทุกคนรู้ว่า * เป็นสัญลักษณ์แทนและอนุญาตให้มีการเขียนสคริปต์ข้ามไซต์จากทุกโดเมนหรือไม่

คุณต้องการส่งหลายAccess-Control-Allow-Originส่วนหัวสำหรับทุกเว็บไซต์ที่ได้รับอนุญาต - แต่น่าเสียดายที่มันไม่ได้รับการสนับสนุนอย่างเป็นทางการในการส่งหลายAccess-Control-Allow-Originส่วนหัวหรือใส่หลายต้นกำเนิด

คุณสามารถแก้ไขได้โดยตรวจสอบที่มาและส่งกลับไปที่ส่วนหัวหากได้รับอนุญาต:

$origin = $_SERVER['HTTP_ORIGIN'];
$allowed_domains = [
    'http://mysite1.com',
    'https://www.mysite2.com',
    'http://www.mysite2.com',
];

if (in_array($origin, $allowed_domains)) {
    header('Access-Control-Allow-Origin: ' . $origin);
}

ปลอดภัยกว่ามาก คุณอาจต้องการแก้ไขการจับคู่และเปลี่ยนเป็นฟังก์ชันด้วยตนเองด้วย regex บางอย่างหรือสิ่งนั้น อย่างน้อยนี่จะส่ง 1 ส่วนหัวกลับเท่านั้นและคุณจะมั่นใจได้ว่าเป็นส่วนหัวที่มีการร้องขอมา โปรดทราบว่าส่วนหัว HTTP ทั้งหมดสามารถปลอมแปลงได้ แต่ส่วนหัวนี้มีไว้เพื่อการป้องกันของลูกค้า อย่าปกป้องข้อมูลของคุณเองด้วยค่าเหล่านั้น หากคุณต้องการทราบข้อมูลเพิ่มเติมอ่านได้ที่ CORS และ CSRF

ทำไมจึงปลอดภัยกว่า

อนุญาตให้เข้าถึงได้จากสถานที่อื่น ๆ จากนั้นเว็บไซต์ที่เชื่อถือได้ของคุณจะอนุญาตให้มีการ Highjacking เซสชัน ฉันจะไปกับตัวอย่างเล็ก ๆ น้อย ๆ - ภาพ Facebook อนุญาตให้มีต้นกำเนิดตัวแทน - ซึ่งหมายความว่าคุณสามารถสร้างเว็บไซต์ของคุณเองได้ที่ไหนสักแห่งและทำให้มันเป็นสาย AJAX (หรือเปิด iframes) ไปที่ Facebook ซึ่งหมายความว่าคุณสามารถคว้าข้อมูลเข้าสู่ระบบของ facebook ของผู้เข้าชมเว็บไซต์ของคุณ ยิ่งแย่ไปกว่านั้นคุณสามารถPOSTส่งคำขอสคริปต์และโพสต์ข้อมูลบน facebook ของใครบางคนขณะที่พวกเขากำลังเรียกดูเว็บไซต์ของคุณ

ใช้ความระมัดระวังอย่างยิ่งเมื่อใช้ACAOส่วนหัว!


12
ฉันคิดว่าคุณต้องใส่ http: // ไว้ข้างหน้าของแต่ละรายการในรายการ อย่างน้อยฉันก็ทำเพื่อเว็บไซต์เดียวที่ฉันทำงานอยู่
blak3r

2
น่าเศร้าที่ดูเหมือนว่าจะไม่ทำงาน ฉันเชื่อว่าสามารถให้ข้อยกเว้นเพียงข้อเดียวต่อการเรียกไปยังส่วนหัว ()
lewsid

5
@ Shanimal & lewsid -> ฉันเดาว่าเครื่องหมายจุลภาคคั่นไม่ทำงานแน่นอน การอ้างอิง: w3.org/TR/cors
Rob

3
สำหรับการจัดการกับรายชื่อโดเมนนี่เป็นคำตอบที่เกี่ยวข้อง: stackoverflow.com/a/1850482/766177
Valentin Despa

13
นี่คือการเพิ่ม 4 ส่วนหัวอย่างไม่มีจุดหมายเพราะแต่ละสายเพื่อheader()แทนที่ส่วนหัวก่อนหน้าของประเภทเดียวกัน ดังนั้นสิ่งที่คุณทำจริงๆคือตั้งค่าส่วนหัวสุดท้าย รายการคู่มือระบุว่าคุณสามารถตั้งค่าพารามิเตอร์ที่สองของfalseการป้องกันไม่ให้ส่วนหัวก่อนหน้านี้ถูกเขียนทับ
BadHorsie

31

คำเตือน Chrome (และเบราว์เซอร์อื่น ๆ ) จะบ่นว่ามีส่วนหัว ACAO หลายรายการหากคุณทำตามคำตอบอื่น ๆ

ข้อผิดพลาดจะเป็นอย่างไร XMLHttpRequest cannot load ____. The 'Access-Control-Allow-Origin' header contains multiple values '____, ____, ____', but only one is allowed. Origin '____' is therefore not allowed access.

ลองสิ่งนี้:

$http_origin = $_SERVER['HTTP_ORIGIN'];

$allowed_domains = array(
  'http://domain1.com',
  'http://domain2.com',
);

if (in_array($http_origin, $allowed_domains))
{  
    header("Access-Control-Allow-Origin: $http_origin");
}

6
นี่เป็นทางออกที่ดียิ่งกว่าที่ฉันโพสต์ไว้
Rob

7

ฉันได้แก้ไขปัญหานี้แล้วเมื่อเรียกตัวควบคุม MVC3 ฉันเพิ่ม:

Response.AddHeader("Access-Control-Allow-Origin", "*"); 

ก่อนที่ฉัน

return Json(model, JsonRequestBehavior.AllowGet);

และฉัน$.ajaxก็บ่นว่ามันไม่ยอมรับส่วนหัวของประเภทเนื้อหาในการโทร ajax ของฉันดังนั้นฉันจึงแสดงความคิดเห็นเพราะฉันรู้ว่า JSON มันถูกส่งผ่านไปยังการกระทำ

หวังว่าจะช่วย


2

เป็นความคิดที่ดีที่จะใช้*ซึ่งทำให้คุณเปิดกว้างในการเขียนสคริปต์ข้ามไซต์ โดยทั่วไปคุณต้องการโดเมนของคุณเองตลอดเวลากำหนดขอบเขตการตั้งค่า SSL ปัจจุบันของคุณและเลือกโดเมนเพิ่มเติม คุณต้องการให้ส่งทั้งหมดเป็นส่วนหัวเดียว ต่อไปนี้จะอนุญาตโดเมนของคุณเองในขอบเขต SSL เดียวกับหน้าปัจจุบันและอาจรวมโดเมนเพิ่มเติมจำนวนเท่าใดก็ได้ มันจะส่งทั้งหมดเป็นส่วนหัวเดียวและเขียนทับส่วนก่อนหน้าหากมีสิ่งอื่นส่งไปแล้วเพื่อหลีกเลี่ยงโอกาสที่เบราว์เซอร์จะบ่นเกี่ยวกับส่วนหัวควบคุมการเข้าถึงหลายรายการที่ถูกส่ง

class CorsAccessControl
{
    private $allowed = array();

    /**
     * Always adds your own domain with the current ssl settings.
     */
    public function __construct()
    {
        // Add your own domain, with respect to the current SSL settings.
        $this->allowed[] = 'http'
            . ( ( array_key_exists( 'HTTPS', $_SERVER )
                && $_SERVER['HTTPS'] 
                && strtolower( $_SERVER['HTTPS'] ) !== 'off' ) 
                    ? 's' 
                    : null )
            . '://' . $_SERVER['HTTP_HOST'];
    }

    /**
     * Optionally add additional domains. Each is only added one time.
     */
    public function add($domain)
    {
        if ( !in_array( $domain, $this->allowed )
        {
            $this->allowed[] = $domain;
        }
    /**
     * Send 'em all as one header so no browsers grumble about it.
     */
    public function send()
    {
        $domains = implode( ', ', $this->allowed );
        header( 'Access-Control-Allow-Origin: ' . $domains, true ); // We want to send them all as one shot, so replace should be true here.
    }
}

การใช้งาน:

$cors = new CorsAccessControl();

// If you are only authorizing your own domain:
$cors->send();

// If you are authorizing multiple domains:
foreach ($domains as $domain)
{
    $cors->add($domain);
}
$cors->send();

คุณได้รับความคิด


1

คุณลองเพิ่มส่วนหัว Access-Control-Allow-Origin ลงในการตอบสนองที่ส่งมาจากเซิร์ฟเวอร์ของคุณหรือไม่? ชอบAccess-Control-Allow-Origin: *?


1
เป็นส่วนหัว HTTP ที่เซิร์ฟเวอร์ของคุณส่งไปเพื่อแจ้งให้เบราว์เซอร์ทราบว่าไม่เป็นไรที่จะเปิดเผยผลลัพธ์ของสคริปต์การโทรถึงแม้ว่าโดเมนต้นทางของสคริปต์จะไม่ตรงกับโดเมนของเซิร์ฟเวอร์ก็ตาม อ่านข้อมูลเกี่ยวกับการแบ่งปันทรัพยากรข้ามแหล่งกำเนิด !
Daniel Brockman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.