การสร้างไฟล์ชั่วคราวในทุบตี


150

มีวิธีที่ดีกว่าในการสร้างไฟล์ชั่วคราวในสคริปต์ทุบตีหรือไม่?

ปกติฉันแค่ตั้งชื่อสิ่งที่อยู่ในใจของฉันเช่น tempfile-123 เพราะมันจะถูกลบเมื่อสคริปต์จบ มีข้อเสียในการทำเช่นนี้นอกเหนือจากการเขียนทับ tempfile-123 ที่เป็นไปได้ในโฟลเดอร์ปัจจุบันหรือไม่? หรือมีข้อได้เปรียบใด ๆ ในการสร้างไฟล์ชั่วคราวอย่างระมัดระวังมากขึ้น?


1
อย่าใช้ไฟล์ชั่วคราว ใช้ไดเรกทอรีชั่วคราวแทน และอย่าใช้ mktemp ดูที่นี่ทำไม: codeproject.com/Articles/15956/…
ceving

17
@ceving บทความนั้นผิดปกติอย่างน้อยที่สุดเมื่อนำไปใช้กับคำสั่ง shell mktemp (ตรงข้ามกับการเรียกไลบรารี mktemp) เมื่อ mktemp สร้างไฟล์ด้วย umask ที่ จำกัด การโจมตีที่ได้รับก็ต่อเมื่อผู้โจมตีทำงานภายใต้บัญชีเดียวกับผู้โจมตี ... ซึ่งในกรณีที่เกมหายไปแล้ว สำหรับแนวทางปฏิบัติที่ดีที่สุดในโลกของการเขียนสคริปต์เชลล์โปรดดูmywiki.wooledge.org/BashFAQ/062
Charles Duffy

คุณยังสามารถใช้tempfile(1)กับระบบที่มี
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

คำตอบ:


179

mktemp(1)หน้าคนอธิบายได้ค่อนข้างดี:

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

ในสคริปต์ฉันเรียก mktemp คล้าย ๆ

mydir=$(mktemp -d "${TMPDIR:-/tmp/}$(basename $0).XXXXXXXXXXXX")

ซึ่งสร้างไดเร็กตอรี่ชั่วคราวที่ฉันสามารถใช้งานได้, และในที่ที่ฉันสามารถตั้งชื่อไฟล์ที่เป็นจริงได้อย่างปลอดภัย

mktempไม่ใช่มาตรฐาน แต่มีอยู่ในหลายแพลตฟอร์ม โดยทั่วไป "X" จะถูกแปลงเป็นแบบสุ่มและอาจจะสุ่มมากขึ้น อย่างไรก็ตามบางระบบ (busybox ash สำหรับหนึ่ง) จำกัด การสุ่มนี้อย่างมีนัยสำคัญมากกว่าระบบอื่น ๆ


อย่างไรก็ตามการสร้างไฟล์ชั่วคราวที่ปลอดภัยนั้นมีความสำคัญมากกว่าแค่การเขียนสคริปต์เชลล์ นั่นเป็นเหตุผลที่หลามมีtempfile , Perl มีFile :: Temp , ruby ​​มีTempfile , ฯลฯ ...


1
ตัวเลือก -t เลิกใช้แล้ว: gnu.org/software/coreutils/manual/html_node/ …
Tibor Vass

