2011-12-31 20 views
7

Tôi đang viết một chương trình kiểu wiki cá nhân trong python lưu trữ các tệp văn bản trong một thư mục có thể cấu hình người dùng.Xác nhận tên tệp trong python

Chương trình sẽ có thể lấy chuỗi (ví dụ: "foo") từ người dùng và tạo tên tệp của foo.txt. Người dùng sẽ chỉ có thể tạo tệp bên trong thư mục wiki và các dấu gạch chéo sẽ tạo một thư mục con (ví dụ: "foo/bar" trở thành "(path-to-wiki) /foo/bar.txt").

Cách tốt nhất để kiểm tra xem đầu vào có an toàn nhất có thể không? Tôi cần phải chú ý điều gì (thư mục traversal - "../"?, Null bytes - "\ 0"?).

Tôi nhận thấy rằng việc nhập người dùng cho tên tệp không bao giờ an toàn 100%, nhưng chương trình sẽ chỉ chạy cục bộ và tôi chỉ muốn bảo vệ mọi lỗi/lỗi phổ biến.

+1

mục tiêu gì? Phiên bản nào của python? –

+0

@ IgnacioVazquez-Abrams: có, nhưng các tệp văn bản thuần túy trong hệ thống tệp có các lợi ích khác. – Puzzled79

+0

@ g.d.d.c: Python 2.7 và/hoặc 3.2 và MacOS/Linux chủ yếu. – Puzzled79

Trả lời

8

Bạn có thể thực thi cho người sử dụng để tạo ra một tập tin/thư mục bên trong wiki bằng cách bình thường con đường với os.path.normpath và sau đó kiểm tra nếu đường dẫn bắt đầu với tiếng nói '(path-to-wiki)'

os.path.normpath('(path-to-wiki)/foo/bar.txt').startswith('(path-to-wiki)') 

Để đảm bảo rằng đường dẫn/tên tệp đã nhập của người dùng không chứa bất kỳ điều gì khó chịu, bạn có thể buộc người dùng nhập đường dẫn hoặc tên tệp vào một trong hai chữ số dưới/dưới, chữ số hoặc có thể là gạch nối hoặc dấu gạch dưới.

Sau đó, bạn luôn có thể kiểm tra tên tập tin bình thường sử dụng một biểu thức chính quy tương tự

userpath=os.path.normpath('(path-to-wiki)/foo/bar.txt') 
re.findall(r'[^A-Za-z0-9_\-\\]',userpath) 

Để tóm tắt

nếu userpath=os.path.normpath('(path-to-wiki)/foo/bar.txt') sau đó

if not os.path.normpath('(path-to-wiki)/foo/bar.txt').startswith('(path-to-wiki)') 
    or re.search(r'[^A-Za-z0-9_\-\\]',userpath): 
    ... Do what ever you want with an invalid path 
0

Bạn chỉ có thể xác nhận tất cả các nhân vật đều có thể in chữ và số ascii ngoại trừ các ký tự '', '.' và '/', sau đó chỉ xóa tất cả các trường hợp kết hợp không hợp lệ ...

safe_string = str() 
for c in user_supplied_string: 
    if c.isalnum() or c in [' ','.','/']: 
     safe_string = safe_string + c 

while safe_string.count("../"): 
    # I use a loop because only replacing once would 
    # leave a hole in that a bad guy could enter ".../" 
    # which would be replaced to "../" so the loop 
    # prevents tricks like this! 
    safe_string = safe_string.replace("../","./") 
# Get rid of leading "./" combinations... 
safe_string = safe_string.lstrip("./") 

Đó là những gì tôi sẽ làm, tôi không biết làm thế nào pythonic nó là nhưng nó nên để bạn khá an toàn. Nếu bạn muốn để xác nhận và không chuyển đổi sau đó bạn chỉ có thể làm một bài kiểm tra cho bình đẳng sau đó như vậy:

valid = save_string == user_supplied_string 
if not valid: 
    raise Exception("Sorry the string %s contains invalid characters" % user_supplied_string) 

Trong khi kết thúc cả hai phương pháp có lẽ sẽ làm việc, tôi thấy phương pháp này cảm thấy một chút rõ ràng hơn và cũng nên hiển thị bất kỳ ký tự lạ/không phù hợp nào như '\ t', '\ r' hoặc '\ n' Chúc mừng!

3

Armin Ronacher có một bài viết trên blog về các chủ đề này (và khác): http://lucumr.pocoo.org/2010/12/24/common-mistakes-as-web-developer/

Những ý tưởng này được thực hiện như safe_join() chức năng trong Flask:

hệ điều hành
def safe_join(directory, filename): 
    """Safely join `directory` and `filename`. 

    Example usage:: 

    @app.route('/wiki/<path:filename>') 
    def wiki_page(filename): 
    filename = safe_join(app.config['WIKI_FOLDER'], filename) 
    with open(filename, 'rb') as fd: 
    content = fd.read() # Read and process the file content... 

    :param directory: the base directory. 
    :param filename: the untrusted filename relative to that directory. 
    :raises: :class:`~werkzeug.exceptions.NotFound` if the resulting path 
    would fall out of `directory`. 
    """ 
    filename = posixpath.normpath(filename) 
    for sep in _os_alt_seps: 
     if sep in filename: 
      raise NotFound() 
    if os.path.isabs(filename) or filename.startswith('../'): 
     raise NotFound() 
    return os.path.join(directory, filename) 
Các vấn đề liên quan