Tôi có cần phải đóng gói các biến awk trong dấu ngoặc kép để vệ sinh chúng không?


7

Theo câu trả lời trên stackoverflow , tôi hiểu rằng việc đóng gói các biến bash trong dấu ngoặc kép là một cách khá an toàn để vệ sinh đầu vào của người dùng.

Còn các biến awk thì sao? Ví dụ: nếu tôi có một cái gì đó như:

awk -v SOURCEIP="$SOURCEIP" -v REVERSEDNS="$REVERSEDNS" '{
   gsub(/^_TMPSOURCEIP_/, SOURCEIP);
   gsub(/^_TMPREVERSEDNS_/, REVERSEDNS);
   print
}' /home/foo/footemplate

Tôi có nên đặt dấu ngoặc kép quanh biến trong các dòng gsub? Vì vậy, nó sẽ trông giống như:

awk -v SOURCEIP="$SOURCEIP" -v REVERSEDNS="$REVERSEDNS" '{
   gsub(/^_TMPSOURCEIP_/, "SOURCEIP");
   gsub(/^_TMPREVERSEDNS_/, "REVERSEDNS");
   print
}' /home/foo/footemplate

Hay điều này không tạo ra sự khác biệt?


3
Không, nếu bạn lấy SOURCEIP trong ngoặc kép bên trong awk, nó sẽ không được xử lý dưới dạng biến.
vội vàng

Câu trả lời:


5

Hai ví dụ này chứng minh sự khác biệt:

$ echo _TMP_ | awk -v VAR='some "text"' '{ gsub(/_TMP_/, VAR) ; print }'
some "text"
$ echo _TMP_ | awk -v VAR='some "text"' '{ gsub(/_TMP_/, "VAR") ; print }'
VAR

Khi không VARđược trích dẫn, awkcoi nó như một biến có giá trị some "text". Khi VARở bên trong dấu ngoặc kép, awk coi nó như một chuỗi ba ký tự.

XEM THÊM: bash có vấn đề vệ sinh. Xem xét:

$ VAR="rm important_file" ; $VAR

Ở trên sẽ xóa important_file. Theo cách này, bashgiống như một ngôn ngữ vĩ mô: nó sẽ thay thế cho một biến và sau đó cố gắng thực hiện kết quả. awkkhác. Xem xét:

$ echo _TMP_ | awk -v VAR='var); print $1' '{ gsub(/_TMP_/, VAR) ; print }'
var); print $1

awkđối xử VARnhư văn bản đơn thuần, không giống như các lệnh tiềm năng để thực thi.

Vấn đề có thể phát sinh, tuy nhiên, nếu một người cho phép bashsửa đổi awktập lệnh. Trong các ví dụ của tôi ở trên, các awktập lệnh đều nằm trong dấu ngoặc đơn. Điều đó ngăn cản việc bashgây rối với họ.


1
VAR='blah; echo $1'cũng không phải là vấn đề đối với shell (trừ khi bạn sử dụng eval). Đây không phải là ngôn ngữ vĩ mô (ngoại trừ một số mở rộng bí danh wrt)
Stéphane Chazelas

4

(OK, xin lỗi, tôi đã đọc câu hỏi của bạn quá nhanh, vì vậy một số câu trả lời của tôi có một chút bên cạnh vấn đề, vẫn để nó vì nó có thể hữu ích cho bạn hoặc một số người)

Có một số điều cần xem xét ở đây.

trích dẫn các biến shell

Để lại một biến không được trích dẫn trong các vỏ POSIX (trong ngữ cảnh danh sách, như trong các đối số cho một lệnh), không awk, là toán tử split + global.

Nếu bạn làm:

cmd foo=$var

Trong trường hợp $var* *.

Không yêu cầu shell phân chia nội dung $vardựa trên giá trị của $IFSbiến shell đặc biệt, theo mặc định trên các khoảng trống. Vì vậy, ở trên, điều đó mang lại cho chúng ta foo=**thực hiện toàn cầu trên mỗi cái, được mở rộng foo=*ra tất cả các tên tệp trong thư mục hiện tại bắt đầu foo=*cho tất cả các tên tệp không bị ẩn.