7
ดูเหมือนว่าวิธีที่ปลอดภัยที่สุดและข้ามแพลตฟอร์มมากที่สุดในการใช้งานmktempอยู่ในการรวมกันกับเช่นดังนั้นbasename mktemp -dt "$(basename $0). XXXXXXXXXX"หากใช้โดยไม่ได้basenameคุณอาจได้รับข้อผิดพลาดเช่นนี้mktemp: แม่แบบที่ไม่ถูกต้อง `/tmp/MOB-SAN-JOB1-183-ScriptBuildTask-7300464891856663368.sh.XXXXXXXXXX' มีตัวคั่นไดเรกทอรี
i4niac

7
ไม่สนใจพวกเขาพิมพ์ผิด (พื้นที่เพิ่มเติม) mktemp -dt "$(basename $0).XXXXXXXXXX"เป็นวิธีที่ถูกต้อง
i4niac

3
@ i4niac: คุณต้องอ้างว่า$0มีช่องว่างมากมายในดินแดน OS X.mktemp -dt "$(basename "$0").XXXXXX"
Orwellophile

11
นอกจากนี้อาจเป็นการดีที่จะลบ tempdir เมื่อสิ้นสุดการเรียกใช้สคริปต์:trap "rm -rf $mydir" EXIT
KumZ

43

ใช่ใช้mktemp

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

> mktemp
/tmp/tmp.xx4mM3ePQY
>

19

คุณอาจต้องการดู mktemp

ยูทิลิตี้ mktemp ใช้เทมเพลตชื่อไฟล์ที่กำหนดและเขียนทับส่วนหนึ่งของมันเพื่อสร้างชื่อไฟล์ที่ไม่ซ้ำกัน แม่แบบอาจเป็นชื่อไฟล์ที่มี 'Xs' ต่อท้ายตัวอย่างเช่น /tmp/tfile.XXXXXXXXXXXX 'Xs' ต่อท้ายจะถูกแทนที่ด้วยการรวมกันของหมายเลขกระบวนการปัจจุบันและตัวอักษรแบบสุ่ม

สำหรับรายละเอียดเพิ่มเติม: man mktemp


11

มีข้อได้เปรียบใด ๆ ในการสร้างไฟล์ชั่วคราวอย่างระมัดระวังมากขึ้น

โดยปกติไฟล์ชั่วคราวจะถูกสร้างขึ้นในไดเรกทอรีชั่วคราว (เช่น/tmp) ซึ่งผู้ใช้และกระบวนการอื่นทั้งหมดมีการเข้าถึงแบบอ่านและเขียน (สคริปต์อื่น ๆ สามารถสร้างไฟล์ใหม่ที่นั่น) ดังนั้นสคริปต์ควรระมัดระวังเกี่ยวกับการสร้างไฟล์เช่นการใช้กับสิทธิ์ที่ถูกต้อง (เช่นอ่านอย่างเดียวสำหรับเจ้าของดูhelp umask:) และชื่อไฟล์ไม่ควรเดาได้ง่าย (สุ่มอย่างสมบูรณ์) มิฉะนั้นหากชื่อไฟล์นั้นไม่ซ้ำกันมันสามารถสร้างความขัดแย้งกับสคริปต์เดียวกันได้หลายครั้ง (เช่นสภาพการแข่งขัน) หรือผู้โจมตีบางคนอาจจี้ข้อมูลที่ละเอียดอ่อนบางอย่าง (เช่นเมื่อสิทธิ์อนุญาตเปิดมากเกินไปและชื่อไฟล์นั้นเดาง่าย) หรือสร้าง / แทนที่ไฟล์ด้วยรหัสรุ่นของตัวเอง (เช่นการเปลี่ยนคำสั่งหรือแบบสอบถาม SQL ขึ้นอยู่กับสิ่งที่เป็น) เก็บไว้)


คุณสามารถใช้วิธีการต่อไปนี้เพื่อสร้างไดเรกทอรีชั่วคราว:

TMPDIR=".${0##*/}-$$" && mkdir -v "$TMPDIR"

หรือไฟล์ชั่วคราว:

TMPFILE=".${0##*/}-$$" && touch "$TMPFILE"

อย่างไรก็ตามมันยังคงคาดเดาได้และไม่ถือว่าปลอดภัย

ตามที่man mktempเราสามารถอ่านได้:

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

ดังนั้นเพื่อความปลอดภัยขอแนะนำให้ใช้mktempคำสั่งเพื่อสร้างไฟล์หรือไดเรกทอรีชั่วคราวที่ไม่ซ้ำกัน ( -d)


2
ไม่ใช่สิ่งที่ถูกถาม แต่มันสามารถเป็นโซลูชั่นที่สมบูรณ์แบบ
jpbochi

1
@jpbochi ฉันได้ปรับปรุงคำตอบเพื่อตอบคำถาม แจ้งให้เราทราบหากช่วยได้
kenorb

2
มันจะปรับปรุงคำตอบแน่นอน upvote ของฉันเป็นของคุณแล้ว ไม่สามารถลงคะแนนมากขึ้น ข้อเสนอแนะข้อหนึ่งที่ฉันต้องทำคืออธิบายสิ่งที่จะขยาย${0##*/}และ$$หรือเชื่อมโยงไปยังเอกสารบางอย่างเกี่ยวกับมัน
jpbochi

0

mktemp น่าจะเป็นงานที่หลากหลายที่สุดโดยเฉพาะอย่างยิ่งถ้าคุณวางแผนที่จะทำงานกับไฟล์ซักพัก

คุณยังสามารถใช้โอเปอเรเตอร์การทดแทนโปรเซสได้ <()หากคุณต้องการใช้ไฟล์ชั่วคราวเป็นการป้อนข้อมูลไปยังคำสั่งอื่นเช่น:

$ diff <(echo hello world) <(echo foo bar)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.