วิธีเปิดใช้งาน CORS ด้วย HTTPOnly Cookie เพื่อรักษาความปลอดภัยโทเค็น

ในบทความนี้ เราจะเห็นวิธีเปิดใช้งาน CORS (การแชร์ทรัพยากรข้ามแหล่งที่มา) ด้วยคุกกี้ HTTPOnly เพื่อรักษาความปลอดภัยโทเค็นการเข้าถึงของเรา

ปัจจุบัน เซิร์ฟเวอร์แบ็กเอนด์และไคลเอ็นต์ฟรอนต์เอนด์ถูกปรับใช้บนโดเมนต่างๆ ดังนั้นเซิร์ฟเวอร์จึงต้องเปิดใช้งาน CORS เพื่อให้ไคลเอ็นต์สื่อสารกับเซิร์ฟเวอร์บนเบราว์เซอร์ได้

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

สารบัญ

เหตุใดคำขอข้ามแหล่งที่มาจึงถูกบล็อก

สมมติว่าแอปพลิเคชันส่วนหน้าของเราปรับใช้ที่ https://app.admintrick.com.com สคริปต์ที่โหลดใน https://app.admintrick.com.com สามารถขอเฉพาะทรัพยากรที่มีต้นกำเนิดเดียวกันเท่านั้น

เมื่อใดก็ตามที่เราพยายามส่งคำขอข้ามต้นทางไปยังโดเมนอื่น https://api.admintrick.com.com หรือพอร์ตอื่น https://app.admintrick.com.com:3000 หรือรูปแบบอื่น http://app.admintrick.com.com คำขอข้ามที่มาจะถูกบล็อกโดยเบราว์เซอร์

แต่ทำไมคำขอเดียวกันที่ถูกบล็อกโดยเบราว์เซอร์ถูกส่งจากเซิร์ฟเวอร์แบ็กเอนด์โดยใช้คำขอ curl หรือส่งโดยใช้เครื่องมือเช่นบุรุษไปรษณีย์โดยไม่มีปัญหา CORS เพื่อความปลอดภัยในการปกป้องผู้ใช้จากการโจมตี เช่น CSRF(Cross-Site Request Forgery)

มาดูตัวอย่างกัน สมมติว่าผู้ใช้รายใดลงชื่อเข้าใช้บัญชี PayPal ของตนเองในเบราว์เซอร์ หากเราสามารถส่งคำขอข้ามต้นทางไปยัง paypal.com จากสคริปต์ที่โหลดบนโดเมนอื่นที่ประสงค์ร้าย.com โดยไม่มีข้อผิดพลาด/การบล็อก CORS เหมือนที่เราส่งคำขอต้นทางเดียวกัน

ผู้โจมตีสามารถส่งหน้า https://malicious.com/transfer-money-to-attacker-account-from-user-paypal-account ที่เป็นอันตรายได้อย่างง่ายดาย โดยแปลงเป็น URL แบบสั้นเพื่อซ่อน URL จริง เมื่อผู้ใช้คลิกลิงก์ที่เป็นอันตราย สคริปต์ที่โหลดในโดเมน malware.com จะส่งคำขอข้ามที่มาไปยัง PayPal เพื่อโอนจำนวนผู้ใช้ไปยังบัญชี PayPal ของผู้โจมตีจะถูกดำเนินการ ผู้ใช้ทุกคนที่ลงชื่อเข้าใช้บัญชี PayPal และคลิกลิงก์ที่เป็นอันตรายนี้จะเสียเงิน ใครๆ ก็ขโมยเงินได้ง่ายๆ โดยที่ผู้ใช้บัญชี PayPal ไม่รู้

ด้วยเหตุผลข้างต้น เบราว์เซอร์จึงบล็อกคำขอข้ามต้นทางทั้งหมด

CORS (การแบ่งปันทรัพยากรแบบข้ามที่มา) คืออะไร

