2009-07-11 10 views
6

Có bất kỳ khai thác bảo mật có thể xảy ra trong trường hợp này:an toàn của Python 'eval' Đối với Danh sách Deserialization

eval(repr(unsanitized_user_input), {"__builtins__": None}, {"True":True, "False":False}) 

nơi unsanitized_user_input là một đối tượng str. Chuỗi là do người dùng tạo và có thể khó chịu. Giả sử khuôn khổ web của chúng tôi đã không làm chúng tôi thất bại, đó là một ví dụ thực sự trung thực-đến-thần từ các nội trang Python.

Nếu điều này là nguy hiểm, chúng ta có thể làm bất cứ điều gì với đầu vào để đảm bảo an toàn không?

Chúng tôi chắc chắn không muốn thực thi bất kỳ thứ gì có trong chuỗi.

Xem thêm:

Bối cảnh lớn hơn đó là (Tôi tin) không cần thiết cho câu hỏi là chúng ta có hàng ngàn những:

repr([unsanitized_user_input_1, 
     unsanitized_user_input_2, 
     unsanitized_user_input_3, 
     unsanitized_user_input_4, 
     ...]) 

trong một số trường hợp lồng nhau:

repr([[unsanitized_user_input_1, 
     unsanitized_user_input_2], 
     [unsanitized_user_input_3, 
     unsanitized_user_input_4], 
     ...]) 

mà bản thân họ chuyển đổi sang chuỗi với repr(), đưa vào lưu trữ liên tục, và cuối cùng đọc lại vào bộ nhớ với eval.

Đánh giá các chuỗi được lưu trữ từ lưu trữ liên tục nhanh hơn nhiều so với dưa chuột và đơn giản. Trình thông dịch là Python 2.5 nên json và ast không có sẵn. Không cho phép mô-đun C và cPickle không được phép.

+0

"Lý do để thực hiện việc này sẽ có ý nghĩa hơn nhiều nếu tôi trình bày ngữ cảnh lớn hơn" Bạn có thể giải thích về vấn đề này không? Hiện tại lệnh này dường như hoàn toàn vô nghĩa - giống như không làm gì với 'unsanitized_user_input' .. – dbr

+0

" chúng ta có hàng ngàn cái này "Điều đó không có ý nghĩa gì cả. Tại sao bạn lưu trữ đầu vào theo cách đó? Không có điểm nào trong repr() để nhập chuỗi cho mục đích lưu trữ. – Miles

+0

Tại sao bạn không sử dụng dưa chua hoặc thứ gì đó đơn giản hơn? –

Trả lời

19

Thực sự nguy hiểm và cách thay thế an toàn nhất là ast.literal_eval (xem mô-đun ast trong thư viện chuẩn). Tất nhiên, bạn có thể xây dựng và thay đổi một số ast để cung cấp ví dụ: đánh giá các biến và tương tự trước khi bạn đánh giá kết quả AST (khi nó xuống đến chữ).

Các thể khai thác của eval bắt đầu với bất kỳ đối tượng nó có thể có được bàn tay của nó trên (nói True đây) và đi qua .__ class_ để loại đối tượng của nó, vv lên đến object, sau đó được lớp con của nó ... về cơ bản nó có thể nhận được bất kỳ loại đối tượng và phá hoại xác tàu. Tôi có thể cụ thể hơn nhưng tôi không muốn làm điều đó trong một diễn đàn công cộng (khai thác nổi tiếng, nhưng xem xét có bao nhiêu người vẫn phớt lờ nó, tiết lộ nó với những người thiếu nhi kịch bản có thể làm mọi việc tồi tệ hơn ... chỉ cần tránh eval trên đầu vào người dùng không an toàn và sống hạnh phúc mãi mãi! -).

3

Nói chung, bạn không bao giờ được phép bất kỳ ai để đăng mã.

Vì vậy, được gọi là "lập trình viên trả tiền chuyên nghiệp" có một thời gian đủ cứng viết mã thực sự hoạt động.

Chấp nhận mã từ công chúng ẩn danh - không có lợi ích của QA chính thức - là điều tồi tệ nhất trong tất cả các trường hợp có thể xảy ra.

Người lập trình chuyên nghiệp - không có QA chính thức tốt, vững chắc - sẽ tạo một băm của hầu hết mọi trang web.Thật vậy, tôi đảo ngược kỹ thuật một số mã không thể tin được xấu từ các chuyên gia trả tiền.

Ý tưởng cho phép không chuyên nghiệp - không bị cản trở bởi QA - để đăng mã thực sự đáng sợ.

8

