วิธีตั้ง Composer ให้รอเวอร์ชันใหม่สักพักก่อนติดตั้ง

Composer 2.11 เพิ่ม cooldown ที่ให้ composer update ข้ามเวอร์ชันที่เพิ่งออกไม่ถึงเวลาที่เราตั้ง เผื่อเวอร์ชันนั้นเป็นของปลอมจากบัญชีที่โดนยึด ตั้งใน composer.json บรรทัดเดียว ยกเว้น package ของเราเองได้ และไม่กระทบ composer install ตอน deploy

เผยแพร่
เวลาอ่าน
5 นาที
สรุปจาก
Eric L. Barnes · phpnews.net
ในโพสต์นี้ 7 หัวข้อ
  1. cooldown คือการรอให้เวอร์ชันใหม่ผ่านไปสักพัก
  2. เปิด cooldown ใน composer.json
  3. ยกเว้น package ของเราเองหรือตอนต้องรีบแพตช์
  4. cooldown ทำงานตอน update ไม่ใช่ตอน deploy
  5. Composer 2.11 แก้ช่องโหว่ของตัวเองอีกเก้าเรื่อง
  6. โหลด zip ไม่ผ่านแล้ว Composer จะไม่สลับไปโหลดจาก git เอง
  7. ของเล็ก ๆ อื่นในรุ่นนี้

ปกติเรารัน composer update แล้วก็ได้ทุก package ในเวอร์ชันล่าสุดที่ตรงกับ composer.json ทันที รวมถึงเวอร์ชันที่เพิ่งออกเมื่อสิบนาทีก่อนด้วย ถ้าเวอร์ชันนั้นมาจากบัญชีของคนดูแล package ที่โดนยึด แล้วคนร้ายใส่โค้ดขโมย .env ไว้ เราก็จะได้ของนั้นมาเป็นกลุ่มแรก ๆ (ถ้ายังไม่คุ้นว่า install กับ update ต่างกันยังไง ลองอ่านวิธีใช้ Composer จัดการ package ก่อนได้ครับ)

cooldown คือการรอให้เวอร์ชันใหม่ผ่านไปสักพัก

package ที่โดนแอบใส่โค้ดอันตรายมักโดนจับได้เร็ว เครื่องสแกนอัตโนมัติเจอภายในไม่กี่นาที ส่วนที่ต้องมีคนอ่านโค้ดถึงจะเจอก็ใช้เวลาเป็นชั่วโมงหรือเป็นวัน แล้ว Packagist ก็ถอดเวอร์ชันนั้นออก ถ้าเราไม่รีบหยิบเวอร์ชันที่เพิ่งออก ก็มีโอกาสสูงที่เวอร์ชันอันตรายจะหายไปก่อนถึงเครื่องเรา

cooldown ของ Composer 2.11 ทำแบบนั้นให้ ในประกาศของ Packagist เรียกอีกชื่อว่า minimum release age และแนะนำให้ตั้งไว้ระหว่างไม่กี่ชั่วโมงถึงหนึ่งวัน ซึ่งเป็นแนวเดียวกับที่ Dependabot รอให้เวอร์ชันใหม่ออกมาครบ 3 วันก่อนเปิด PR ต่างกันที่ cooldown ใช้กับ composer update ที่เรารันเองบนเครื่องด้วย ฝั่ง npm ก็ตั้งแบบเดียวกันได้ใน .npmrc อ่านต่อได้ที่วิธีตั้ง npm ให้รอเวอร์ชันใหม่สักพักก่อนติดตั้ง

Composer ดูอายุของเวอร์ชันจาก published-time ที่ Packagist.org บันทึกตอนเวอร์ชันนั้นขึ้นมาครั้งแรก ไม่ได้ดูจาก time เพราะคนปล่อย package กำหนดค่า time เองได้ คนร้ายจึงย้อนวันที่ให้ดูเหมือนออกมานานแล้วได้

เปิด cooldown ใน composer.json

