Tại sao Ác ma lại là một vấn đề?
Bởi vì máy tính làm chính xác những gì bạn yêu cầu chúng làm, ngay cả khi nó không phải như ý bạn hoặc hoàn toàn không hợp lý. Nếu bạn yêu cầu công cụ Regex chứng minh rằng, đối với một số đầu vào nhất định, có hoặc không phù hợp với một mẫu nhất định, thì công cụ sẽ cố gắng thực hiện điều đó bất kể có bao nhiêu kết hợp khác nhau phải được kiểm tra.
Đây là một mẫu đơn giản được lấy cảm hứng từ ví dụ đầu tiên trong bài đăng của OP:
^((ab)*)+$
Cho đầu vào:
abababababababababababab
Công cụ regex thử một cái gì đó giống như (abababababababababababab)
và một kết quả phù hợp được tìm thấy trong lần thử đầu tiên.
Nhưng sau đó chúng tôi ném cờ lê khỉ vào:
abababababababababababab a
Đầu tiên động cơ sẽ thử (abababababababababababab)
nhưng không thành công vì phần phụ đó a
. Điều này gây ra bracktracking thảm khốc, bởi vì mô hình của chúng tôi (ab)*
, thể hiện thiện chí, sẽ phát hành một trong các hình ảnh chụp của nó (nó sẽ "quay ngược") và để mô hình bên ngoài thử lại. Đối với công cụ regex của chúng tôi, nó trông giống như sau:
(abababababababababababab)
- Không
(ababababababababababab)(ab)
- Không
(abababababababababab)(abab)
- Không
(abababababababababab)(ab)(ab)
- Không
(ababababababababab)(ababab)
- Không
(ababababababababab)(abab)(ab)
- Không
(ababababababababab)(ab)(abab)
- Không
(ababababababababab)(ab)(ab)(ab)
- Không
(abababababababab)(abababab)
- Không
(abababababababab)(ababab)(ab)
- Không
(abababababababab)(abab)(abab)
- Không
(abababababababab)(abab)(ab)(ab)
- Không
(abababababababab)(ab)(ababab)
- Không
(abababababababab)(ab)(abab)(ab)
- Không
(abababababababab)(ab)(ab)(abab)
- Không
(abababababababab)(ab)(ab)(ab)(ab)
- Không
(ababababababab)(ababababab)
- Không
(ababababababab)(abababab)(ab)
- Không
(ababababababab)(ababab)(abab)
- Không
(ababababababab)(ababab)(ab)(ab)
- Không
(ababababababab)(abab)(abab)(ab)
- Không
(ababababababab)(abab)(ab)(abab)
- Không
(ababababababab)(abab)(ab)(ab)(ab)
- Không
(ababababababab)(ab)(abababab)
- Không
(ababababababab)(ab)(ababab)(ab)
- Không - Không - Không - Không
(ababababababab)(ab)(abab)(abab)
- Không
(ababababababab)(ab)(abab)(ab)(ab)
- Không
(ababababababab)(ab)(ab)(ababab)
- Không
(ababababababab)(ab)(ab)(abab)(ab)
- Không
(ababababababab)(ab)(ab)(ab)(abab)
- Không
(ababababababab)(ab)(ab)(ab)(ab)(ab)
- Không
...
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abababab)
- Không
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ababab)(ab)
- Không
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(abab)
- Không
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(ab)(ab)
- Không
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ababab)
- Không
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)(ab)
- Không
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(abab)
- Không
(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)(ab)
- Không
Số lượng các kết hợp có thể có tỷ lệ theo cấp số nhân với độ dài của đầu vào và trước khi bạn biết điều đó, công cụ regex đang tiêu thụ tất cả tài nguyên hệ thống của bạn khi cố gắng giải quyết vấn đề này cho đến khi, khi sử dụng hết mọi tổ hợp thuật ngữ có thể, nó cuối cùng đã bỏ báo cáo "Không có kết quả phù hợp." Trong khi đó máy chủ của bạn đã biến thành một đống kim loại nóng chảy đang cháy.
Làm thế nào để phát hiện ra các Regexes xấu xa
Nó thực sự rất phức tạp. Tôi đã tự viết một vài câu, mặc dù tôi biết chúng là gì và nói chung là làm thế nào để tránh chúng. Xem Regex mất nhiều thời gian đáng ngạc nhiên . Gói mọi thứ bạn có thể vào một nhóm nguyên tử có thể giúp ngăn ngừa vấn đề bẻ khóa ngược. Về cơ bản, nó yêu cầu công cụ regex không truy cập lại một biểu thức đã cho - "khóa bất kỳ thứ gì bạn đã khớp trong lần thử đầu tiên". Tuy nhiên, lưu ý rằng biểu thức nguyên tử không ngăn chặn backtracking bên trong biểu thức, do đó ^(?>((ab)*)+)$
vẫn nguy hiểm, nhưng ^(?>(ab)*)+$
an toàn (nó sẽ khớp (abababababababababababab)
và sau đó từ chối từ bỏ bất kỳ ký tự nào đã khớp của nó, do đó ngăn chặn backtracking thảm khốc).
Thật không may, một khi nó được viết, thực sự rất khó để tìm ra ngay lập tức hoặc nhanh chóng một regex vấn đề. Cuối cùng, nhận ra một regex xấu cũng giống như nhận ra bất kỳ mã xấu nào khác - cần rất nhiều thời gian và kinh nghiệm và / hoặc một sự kiện thảm khốc.
Điều thú vị là, vì câu trả lời này được viết lần đầu tiên, một nhóm nghiên cứu tại Đại học Texas ở Austin đã xuất bản một bài báo mô tả sự phát triển của một công cụ có khả năng thực hiện phân tích tĩnh các Biểu thức chính quy với mục đích rõ ràng là tìm ra các mẫu "xấu" này. Công cụ này được phát triển để phân tích các chương trình Java, nhưng tôi nghi ngờ rằng trong những năm tới, chúng ta sẽ thấy nhiều công cụ hơn được phát triển xung quanh anaylsing và phát hiện các mẫu có vấn đề trong JavaScript và các ngôn ngữ khác, đặc biệt là khi tỷ lệ các cuộc tấn công ReDoS tiếp tục tăng .
Phát hiện tĩnh các lỗ hổng DoS trong các chương trình sử dụng Biểu thức chính quy
Valentin Wüstholz, Oswaldo Olivo, Marijn JH Heule và Isil Dillig
Đại học Texas tại Austin