ปกติเรารัน 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อยู่แล้ว ถ้าตั้งเป็นfalseComposer จะไม่ข้ามเวอร์ชันที่ยังไม่ครบเวลา
ถ้าอยากให้มีผลกับทุกโปรเจกต์บนเครื่อง ก็ใส่ค่าเดียวกันไว้ใน 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.jsoncomposer installล้มเมื่อcomposer.lockยังมี merge conflict ค้างที่content-hashแทนที่จะติดตั้งไปทั้งอย่างนั้น- เจอ 429 ที่มี
Retry-AfterComposer จะรอแล้วลองใหม่เอง - resolve dependency เร็วขึ้นและใช้ memory น้อยลง
อัปเดตด้วย composer self-update แล้วเช็คด้วย composer --version ว่าเป็น 2.11 ครับ ส่วนรายละเอียดทั้งหมด รวมถึง policy แบบอื่นที่ตั้งได้ อยู่ในประกาศของ Packagist และเอกสาร config ของ Composer