cooldown ปิดไว้เป็นค่าเริ่มต้น เปิดได้ด้วยการใส่ period ใต้ config.policy

{
    "config": {
        "policy": {
            "cooldown": {
                "period": "1 day"
            }
        }
    }
}
  • period เขียนเป็นตัวเลขกับหน่วย second, minute, hour, day หรือ week เช่น "24 hours" หรือ "7 days" ใส่เป็นจำนวนวินาทีก็ได้ ส่วนคำอย่าง "tomorrow" ใช้ไม่ได้
  • block เป็น true อยู่แล้ว ถ้าตั้งเป็น false Composer จะไม่ข้ามเวอร์ชันที่ยังไม่ครบเวลา

ถ้าอยากให้มีผลกับทุกโปรเจกต์บนเครื่อง ก็ใส่ค่าเดียวกันไว้ใน config.json ของ Composer ฝั่ง global แทน (ดูว่าอยู่โฟลเดอร์ไหนได้ด้วย composer config --global home)

หลังเพิ่ม policy ใน composer.json Composer จะเตือนว่า composer.lock ไม่ตรงกับ composer.json เพราะรุ่นนี้นับ policy เป็นส่วนหนึ่งของ lock file ด้วย รัน composer update --lock หนึ่งรอบแล้ว commit lock file ที่ได้ไปพร้อมกัน

ยกเว้น package ของเราเองหรือตอนต้องรีบแพตช์

package ที่เราดูแลเองไม่จำเป็นต้องรอ ส่วน package ที่ต้องได้แพตช์ความปลอดภัยทันทีก็ไม่ควรรอ ใส่ไว้ใน ignore พร้อมเหตุผล ใช้ * แทนทั้ง vendor ได้

"cooldown": {
    "period": "1 day",
    "ignore": {
        "phattarachai/*": "package ของเราเอง"
    }
}

แพตช์ความปลอดภัยต้องรอ cooldown เหมือนเวอร์ชันอื่น Packagist ตั้งใจให้เป็นแบบนี้ เพราะถ้ายกเว้นเวอร์ชันที่อ้างว่าแก้ช่องโหว่ คนร้ายที่คุม package ได้ก็แค่ประกาศ advisory ปลอม หรือปล่อยเวอร์ชันอันตรายตามหลังแพตช์จริงทันที วันที่ package ที่ใช้อยู่มีช่องโหว่ใหญ่ เราจึงต้องข้าม cooldown เองทีละครั้ง

composer update --no-cooldown laravel/framework

--no-cooldown ใช้ได้กับ update, require, remove และ create-project และข้ามแค่ cooldown ส่วนการเช็ค advisory ยังทำงานตามปกติ ถ้าอยากปิดชั่วคราวทั้งคำสั่ง ใช้ COMPOSER_POLICY_COOLDOWN_PERIOD=0 composer update ได้เหมือนกัน

cooldown ทำงานตอน update ไม่ใช่ตอน deploy

cooldown มีผลเฉพาะตอนที่ Composer ต้องเลือกเวอร์ชัน

คำสั่ง cooldown มีผลไหม
composer update, composer require มีผล ข้ามเวอร์ชันที่ยังไม่ครบเวลา
composer install ไม่มีผล ติดตั้งตาม composer.lock ทุกตัว
composer outdated, composer show --latest บอกเวอร์ชันล่าสุดที่ผ่าน cooldown แล้ว และบอกว่ามีเวอร์ชันไหนรออยู่

ขั้น deploy ที่รัน composer install (ลำดับขั้น deploy ของเราก็เป็นแบบนั้น) จึงไม่เปลี่ยนอะไร จุดที่ cooldown ช่วยคือตอนที่เรา update บนเครื่องตัวเองแล้ว commit lock file ขึ้นไป

ถ้ารัน composer update ทั้งโปรเจกต์แล้ว lock file มีเวอร์ชันที่ยังไม่ครบเวลาอยู่ Composer จะถอยไปใช้เวอร์ชันก่อนหน้า และถ้าไม่มีเวอร์ชันอื่นที่ตรงกับ composer.json เลย คำสั่งจะล้ม ส่วน dev-main และ PHP extension ไม่ต้องรอ cooldown