Nếu bạn có thể chứng minh ngoài nghi ngờ rằng unsanitized_user_input là một phiên bản str từ trình xây dựng Python không có gì bị giả mạo, thì điều này luôn an toàn. Trong thực tế, nó sẽ an toàn ngay cả khi không có tất cả các đối số thừa kể từ eval(repr(astr)) = astr cho tất cả các đối tượng chuỗi như vậy. Bạn đặt vào một chuỗi, bạn lấy lại một chuỗi. Tất cả những gì bạn đã làm là trốn thoát và bỏ qua nó.

Tất cả điều này khiến tôi nghĩ rằng eval(repr(x)) không phải là điều bạn muốn - không có mã nào được thực thi trừ khi ai đó cung cấp cho bạn một đối tượng unsanitized_user_input trông giống như một chuỗi nhưng không phải vậy, nhưng đó là một câu hỏi khác-- trừ khi bạn đang cố sao chép một cá thể chuỗi theo cách chậm nhất có thể: D.

+1

Đó là chính xác; Tôi chắc chắn không muốn bất cứ điều gì trong chuỗi được thực hiện. Lý do để làm điều này sẽ có ý nghĩa hơn nhiều nếu tôi trình bày ngữ cảnh lớn hơn nhưng tôi đã cố gắng đơn giản hóa kịch bản cho câu hỏi. – gravitation

4

Với tất cả mọi thứ như bạn mô tả, đó là về mặt kỹ thuật an toàn để eval chuỗi repred, tuy nhiên, tôi muốn tránh làm nó anyway như nó chuốc lấy phiền:

  • Có thể có một số góc hợp cụ thể lạ nơi giả định của bạn rằng chỉ có các chuỗi bị hủy bỏ được lưu trữ (ví dụ: một lỗi/đường dẫn khác nhau vào bộ nhớ không repr ngay lập tức vì việc khai thác mã lỗi khai thác ở nơi nó có thể không thể khai thác được)

  • Thậm chí nếu mọi thứ đều ổn, giả định có thể thay đổi tại một số điểm và dữ liệu không được bảo mật có thể được lưu trữ trong trường đó bởi một người nào đó đồ của mã eval.

  • Mã của bạn có thể bị sử dụng lại (hoặc tệ hơn, sao chép + dán) vào trường hợp bạn không cân nhắc.

Như Alex Martelli chỉ ra, trong python2.6 và cao hơn, có ast.literal_eval mà sẽ xử lý một cách an toàn cả hai dây và kiểu dữ liệu đơn giản khác như tuples. Đây có lẽ là giải pháp an toàn nhất và đầy đủ nhất.

Một khả năng khác là sử dụng codec string-escape. Đây là nhanh hơn nhiều so eval (khoảng 10 lần theo timeit), có sẵn trong các phiên bản sớm hơn literal_eval, và nên làm những gì bạn muốn:

>>> s = 'he\nllo\' wo"rld\0\x03\r\n\tabc' 
>>> repr(s)[1:-1].decode('string-escape') == s 
True 

(Các [1: -1] là dải có dấu ngoặc kép bên ngoài repr thêm.)

1
repr([unsanitized_user_input_1, 
     unsanitized_user_input_2, 
     ... 

... unsanitized_user_input là một str đối tượng

Bạn không cần phải serialise chuỗi để lưu trữ chúng trong một da tabase ..

Nếu đây là tất cả các chuỗi, như bạn đã đề cập - tại sao bạn không thể lưu trữ các chuỗi trong một db.StringListProperty?

Các mục nhập lồng nhau có thể phức tạp hơn một chút, nhưng tại sao lại là trường hợp này? Khi bạn phải sử dụng eval để lấy dữ liệu từ cơ sở dữ liệu, có thể bạn đang làm sai điều gì đó ..

Bạn không thể lưu trữ mỗi số unsanitized_user_input_x vì nó là một hàng tham chiếu?

Một trong số đó có thể không áp dụng được, vì tôi không biết bạn đang cố gắng đạt được điều gì, nhưng quan điểm của tôi là - bạn không thể cấu trúc dữ liệu theo cách bạn không phải dựa vào eval (và cũng dựa vào nó không phải là một vấn đề an ninh)?

+0

Một lý do là các chuỗi cần phải được nén để vừa với giới hạn thực thể 1MB của App Engine và tôi nghĩ chi phí của việc nén 1000 chuỗi riêng lẻ có thể cao hơn nhiều so với tuần tự hóa chúng, nén chúng lại với nhau và đặt chúng vào blob. Tiết kiệm không gian có lẽ sẽ ít hơn. Nhưng đó là một điểm tốt ... Tôi chắc chắn đang tìm cách tránh eval mà không làm tăng chi phí vận hành quá nhiều. – gravitation