Việc trích dẫn tên tệp có đủ bảo mật để chạy `xargs sudo rm -rf` không?


10

Tôi đã viết một tập lệnh xóa tất cả ngoại trừ hai tệp cuối cùng trong một thư mục:

#!/bin/bash
ls -1 --quoting-style=shell-always /path/to/some/folder \
    | head -n -2 \
    | xargs printf -- "'/path/to/some/folder/%s'\n" \
    | xargs sudo rm -rf

Kịch bản này sẽ được thực hiện như một công việc định kỳ mỗi ngày.

Lý do là như sau:

  1. Có được danh sách tất cả các tệp bằng cách sử dụng ls -1(để tôi nhận được một tệp trên mỗi dòng);

  2. Loại bỏ hai cuối cùng khỏi danh sách bằng cách sử dụng head -n -2;

  3. lsin các đường dẫn tương đối, sử dụng xargs printfđiều để thêm vào đường dẫn thư mục và biến nó thành một đường dẫn tuyệt đối;

  4. Gửi cho họ sudo rm -rfsử dụng xargs.

Mọi người đều có quyền truy cập vào thư mục này, vì vậy bất kỳ ai cũng có thể tạo và xóa bất kỳ tệp nào trong thư mục này.

Vấn đề là: sudo rm -rf thật đáng sợ. xargs sudo rm -rflà vô cùng đáng sợ.

Tôi muốn chắc chắn rằng không ai có thể làm hỏng các thư mục / hệ thống khác bằng cách tạo các tệp thông minh sẽ bị xóa (vô tình hoặc cố ý). Tôi không biết, một cái gì đó thông minh như:

file with / spaces.txt

mà có thể dẫn đến một siêu đáng sợ sudo rm -rf /.

EDIT: Lỗi của tôi, tên tệp không thể chứa /, vì vậy vấn đề cụ thể này sẽ không xảy ra, nhưng câu hỏi về việc liệu có những rủi ro khác hay không vẫn còn tồn tại.

Đây là lý do tại sao tôi đang sử dụng --quoting-style=shell-always, điều này sẽ ngăn chặn mọi thủ thuật với các tệp có khoảng trắng. Nhưng bây giờ tôi tự hỏi liệu ai đó có thể thông minh hơn với khoảng trắng dấu ngoặc kép trong tên tệp, có lẽ.

Kịch bản của tôi có an toàn không?


Lưu ý: Tôi cần sudobởi vì tôi đang truy cập thư mục từ xa (từ một ổ đĩa mạng được ánh xạ bằng cách sử dụng mount) và tôi không thể làm cho nó hoạt động mà không có sudo.


3
Bạn đã xem xét làm một cái gì đó như thế printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rmnào?
Steeldo

Có thể tạo một tệp có ký tự /trong tên tôi đang cố gắng đạt được điều này ở đây không
George Udosen

3
@George Không, tên tệp không thể chứa dấu gạch chéo.
wjandrea

Vì vậy, khi OP nói người thông minh, tôi đã tự hỏi ...
George Udosen

6
Đơn giản vì bạn đang phân tích cú pháp lsđầu ra, đây đã là một lệnh được viết kém, ngay cả khi trích dẫn. lscũng sử dụng Locale cho thứ tự sắp xếp, tôi nghĩ, vì vậy tôi không thấy mục đích của những gì headtrong việc loại bỏ cuối cùng 2 (trừ khi bạn đang cố gắng để thoát khỏi ...đó iirc không được phép làm đối số cho rmChỉ cần sử dụng anyway. find /path/to/folder -type f delete. Và không sudonếu bạn chạy từ cron - cron đã ở cấp gốc
Sergiy Kolodyazhnyy

Câu trả lời:


10

Trong Linux, bất kỳ ký tự nào cũng là một tên tệp hợp lệ cấu thành ký tự ngoại trừ:

  • \0 (ASCII NUL): như được sử dụng để chấm dứt chuỗi trong C
  • / (dấu gạch chéo): như được sử dụng để phân tách đường dẫn

Vì vậy, cách tiếp cận của bạn chắc chắn sẽ không hoạt động trong nhiều trường hợp như bạn có thể tưởng tượng, ví dụ như nó có xử lý một dòng mới ( \n) trong tên tệp không? ( Gợi ý: Không ).

Vài lưu ý:

  • Đừng phân tích cú pháp ls; sử dụng các công cụ chuyên dụng (có ít nhất một cho hầu hết các trường hợp sử dụng)
  • Khi xử lý tên tệp, hãy cố gắng tận dụng đầu ra tách biệt NUL được cung cấp bởi hầu hết các công cụ GNU hoạt động với dữ liệu đó
  • Cẩn thận khi đường ống, đảm bảo cả hai chương trình có thể hiểu được phân tách NUL
  • Bất cứ khi nào bạn gọi xargs, hãy xem nếu bạn có thể thoát khỏi find ... -exec; trong hầu hết các trường hợp, bạn sẽ ổn chỉ với findmột mình

Tôi nghĩ rằng những điều này sẽ giúp bạn đi bây giờ. Steeldo đã cung cấp ý tưởng tách biệt NUL trong bình luận ( printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm), sử dụng ý tưởng này làm điểm bắt đầu.


Cảm ơn câu trả lời của bạn :) Tôi nghĩ bạn nên trích dẫn bình luận của Steeldo thay vì chỉ đề cập (vì các bình luận không phải là vĩnh viễn). Tôi cũng sẽ xem xét find, cảm ơn vì lời đề nghị.
Pedro A