CORS เป็นกลไกการรักษาความปลอดภัยตามส่วนหัวที่ใช้โดยเซิร์ฟเวอร์เพื่อบอกให้เบราว์เซอร์ส่งคำขอข้ามต้นทางจากโดเมนที่เชื่อถือได้
เซิร์ฟเวอร์ที่เปิดใช้งานด้วยส่วนหัว CORS ที่ใช้เพื่อหลีกเลี่ยงคำขอข้ามต้นทางที่ถูกบล็อกโดยเบราว์เซอร์

CORS ทำงานอย่างไร?

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

คำขอ CORS สองประเภทมีอยู่:

  • ของ่ายๆ
  • คำขอเที่ยวบินล่วงหน้า

คำของ่ายๆ:

  • เบราว์เซอร์ส่งคำขอไปยังโดเมนข้ามต้นทางที่มีต้นทาง (https://app.admintrick.com.com)
  • เซิร์ฟเวอร์ส่งการตอบกลับที่เกี่ยวข้องกลับด้วยวิธีการที่อนุญาตและต้นทางที่อนุญาต
  • หลังจากได้รับคำขอ เบราว์เซอร์จะตรวจสอบค่าส่วนหัวต้นทางที่ส่ง (https://app.admintrick.com.com) และรับค่า access-control-allow-origin (https://app.admintrick.com.com) ว่าเหมือนกันหรือ ตัวแทน
  สร้างลิงก์ภายในด้วยปลั๊กอิน WordPress 10 อันดับแรก

. มิฉะนั้น มันจะส่งข้อผิดพลาด CORS

  • คำขอเที่ยวบินล่วงหน้า:
  • ขึ้นอยู่กับพารามิเตอร์คำขอที่กำหนดเองจากคำขอข้ามต้นทางเช่นเมธอด (PUT, DELETE) หรือส่วนหัวที่กำหนดเองหรือประเภทเนื้อหาอื่น ฯลฯ เบราว์เซอร์จะตัดสินใจส่งคำขอตัวเลือก preflight เพื่อตรวจสอบว่าคำขอจริงปลอดภัยหรือไม่ หรือไม่.

หลังจากได้รับการตอบสนอง (รหัสสถานะ: 204 ซึ่งหมายถึงไม่มีเนื้อหา) เบราว์เซอร์จะตรวจสอบพารามิเตอร์การอนุญาตการควบคุมการเข้าถึงสำหรับคำขอจริง หากเซิร์ฟเวอร์อนุญาตพารามิเตอร์คำขอ ส่งและรับคำขอข้ามต้นทางจริง

หาก access-control-allow-origin: * แสดงว่าอนุญาตการตอบกลับสำหรับต้นทางทั้งหมด แต่มันไม่ปลอดภัยถ้าไม่จำเป็น

วิธีเปิดใช้งาน CORS

หากต้องการเปิดใช้งาน CORS สำหรับโดเมนใดๆ ให้เปิดใช้งานส่วนหัว CORS เพื่ออนุญาตที่มา วิธีการ ส่วนหัวที่กำหนดเอง ข้อมูลประจำตัว ฯลฯ

  • เบราว์เซอร์อ่านส่วนหัว CORS จากเซิร์ฟเวอร์และอนุญาตคำขอจริงจากไคลเอนต์หลังจากตรวจสอบพารามิเตอร์คำขอแล้วเท่านั้น
  • Access-Control-Allow-Origin: เพื่อระบุโดเมนที่แน่นอน (https://app.geekflate.com, https://lab.admintrick.com.com) หรือสัญลักษณ์แทน
  • Access-Control-Allow-Methods: เพื่ออนุญาตวิธี HTTP (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) ที่เราต้องการเท่านั้น
  • Access-Control-Allow-Headers: เพื่ออนุญาตเฉพาะส่วนหัว (Authorization, csrf-token)
  • Access-Control-Allow-Credentials: ค่าบูลีนที่ใช้เพื่ออนุญาต cross-origin-credentials (คุกกี้, ส่วนหัวการให้สิทธิ์)

Access-Control-Max-Age: บอกให้เบราว์เซอร์แคชการตอบสนอง preflight ในบางครั้ง

Access-Control-Expose-Headers: ระบุส่วนหัวที่สามารถเข้าถึงได้โดยสคริปต์ฝั่งไคลเอ็นต์

สำหรับการเปิดใช้งาน CORS ใน apache และเว็บเซิร์ฟเวอร์ Nginx ให้ทำตามบทช่วยสอนนี้

const express = require('express');
const app = express()

app.get('/users', function (req, res, next) {
  res.json({msg: 'user get'})
});

app.post('/users', function (req, res, next) {
    res.json({msg: 'user create'})
});

app.put('/users', function (req, res, next) {
    res.json({msg: 'User update'})
});

app.listen(80, function () {
  console.log('CORS-enabled web server listening on port 80')
})

เปิดใช้งาน CORS ใน ExpressJS

มาดูตัวอย่างแอป ExpressJS ที่ไม่มี CORS:

npm install cors

ในตัวอย่างข้างต้น เราได้เปิดใช้งานปลายทาง API ของผู้ใช้สำหรับเมธอด POST, PUT, GET แต่ไม่ใช่เมธอด DELETE

เพื่อให้ง่ายต่อการเปิดใช้งาน CORS ในแอป ExpressJS คุณสามารถติดตั้ง cors

app.use(cors({
    origin: '*'
}));

Access-Control-Allow-Origin

app.use(cors({
    origin: 'https://app.admintrick.com.com'
}));

เปิดใช้งาน CORS สำหรับโดเมนทั้งหมด

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ]
}));