Vì vậy, thực sự, bạn hầu như luôn luôn trích dẫn các biến shell của mình , cho dù chúng có phải là đối số awkhay không. Điều đó cũng áp dụng cho thay thế lệnh shell ( `...`$(...)) và mở rộng số học shell ( $((...))).

truyền dữ liệu nguyên trạng awk

Vấn đề khác là awk(không phải shell) mở rộng các chuỗi thoát dấu gạch chéo ngược trong các phép gán của các biến như -v var=value (và với GNU awk4.2 trở lên, nếu giá trị bắt đầu bằng @/và kết thúc /, nó được coi là một loại biến regrec ).

Chẳng hạn, -v var='\n/\n/'đặt nội dung của awk varbiến thành <newline>/<newline>/, không \n/\n/. Điều đó cũng áp dụng cho awkcác biến được định nghĩa là:

awk '...' var=value

Để truyền dữ liệu đến awkmà không trải qua quá trình mở rộng đó, bạn có thể sử dụng các mảng ENVIRONhoặc ARGVawk:

var=$value awk 'BEGIN {var=ENVIRON["var"]} ...'

.

hoặc là:

awk 'BEGIN {var=ARGV[1]; delete ARGV[1]} ...' "$value"

trích dẫn và awkcác biến

Sự phân chia + toàn cầu đó chỉ là một tính năng shell (mis-). Các awkngôn ngữ là một ngôn ngữ hoàn toàn khác nhau.

Trong awk, các biến được tham chiếu đến a varname, không $varnamevà dấu ngoặc kép được sử dụng để giới thiệu chuỗi. Vậy "varname"varnamechuỗi, trong khi varnameđề cập đến biến.

vệ sinh các biến để tránh tiêm mã

Nói một cách chính xác, trích dẫn các biến shell không vệ sinh, nó không trích dẫn các biến đang sử dụng toán tử split + global. Mặc dù trong hầu hết các ngôn ngữ bạn đặt dấu ngoặc kép quanh các chuỗi cố định, trong shell, nó lại theo cách khác: mọi thứ đều là chuỗi và dấu ngoặc kép được sử dụng để ngăn chặn một số hành vi đặc biệt và đặc biệt là các biến nên luôn luôn được trích dẫn (một quyết định thiết kế kém loại đó có ý nghĩa trong lớp vỏ Bourne vào những năm 70, nhưng là một trở ngại trong lớp vỏ hiện đại, zshlà lớp vỏ duy nhất cố định một phần điều đó).

Shell hoặc awk sẽ không đánh giá / giải thích mã được lưu trữ trong biến của riêng họ trừ khi bạn bảo họ.

var='foo; rm -f var'
echo $var
# or
echo "$var"