Mặc dù vậy, tôi có một câu hỏi: Tôi không hiểu ý của bạn là "cách tiếp cận của bạn chắc chắn sẽ không hiệu quả trong nhiều trường hợp như bạn có thể tưởng tượng" - "không hoạt động" như trong "không an toàn" hay "không an toàn"? Bởi vì "cách tiếp cận của bạn" đề cập đến tôi chứ không phải người dùng độc hại và các tuyên bố trước đó của bạn có lợi cho tôi, vì vậy tôi bối rối.
Pedro A

@PedroA Bạn là bạn :) Như tôi đã nói, vì tất cả các ký tự đều hợp lệ ngoại trừ hai ký tự được đề cập, bạn sẽ có thể tưởng tượng nhiều trường hợp lsphương pháp phân tích cú pháp của bạn sẽ thất bại, ví dụ như bạn đã tính đến một dòng mới trong tên tệp?
heemayl

Ồ, một dòng mới trong tên tệp ... Tôi đã không nghĩ về điều đó. Nếu bạn không phiền khi thêm nó vào câu trả lời của mình :) Ngoài ra, xin lỗi để hỏi, nhưng head -zlàm gì? Nghe có vẻ vô lý nhưng tôi không có mancũng không infotrong linux CoreOS container của tôi ... Không thể tìm thấy trên Internet hoặc. Tôi nhận đượchead: invalid option 'z'
Pedro A

@PedroA Newline chỉ là một trường hợp, có nhiều trường hợp như bạn có thể đoán . Bạn cần GNU head(đi kèm với GNU coreutils). Đây là phiên bản trực tuyến: manpages.ubfox.com/manpages/xenial/man1/head.1.html
heemayl

3

xargs không hỗ trợ một số trích dẫn: với dấu ngoặc đơn, dấu ngoặc kép hoặc dấu gạch chéo ngược cho phép nó chấp nhận các đối số tùy ý¹, nhưng với cú pháp khác với cú pháp trích dẫn của shell giống như Bourne.

Việc triển khai GNU lsnhư tìm thấy trên Ubuntu không có bất kỳ chế độ trích dẫn nào tương thích với xargsđịnh dạng đầu vào.

ls --quoting-style=shell-alwaystương thích với cú pháp trích dẫn shell ksh93, bash và zsh, nhưng chỉ khi đầu ra của lsđược giải thích bởi shell trong cùng một miền như lskhi nó xuất ra nó. Ngoài ra, nên tránh một số địa phương, như những người sử dụng BIG5, BIG5-HKSCS, GBK hoặc GB18030.

Vì vậy, với các shell đó, bạn thực sự có thể làm:

typeset -a files
eval "files=($(ls --quoting-style=shell-always))"
xargs -r0a <(printf '%s\0' "${files[@]:0:3}") ...

Nhưng điều đó có ít lợi thế hơn:

files=(*(N))                 # zsh
files=(~(N)*)                # ksh93
shopt -s nullglob; files=(*) # bash

Trường hợp duy nhất trở nên hữu ích là khi bạn muốn sử dụng -ttùy chọn lssắp xếp các tệp theo mtime / atime / ctime hoặc -S/ -V. Nhưng ngay cả khi đó, bạn cũng có thể sử dụng zsh:

files=(*(Nom))

ví dụ để sắp xếp các tệp theo mtime (sử dụng oLcho -Sncho -V).

Để xóa tất cả trừ hai tệp thông thường được sửa đổi gần đây nhất:

rm -f -- *(D.om[3,-1])

Vẫn còn một số hạn chế về độ dài (bởi execve()và trong một số xargstriển khai không phải là GNU tùy ý thấp hơn nhiều) và một số xargstriển khai không phải GNU sẽ bóp nghẹt đầu vào có chứa chuỗi byte không tạo thành các ký tự hợp lệ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.