การเปิดใช้งาน CORS สำหรับโดเมนเดียว

หากคุณต้องการอนุญาต CORS สำหรับแหล่งกำเนิด https://app.admintrick.com.com และ https://lab.admintrick.com.com

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST']
}));

Access-Control-Allow-Methods

สำหรับการเปิดใช้งาน CORS สำหรับวิธีการทั้งหมด ให้ข้ามตัวเลือกนี้ในโมดูล CORS ใน ExpressJS แต่สำหรับการเปิดใช้งานวิธีการเฉพาะ (GET, POST, PUT)

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token']
}));

Access-Control-Allow-Headers

ใช้เพื่ออนุญาตให้ส่วนหัวอื่นที่ไม่ใช่ค่าเริ่มต้นส่งพร้อมกับคำขอจริง

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true
}));

Access-Control-Allow-Credentials

ข้ามสิ่งนี้หากคุณไม่ต้องการบอกเบราว์เซอร์ให้อนุญาตข้อมูลรับรองตามคำขอแม้ใน withCredentials จะถูกตั้งค่าเป็น true

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600 
}));

Access-Control-Max-Age

  การกู้คืนรหัสผ่านของ Excel เป็นเรื่องง่ายด้วยเครื่องมือ 8 อย่างเหล่านี้

เพื่อให้เบราว์เซอร์ใกล้ชิดกับแคชข้อมูลการตอบสนองของ preflight ในแคชสำหรับวินาทีที่ระบุ ข้ามสิ่งนี้หากคุณไม่ต้องการแคชการตอบกลับ

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600,
    exposedHeaders: ['Content-Range', 'X-Content-Range']
}));

การตอบสนอง preflight ที่แคชไว้จะใช้งานได้เป็นเวลา 10 นาทีในเบราว์เซอร์

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600,
    exposedHeaders: ['*', 'Authorization', ]
}));

การเข้าถึง-การควบคุม-เปิดเผย-ส่วนหัว

ถ้าเราใส่ไวด์การ์ด

ใน ExposedHeaders จะไม่เปิดเผยส่วนหัวการให้สิทธิ์ เลยต้องเปิดเผยให้ชัดเหมือนข้างล่างนี้

