Hãy sử dụng \A và \z (chứ không phải là ^ và $) khi validate bằng regex.

Hãy sử dụng \A và \z (chứ không phải là ^ và $) khi validate bằng regex.

Khi validate bằng regex, thông thường chúng ta hay sử dụng ^ và $ để kiểm tra Exact match. Tuy nhiên, phải sử dụng \A và \z thì mới đúng. Đối với Ruby thì việc sử dụng ^ và $ để kiểm tra Exact match là một cái bẫy tiềm tàng về lỗ hổng bảo mật. Còn đối với Perl hay PHP thì không giống như Ruby nhưng có khả năng sẽ phát sinh bug nên tốt hơn hết là hãy sử dụng \A và \z.

Mở đầu

Trong bài viết "Cạm bẫy Ruby/Rails đối với PHPer" trên trang Blog của Okagi cũng trích dẫn từ Ruby on Rails Security Guide những ví dụ cụ thể về việc sử dụng regex ^ và $ để kiểm tra Exact match. Các biểu thức chính quy (regex) sau đây là những biểu thức chính quy chỉ cho phép sử dụng URL https hoặc http.

/^https? : \/\/[^\n]+$/i

Tuy nhiên, đối với trường hợp Ruby thì input đầu vào bên dưới cũng sẽ match với biểu thức chính quy bên trên.

javascript: exploit_code();/*

http: //hi.com

*/

Đây chạ phải là một cái bẫy quá rõ ràng sao? Vậy, tại sao lại phát sinh điều này? Có 2 lí do như sau:

  • ^ và $ là ký tự mở đầu và kết thúc một HÀNG, không phải một chuỗi
  • Chức năng biểu thức chính quy của Ruby mặc định là chế độ multi-line

Ý nghĩa của ^ và $

Không chỉ Ruby mà cả Perl và PHP cũng vậy, ^ và $ là ký tự mở đầu và kết thúc một hàng. Trường hợp chỉ định ở đầu và cuối một chuỗi thì sẽ sử dụng \A và \z.

Chế độ mặc định multi-line

Đây là một đặc điểm riêng của Ruby. Tuy nhiên, các biểu thức chính quy mặc định sẽ hoạt động theo cách chỉ định m modifier của Perl hoặc PHP. Còn đối với Perl hoặc PHP thì dù có xuống dòng giữa chừng của một chuỗi thì cũng vẫn chỉ coi toàn bộ chuỗi đó là một hàng mà thôi. Mặc khác, trường hợp đối với Ruby hay là trường hợp đã chỉ định m modifier trong biểu thức chính quy của Perl hay PHP thì đều coi xuống dòng là cách thức phân chia các hàng với nhau và coi đó là nhiều hàng khác nhau. Chính vì lẽ đó mà các biểu thức chính quy kiểu /^xxx$/ (hoặc /^xxx$/m đối với Perl hay PHP) sẽ "khớp trong trường hợp có hàng match với xxx.

Thêm nữa, ngay cả trong Ruby cũng có chỉ định m modifier. Cái này tương ứng với chỉ định s trong Perl hay PHP nên dấu "." sẽ là chỉ dẫn để khớp với việc xuống hàng.

Đối với trường hợp là PoC ở đầu thì do có http URL là http://hi.com ở hàng thứ 2 nên regex sẽ tiến hành khớp và coi đó là một input đúng.

Lỗ hổng tiềm tàng

  • SQL injection: Sau khi thực hiện kiểm tra tính hợp lệ của các giá trị số bằng biểu thức chính quy thì sẽ đến input kiểu như 1\nOR 1=1
  • Mail header injection: Sau khi kiểm tra tính hợp lệ của Mail address bằng biểu thức chính quy thì sẽ đến input kiểu như [email protected]\nSubject: hoge
  • HTTP header injection: Sau khi kiểm tra destination URL bằng biểu thức chính quy thì sẽ đến input kiểu như http://example.jp/\nSet-Cookie: SESSIONID=ABC

Vậy nên xử lý như thế nào đối với Ruby

Nếu sử dụng biểu thức chính quy để validate chuỗi thì không nên sử dụng ^ và $ mà nên sử dụng \A và \z ở đầu và cuối chuỗi. Thường thì những người dùng Ruby thường dùng ^ và $ nhưng trong blog của Okagi cũng chỉ rõ là nếu dùng như thế thì đó sẽ là một cãi bẫy khi bạn chuyển từ một ngôn ngữ khác sang ngôn ngữ Ruby.

Nên tránh dùng ^ và $ đối với cả Perl và PHP

Blog của Okagi cũng viết rõ pre_*()mb_regex_*() của PHP là mở đầu và kết thúc của một chuỗi và tương ứng với đó là ^ và $". Tuy nhiên, nói một cách chính xác thì ^ và $ trong PHP là mở đầu và kết thúc của một hàng. Không chỉ Okagi mà đại đa số mọi người đều sử dụng ^ và $ trong việc kiểm tra theo biểu thức chính quy để chỉ định khớp chính xác. Nhưng, cách làm đó thực sự không đúng.

Tuy nhiên, không phải là tất cả những script trước đây của PHP hay Perl đều tiềm tàng lỗ hổng bảo mật. Điều này là do theo mặc định thì các regex của PHP và Perl đều theo chế độ single-line (hàng đơn) và ^ và $ không phải match trước sau khi xuống dòng giữa chuỗi.

Tuy nhiên, ngay cả khi xuống dòng ở cuối hàng thì $ cũng sẽ bị khớp (ngay trước chỗ xuống dòng). Nói cách khác, PHP script bên dưới trả về 1 (đã match).

preg_match('/^[0-9]+$/', "123\n")

Còn bên dưới thì trả về 0 (not match).

preg_match('/\A[0-9]+\z/', "1234\n")

Theo đó, nếu muốn khớp chính xác bằng ^ và $ thì sẽ bị sót trường hợp mà có xuống dòng ở cuối chuỗi dữ liệu.

Tài liệu

Hiện không có nhiều tài liệu về Ruby nhưng nếu bạn nào lần đầu tiếp cận với Ruby thì đều được giải thích rõ ràng và chính xác về ý nghĩa của ^ $ \A \z .

Những tài liệu về PHP gần như bị xóa sổ. Tuy nhiên, bạn có thể có được những định nghĩa chính xác trong ấn bản lần thứ 2 của tài liệu "PROGRAMMER’S RECiPE".

Lời khuyên: Nên tìm đọc ấn bản thứ 2.

Tài liệu “Nhập môn lập trình PHP_Con đường đến chuyên nghiệp” cũng là tài liệu mô tả chính xác mọi thứ. Còn trong tài liệu "徹底攻略PHP5技術者認定[上級]試験問題集[PJ0-200]対応" thì giải thích tương đối khó hiểu (do chỉ dành cho cấp độ Cao cấp).

Tóm lại, những bạn nào đã đang và mong muốn tạo ra những ứng dụng WEB an toàn thì cần cân nhắc những vấn đề, nguyên nhân và lí do sinh ra các lỗ hổng bảo mật, từ đó đưa ra cách giải quyết.

Kết luận

Khi validate dựa vào biểu thức chính quy (regex), thông thường chúng ta hay sử dụng ^ và $ để kiểm tra Exact match. Tuy nhiên, điều đó là không đúng. Chúng ta nên sử dụng \A và \z. Việc sử dụng ^ và $, nếu là ở Ruby thì chương trình có lỗ hổng bảo mật, dễ bị hack. Còn đối với các ngôn ngữ khác thì phát sinh vấn đề là không thể check được xuống dòng ở cuối chuỗi dữ liệu.


All Rights Reserved