Composer 2.11 แก้ช่องโหว่ของตัวเองอีกเก้าเรื่อง

ในrelease notes มีรายการแก้ด้านความปลอดภัยเก้ารายการ สองรายการมี advisory และยังไม่มีเลข CVE

  • GHSA-fm2w-xgf2-f9fh ระดับ Moderate (5.3) Composer คัดลอก HTTP header ที่ตั้งไว้กับ repository ใน options.http.header เช่น token ของ private repository ลงไปใน composer.lock ด้วย ถ้าเราใส่ token แบบนี้ไว้ ให้อัปเดต Composer แล้วรัน composer update --lock ให้ lock file เขียนใหม่ และถ้า lock file ที่มี token เคย commit ขึ้น repo ไปแล้ว ให้ยกเลิก token นั้นแล้วออกใหม่ ส่วน token ที่เก็บใน auth.json หรือ COMPOSER_AUTH ไม่ได้รับผลกระทบ
  • GHSA-c968-33g3-wh7p ระดับ High (7.1) ชื่อ package ที่มี .. ใน vendor/composer/installed.json ทำให้ Composer ลบหรือเขียนไฟล์นอกโฟลเดอร์ vendor ได้ คนร้ายต้องเขียนไฟล์นั้นได้ก่อน เช่นผ่าน cache ของ CI ที่ใช้ร่วมกับ pull request จาก fork

สองรายการนี้แก้ไปถึง Composer 2.2.31 ซึ่งเป็นรุ่น LTS ด้วย อีกเจ็ดรายการเป็นการปิดช่องเล็ก ๆ เช่นไม่ส่ง header ของเราไปให้ host อื่นที่ชื่อขึ้นต้นเหมือนกัน และไม่ให้รหัสผ่าน SVN โผล่ใน debug output ถ้าอยากรู้ว่าระดับ Moderate กับ High ต่างกันแค่ไหน อ่านต่อได้ที่คะแนน CVSS คืออะไร

โหลด zip ไม่ผ่านแล้ว Composer จะไม่สลับไปโหลดจาก git เอง

ใน Composer 2.9 ลงไป ถ้าโหลดไฟล์ zip ของ package ไม่สำเร็จ Composer จะเปลี่ยนไป clone จาก git แทนโดยไม่บอก ซึ่งเสี่ยงเพราะต้นทางอาจไม่ใช่ที่เดียวกับที่เราเชื่อ 2.10 ปิดพฤติกรรมนี้เป็นค่าเริ่มต้นและเหลือตัวเลือก source-fallback ไว้ชั่วคราว 2.11 ลบตัวเลือกนั้นออกแล้ว โหลด zip ไม่ผ่านก็จบที่ error

ถ้ามี package ที่ต้องติดตั้งจาก git จริง ๆ ให้บอก Composer ตรง ๆ ด้วย preferred-install ราย vendor หรือ --prefer-source

ของเล็ก ๆ อื่นในรุ่นนี้

  • composer audit --direct เช็คเฉพาะ package ที่เราเรียกใช้ตรง ๆ ใน composer.json
  • composer install ล้มเมื่อ composer.lock ยังมี merge conflict ค้างที่ content-hash แทนที่จะติดตั้งไปทั้งอย่างนั้น
  • เจอ 429 ที่มี Retry-After Composer จะรอแล้วลองใหม่เอง
  • resolve dependency เร็วขึ้นและใช้ memory น้อยลง

อัปเดตด้วย composer self-update แล้วเช็คด้วย composer --version ว่าเป็น 2.11 ครับ ส่วนรายละเอียดทั้งหมด รวมถึง policy แบบอื่นที่ตั้งได้ อยู่ในประกาศของ Packagist และเอกสาร config ของ Composer

อ่านต่อ

ถูกอ้างถึงใน

ล่าสุด

ดูทั้งหมด →