ด้านบนจะแสดงส่วนหัวและส่วนหัวการอนุญาตทั้งหมดด้วย

  • คุกกี้ HTTP คืออะไร
  • คุกกี้คือข้อมูลชิ้นเล็กๆ ที่เซิร์ฟเวอร์จะส่งไปยังเบราว์เซอร์ไคลเอ็นต์ ในคำขอในภายหลัง เบราว์เซอร์จะส่งคุกกี้ทั้งหมดที่เกี่ยวข้องกับโดเมนเดียวกันในทุกคำขอ
  • คุกกี้มีแอตทริบิวต์ซึ่งสามารถกำหนดเพื่อให้คุกกี้ทำงานแตกต่างออกไปตามที่เราต้องการ
  • ชื่อ ชื่อคุกกี้.
  • ค่า: ข้อมูลของคุกกี้ที่เกี่ยวข้องกับชื่อคุกกี้
  • โดเมน: คุกกี้จะถูกส่งไปยังโดเมนที่กำหนดเท่านั้น
  • เส้นทาง: คุกกี้ที่ส่งหลังจากเส้นทางคำนำหน้า URL ที่กำหนดไว้เท่านั้น สมมติว่าเราได้กำหนดเส้นทางคุกกี้ของเราเช่น path=’admin/’ ไม่ได้ส่งคุกกี้สำหรับ URL https://admintrick.com.com/expire/ แต่ส่งด้วยคำนำหน้า URL https://admintrick.com.com/admin/
  • อายุสูงสุด/หมดอายุ (ตัวเลขเป็นวินาที): คุกกี้จะหมดอายุเมื่อใด อายุการใช้งานของคุกกี้ทำให้คุกกี้ใช้งานไม่ได้หลังจากเวลาที่กำหนด [Strict, Lax, None]HTTPOnly(Boolean): เซิร์ฟเวอร์แบ็กเอนด์สามารถเข้าถึงคุกกี้ HTTPOnly นั้นได้ แต่ไม่สามารถเข้าถึงสคริปต์ฝั่งไคลเอ็นต์เมื่อเป็นจริง ปลอดภัย (บูลีน): คุกกี้จะส่งผ่านโดเมน SSL/TLS เมื่อเป็นจริงเท่านั้นsameSite(สตริง

): ใช้เพื่อเปิดใช้งาน/จำกัดคุกกี้ที่ส่งผ่านคำขอข้ามไซต์ หากต้องการทราบรายละเอียดเพิ่มเติมเกี่ยวกับคุกกี้ sameSite ดูที่

MDN

. ยอมรับสามตัวเลือก เข้มงวด, หละหลวม, ไม่มี ตั้งค่าความปลอดภัยของคุกกี้เป็น true สำหรับการกำหนดค่าคุกกี้ sameSite=None

เหตุใดจึงใช้คุกกี้ HTTPOnly สำหรับโทเค็น

การจัดเก็บโทเค็นการเข้าถึงที่ส่งจากเซิร์ฟเวอร์ในที่เก็บข้อมูลฝั่งไคลเอ็นต์ เช่น ที่จัดเก็บในตัวเครื่อง ฐานข้อมูลที่จัดทำดัชนี และคุกกี้ (ไม่ได้ตั้งค่า HTTPOnly เป็น true) มีความเสี่ยงต่อการโจมตี XSS มากกว่า สมมติว่าหน้าใดหน้าหนึ่งของคุณอ่อนแอต่อการโจมตี XSS ผู้โจมตีอาจใช้โทเค็นของผู้ใช้ที่จัดเก็บไว้ในเบราว์เซอร์ในทางที่ผิด