Sẽ không làm cho nội dung của biến được đánh giá là mã shell (mặc dù mã đầu tiên sẽ trải qua quá trình phân tách và tạo khối có thể gây hậu quả nghiêm trọng (ví dụ với var='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*'). Bạn cần:

eval "echo $var"
# or
sh -c "echo $var"

cho nó được đánh giá / giải thích như mã shell.

awkkhông có evaltính năng như vậy . perl/ pythonlàm.

Nhưng hãy cẩn thận của ô nhiễm chéo. Bạn có thể có shell pass dữ liệu biến (trong biến shell ) dưới dạng mã để thực thi bởi awk:

awk '{print "'"$var"': " $0}'

sẽ nguy hiểm trong trường hợp biến $var shell chứa ví dụ:

var='test"; print "foo" > /etc/passwd; print "blah'

bởi vì shell sẽ thực thi:

["awk", "{print \"test\"; print \"foo\" > /etc/passwd; print \"blah: \" $0}"]

Hoặc đường vòng khác:

awk '{system("echo foo: " $0)}' < file

nơi awksẽ chạy một vỏ như:

["sh", "-c", "echo foo: content-of-the-line"]

cho mỗi dòng file(và nghĩ về những gì một dòng ; rm -rf /sẽ làm).

Nó không chỉ giữa awksh. Bạn phải cẩn thận bất cứ khi nào dữ liệu biến / không kiểm soát có thể được đánh giá là mã bởi một thông dịch viên khác. Ví dụ là:

sed "s/$regexp/blah/g"

sedNgôn ngữ của nó bị hạn chế nhưng nó vẫn có thể gây hại, như với regexp='//;w /etc/passwd; s/'.

Hoặc là:

find . -exec sh -c "echo {}" \;

Bây giờ, để tránh những vấn đề đó, có hai cách tiếp cận chung:

  1. chuyển đổi biến từ một thông dịch viên khác. Nó hoạt động cho shell -> awk hoặc find -> sh case ở trên. Thích thay đổi:

    awk '{print "'"$var"': " $0}'

    đến:

    awk -v awk_var="$var" '{print awk_var ": " $0}'

    Và:

    find . -exec sh -c "echo {}" \;

    đến:

    find . -exec sh -c 'echo "$1"' sh {} \;

    nhưng điều đó sẽ không làm việc cho vỏ -> sed, hoặc awk -> vỏ.

  2. khi 1 không thể, bạn cần vệ sinh các biến để loại bỏ hoặc thoát khỏi các ký tự có thể là một vấn đề. Trong,

    awk '{system("echo foo: " $0)}'

    bạn cần chuyển đổi $0thành một chuỗi rõ ràng khi có liên quan đến trình bao. Một tùy chọn là tiền tố mỗi ký tự có dấu gạch chéo ngược nhưng sẽ không hoạt động cho dòng mới (không phải là vấn đề ở đây). Một cách khác là gửi chuỗi trong dấu ngoặc đơn và thoát từng trích dẫn.

    awk 'function escape(s) {
           gsub(/'\''/,"&\\\\&&",s)
           return "'\''" s "'\''"
         }
         {system("echo foo: " escape($0))}'

Cảm ơn, đó là thông tin tuyệt vời. Tôi vẫn còn một chút bối rối về "sự an toàn" của việc chuyển những thứ xung quanh như thế. Trong trường hợp của tôi, tôi muốn nó mở rộng nhưng tôi không muốn nó tàn phá. Với mục đích thảo luận, hãy nói rằng giá trị của biến shell $SOURCEIPrm -fr /. Nếu tôi chuyển nó để awk qua awk -v AWKVAREXAMPLE="$SOURCEIP"và sau đó có awk làm một gsub như thế gsub(/^_TARGETSTRING_/, AWKVAREXAMPLE);cuối cùng sẽ "rò rỉ" ra khỏi vỏ và phá hủy mọi thứ?
Mike B

1
@MikeB, không. Nó sẽ bị rò rỉ ra ngoài vỏ nếu awkgọi một vỏ và thông qua đó như là một mã cho nó để giải thích như thế nào trong: awk '{system("echo " var)}'(nơi var;rm -rf /), nơi mà awkcác cuộc gọi ["sh", "-c", "echo; rm -rf /"]hoặc awk '{print | "tr " v1 " " v2}'nơi awkđường ống đầu ra để ["sh", "-c", "tr content-of-v1 content-of-v2"].
Stéphane Chazelas 6/214

1
Những điều bạn muốn tránh là như: awk "{print \"$shell_variables\"}"vì ở đó, nội dung của biến shell được hiểu là mã awk.
Stéphane Chazelas 6/214

0

Nếu bạn đang chuyển một biến Awk cho hệ thống , bạn cần trích dẫn nó:

function quote(str,   d, m, x, y, z) {
  d = "\47"; m = split(str, x, d)
  for (y in x) z = z d x[y] d (y < m ? "\\" d : "")
  return z
}

Thí dụ:

system(sprintf("ffmpeg -i %s outfile.m4a", quote(ARGV[1])))

Nguồn

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.