Có một số điều 'gọn gàng' có thể được thực hiện bằng các ngôn ngữ động có thể được giấu trong các phần của mã không rõ ràng ngay lập tức đối với một lập trình viên hoặc kiểm toán viên khác về chức năng của một đoạn mã nhất định.
Xem xét chuỗi này trong irb (vỏ ruby tương tác):
irb(main):001:0> "bar".foo
NoMethodError: undefined method `foo' for "bar":String
from (irb):1
from /usr/bin/irb:12:in `<main>'
irb(main):002:0> class String
irb(main):003:1> def foo
irb(main):004:2> "foobar!"
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> "bar".foo
=> "foobar!"
Điều gì đã xảy ra là tôi đã cố gắng gọi phương thức foo
trong hằng chuỗi. Điều này thất bại. Sau đó tôi đã mở lớp String và định nghĩa phương thức foo
o return "foobar!"
, và sau đó gọi nó. Điều này đã làm việc.
Đây được biết đến như một lớp học mở và mang đến cho tôi những cơn ác mộng mỗi khi tôi nghĩ về việc viết mã bằng ruby có bất kỳ loại bảo mật hoặc tính toàn vẹn nào. Chắc chắn rằng nó cho phép bạn thực hiện một số thứ gọn gàng khá nhanh ... nhưng tôi có thể làm nó để mỗi khi ai đó lưu trữ một chuỗi, nó sẽ lưu nó vào một tệp hoặc gửi nó qua mạng. Và một chút định nghĩa lại Chuỗi này có thể được giấu ở bất cứ đâu trong mã.
Nhiều ngôn ngữ động khác có những điều tương tự có thể được thực hiện. Perl có Tie :: Scalar có thể thay đổi cách hoạt động của một vô hướng cụ thể (điều này rõ ràng hơn một chút và yêu cầu một lệnh cụ thể mà bạn có thể nhìn thấy, nhưng một vô hướng được truyền từ một nơi khác có thể là một vấn đề). Nếu bạn có quyền truy cập vào Perl Cookbook, hãy tìm Recipe 13.15 - Tạo các biến ma thuật bằng cà vạt.
Do những điều này (và những thứ khác thường là một phần của ngôn ngữ động), nhiều cách tiếp cận để phân tích tĩnh về bảo mật trong mã không hoạt động. Perl và Undecidability cho thấy đây là trường hợp và chỉ ra ngay cả những vấn đề tầm thường như vậy với việc làm nổi bật cú pháp ( whatever / 25 ; # / ; die "this dies!";
đặt ra thách thức bởi vì whatever
có thể được định nghĩa để lấy đối số hoặc không trong thời gian chạy đánh bại hoàn toàn bộ phân tích cú pháp hoặc bộ phân tích tĩnh).
Điều này thậm chí còn thú vị hơn với Ruby với khả năng truy cập vào môi trường mà việc đóng cửa được xác định trong (xem YouTube: Giữ Ruby Reasonable từ RubyConf 2011 của Joshua Ballanco). Tôi đã nhận ra video này từ một bình luận Ars Technica của MouseTheLuckyDog .
Hãy xem xét các mã sau đây:
def mal(&block)
puts ">:)"
block.call
t = block.binding.eval('(self.methods - Object.methods).sample')
block.binding.eval <<-END
def #{t.to_s}
raise 'MWHWAHAW!'
end
END
end
class Foo
def bar
puts "bar"
end
def qux
mal do
puts "qux"
end
end
end
f = Foo.new
f.bar
f.qux
f.bar
f.qux
Mã này hoàn toàn có thể nhìn thấy, nhưng mal
phương thức có thể ở một nơi khác ... và với các lớp mở, tất nhiên, nó có thể được định nghĩa lại ở một nơi khác.
Chạy mã này:
~ / $ ruby foo.rb
quán ba
> :)
qux
quán ba
b.rb: 20: trong `qux ': MWHWAHAW! (Lỗi runtime)
từ b.rb: 30: trong `'
~ / $ ruby foo.rb
quán ba
> :)
qux
b.rb: 20: trong 'thanh': MWHWAHAW! (Lỗi runtime)
từ b.rb: 29: trong `'
Trong mã này, bao đóng có thể truy cập tất cả các phương thức và các ràng buộc khác được định nghĩa trong lớp ở phạm vi đó. Nó chọn một phương thức ngẫu nhiên và xác định lại nó để đưa ra một ngoại lệ. (xem lớp Binding trong Ruby để có ý tưởng về những gì đối tượng này có quyền truy cập)
Các biến, phương thức, giá trị của bản thân và có thể là một khối lặp có thể được truy cập trong ngữ cảnh này đều được giữ lại.
Một phiên bản ngắn hơn cho thấy định nghĩa lại của một biến:
def mal(&block)
block.call
block.binding.eval('a = 43')
end
a = 42
puts a
mal do
puts 1
end
puts a
Mà, khi chạy sản xuất:
42
1
43
Điều này còn hơn cả lớp mở mà tôi đã đề cập ở trên khiến cho việc phân tích tĩnh không thể thực hiện được. Điều được chứng minh ở trên là một bao đóng được chuyển qua một nơi khác, mang trong nó toàn bộ môi trường mà nó được định nghĩa. Đây được gọi là môi trường hạng nhất (giống như khi bạn có thể chuyển qua các hàm, chúng là các hàm hạng nhất, đây là môi trường và tất cả các ràng buộc có sẵn tại thời điểm đó). Người ta có thể định nghĩa lại bất kỳ biến nào được xác định trong phạm vi đóng.
Tốt hay xấu, phàn nàn về ruby hay không (có những cách sử dụng mà người ta muốn có thể có được ở môi trường của một phương thức (xem Safe in Perl)), câu hỏi "tại sao ruby sẽ bị hạn chế trong một dự án của chính phủ "Thực sự được trả lời trong video đó được liên kết ở trên.
Cho rằng:
- Ruby cho phép một người trích xuất môi trường từ bất kỳ đóng cửa
- Ruby nắm bắt tất cả các ràng buộc trong phạm vi đóng cửa
- Ruby duy trì tất cả các ràng buộc như sống và biến đổi
- Ruby có các ràng buộc mới phủ bóng các ràng buộc cũ (thay vì nhân bản môi trường hoặc cấm rebinding)
Với ý nghĩa của bốn lựa chọn thiết kế này, không thể biết bất kỳ bit mã nào làm gì.
Thông tin thêm về điều này có thể được đọc tại blog Tóm tắt Heresies . Bài đăng cụ thể là về Đề án nơi đã có một cuộc tranh luận như vậy. (liên quan đến SO: Tại sao Scheme không hỗ trợ môi trường hạng nhất? )
Tuy nhiên, theo thời gian, tôi nhận ra rằng có nhiều khó khăn hơn và ít sức mạnh hơn với môi trường hạng nhất so với tôi nghĩ ban đầu. Tại thời điểm này tôi tin rằng môi trường hạng nhất là vô dụng và nguy hiểm nhất là tồi tệ nhất.
Tôi hy vọng phần này cho thấy khía cạnh nguy hiểm của môi trường hạng nhất và lý do tại sao nó sẽ được yêu cầu loại bỏ Ruby khỏi giải pháp được cung cấp. Ruby không chỉ là một ngôn ngữ động (như đã trả lời khác, các ngôn ngữ động khác đã được cho phép trong các dự án khác), nhưng có những vấn đề cụ thể khiến một số ngôn ngữ động thậm chí còn khó giải thích hơn.