คุกกี้ HTTPOnly ถูกตั้งค่า/รับโดยเซิร์ฟเวอร์/แบ็กเอนด์เท่านั้น แต่ไม่ใช่ในฝั่งไคลเอ็นต์

  • สคริปต์ฝั่งไคลเอ็นต์จำกัดการเข้าถึงคุกกี้ HTTPonly นั้น ดังนั้นคุกกี้ HTTPOnly จึงไม่เสี่ยงต่อการโจมตี XSS และมีความปลอดภัยมากขึ้น เพราะเข้าถึงได้โดยเซิร์ฟเวอร์เท่านั้น
  • เปิดใช้งานคุกกี้ HTTPOnly ในแบ็กเอนด์ที่เปิดใช้งาน CORS
  • การเปิดใช้งานคุกกี้ใน CORS จำเป็นต้องมีการกำหนดค่าด้านล่างในแอปพลิเคชัน/เซิร์ฟเวอร์
  • ตั้งค่าส่วนหัว Access-Control-Allow-Credentials เป็น true

Access-Control-Allow-Origin และ Access-Control-Allow-Headers ไม่ควรเป็นสัญลักษณ์แทน

const express = require('express'); 
const app = express();
const cors = require('cors');

app.use(cors({ 
  origin: [ 
    'https://app.geekflare.com', 
    'https://lab.geekflare.com' 
  ], 
  methods: ['GET', 'PUT', 'POST'], 
  allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], 
  credentials: true, 
  maxAge: 600, 
  exposedHeaders: ['*', 'Authorization' ] 
}));

app.post('/login', function (req, res, next) { 
  res.cookie('access_token', access_token, {
    expires: new Date(Date.now() + (3600 * 1000 * 24 * 180 * 1)), //second min hour days year
    secure: true, // set to true if your using https or samesite is none
    httpOnly: true, // backend only
    sameSite: 'none' // set to none for cross-request
  });

  res.json({ msg: 'Login Successfully', access_token });
});

app.listen(80, function () { 
  console.log('CORS-enabled web server listening on port 80') 
}); 

.

แอตทริบิวต์ SameSite ของคุกกี้ควรเป็น None

  8 สุดยอด 7 วันในการโฮสต์เซิร์ฟเวอร์ Die สำหรับทุกคน

สำหรับการเปิดใช้งานค่า sameSite เป็น none ให้ตั้งค่าการรักษาความปลอดภัยเป็น true: เปิดใช้งานแบ็กเอนด์ด้วยใบรับรอง SSL/TLS เพื่อทำงานในชื่อโดเมน

มาดูตัวอย่างโค้ดที่กำหนดโทเค็นการเข้าถึงในคุกกี้ HTTPOnly หลังจากตรวจสอบข้อมูลรับรองการเข้าสู่ระบบ

คุณสามารถกำหนดค่าคุกกี้ CORS และ HTTPOnly ได้โดยใช้สี่ขั้นตอนข้างต้นในภาษาแบ็กเอนด์และเว็บเซิร์ฟเวอร์ของคุณ

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.admintrick.com.com/user', true);
xhr.withCredentials = true;
xhr.send(null);

คุณสามารถทำตามบทช่วยสอนนี้สำหรับ apache และ Nginx เพื่อเปิดใช้งาน CORS โดยทำตามขั้นตอนข้างต้น

fetch('http://api.admintrick.com.com/user', {
  credentials: 'include'
});

withCredentials สำหรับคำขอข้ามต้นทาง

$.ajax({
   url: 'http://api.admintrick.com.com/user',
   xhrFields: {
      withCredentials: true
   }
});

ข้อมูลประจำตัว (คุกกี้ การอนุญาต) ที่ส่งไปพร้อมกับคำขอต้นทางเดียวกันโดยค่าเริ่มต้น สำหรับ cross-origin เราต้องระบุ withCredentials ให้เป็นจริง

axios.defaults.withCredentials = true

XMLHttpRequest API

เรียก API

JQuery AjaxAxiosบทสรุป ฉันหวังว่าบทความข้างต้นจะช่วยให้คุณเข้าใจวิธีการทำงานของ CORS และเปิดใช้งาน CORS สำหรับคำขอข้ามต้นทางในเซิร์ฟเวอร์ เหตุใดการจัดเก็บคุกกี้ใน HTTPOnly จึงปลอดภัยและการใช้ withCredentials ในไคลเอนต์สำหรับคำขอข้ามต้นทาง

เรื่องล่าสุด

x