2010-05-16 26 views
10

Tôi đang thực hiện truy vấn Doctrine và tôi phải thực hiện đối sánh ký tự đại diện trong mệnh đề where. Làm thế nào tôi nên thoát khỏi biến mà tôi muốn chèn?

Truy vấn Tôi muốn nhận được:

SELECT u.* FROM User as u WHERE name LIKE %var% 

Mã php đến nay:

$query = Doctrine_Query::create() 
       ->from('User u') 
       ->where(); 

gì nên đến trong mệnh đề ở đâu? Biến Tôi muốn để phù hợp là $ name

Trả lời

23

Không ai trả lời câu hỏi của bạn một cách chính xác, vì vậy tôi sẽ làm một đâm vào nó.

->where('u.name LIKE ?', array("%$name%")); 
->where('u.username LIKE ?', '%'.$username.'%') 

Cả hai cách này đều an toàn. Hãy để tôi giải thích một vài kịch bản.

Kịch bản 1

Hãy tưởng tượng bạn muốn cho phép người dùng tìm kiếm tên người dùng phù hợp, nhưng bạn không bao giờ muốn Liệt kê tất cả tên người dùng. Có lẽ bạn không muốn ai đó dễ dàng ăn cắp danh sách một triệu tên người dùng từ bạn. ở đâu đó trước khi mã này, bạn đã làm một cái gì đó như thế này:

if (strlen(trim($name)) < 5) throw Boogey_Monster_Exception(); 

Bạn nghĩ điều này sẽ ngăn chặn ai đó rời khỏi hiện trường trống và kéo xuống danh sách tất cả tên người dùng ... nhưng trong thực tế người dùng có thể gửi "_____ "hoặc" %%%%% "hoặc bất kỳ thứ gì tương tự để nhận danh sách tất cả tên người dùng, không chỉ khớp với 5 hoặc nhiều ký tự đã biết.

Cá nhân tôi đã thấy hình thức tấn công này được sử dụng trên một số trang web công cộng lớn.

Kịch bản 2

Bạn có một trang web với rất nhiều người sử dụng và rất nhiều dữ liệu người dùng. Bạn có 10.000.000 hàng trong bảng người dùng của mình. Bạn muốn cho phép người dùng của trang web tìm tên người dùng của người dùng khác bằng cách tìm kiếm các tiền tố đã biết.

Vì vậy, bạn viết một số mã như thế này, được sửa đổi đôi chút từ ví dụ trên để chỉ có ký tự đại diện SAU chuỗi tìm kiếm.

->where('u.name LIKE ?', array("$name%")); 

Nếu bạn có chỉ mục trên u.name thì truy vấn LIKE này sẽ sử dụng chỉ mục. Vì vậy, nếu người dùng gửi $ name = "john", thì truy vấn này sẽ đối sánh hiệu quả với người dùng như johndoe, johnwayne, johnwaynegacy, v.v.

Tuy nhiên, nếu người dùng gửi $ name = "% john" thay vào đó, truy vấn này không còn sử dụng chỉ mục và bây giờ yêu cầu quét toàn bộ bảng. Trên một cơ sở dữ liệu rất lớn, đây có thể là một truy vấn rất chậm.

Hướng dẫn sử dụng MySQL trên SQLi đề cập đến cùng một điều này (trang 78-79) và tôi googled cho một số ví dụ về hiệu suất truy vấn chậm và tìm thấy một liên kết.Điều này nghe có vẻ không phải là một vấn đề lớn, nhưng đối với các trang web được hỗ trợ bởi RDBMS, RDBMS thường là một nút cổ chai đáng kể, và phần lớn các kỹ thuật hiệu suất xoay quanh việc giảm ganh đua trên RDBMS. NẾU bạn có một số lượng người dùng khởi động một cuộc tấn công liên quan đến xử lý cơ sở dữ liệu trong 60 giây và bạn có một nhóm xử lý cơ sở dữ liệu nhỏ, bạn có thể thấy cách này có thể nhanh chóng mở rộng để xử lý tất cả các xử lý cơ sở dữ liệu của bạn và ngăn người dùng hợp pháp từ việc có thể có được một.

Liên kết

http://dev.mysql.com/tech-resources/articles/guide-to-php-security-ch3.pdf

http://forums.mysql.com/read.php?24,13397,13397

Giải pháp

Dù sao, giải pháp tốt hơn (như đã đề cập trong cuốn hướng dẫn MySQL liên kết ở trên và bởi commenter @Maxence, là để sử dụng addcslashes()):

$username = addcslashes("%something_", "%_"); 

Lưu ý rằng kể từ khi các ví dụ sql ở đây sử dụng câu lệnh chuẩn bị, hoàn toàn miễn nhiễm với tiêm sql, không cần thiết hoặc mong muốn sử dụng mysql_real_escape_string(); việc thoát nó thực hiện là duy nhất để ngăn chặn tiêm sql. Những gì chúng tôi đang cố gắng ngăn chặn là tiêm ký tự đại diện và yêu cầu một hàm thoát khỏi hai ký tự đại diện sql, '%' và '_'.

+1

Thú vị, cảm ơn. – Tom

+1

Tôi nghĩ rằng một giải pháp tốt để tránh gánh nặng cao trên DB là tránh các truy vấn LIKE và sử dụng một công cụ tìm kiếm như [sphinxs] (http://sphinxsearch.com/). Thật tuyệt vời! – JeanValjean

+0

@mehaase Tôi thấy bạn đã đưa ra -1 câu trả lời khác, nhưng phương pháp của bạn cũng không an toàn vì bạn đã quên thoát khỏi ký tự thoát. Vì vậy, ít nhất nó sẽ giống như thế này: 'addcslashes ('% something_', '\\% _');'. Hãy nhớ rằng trong MySQL '\\ một cái gì đó 'LIKE' \\ something'' đánh giá là' FALSE' nhưng ''\\ một cái gì đó' LIKE '\\\\''''''''''i''i_kiếm thành' TRUE' ;-) – Karolis

-2

Something xấu xảy ra với tài liệu hướng dẫn học thuyết của vì vậy đây là Google copy (kiểm tra Giống như Expressions phần)

... 
->where('u.name LIKE ?', array("%$name%")); 
+1

Điều gì sẽ xảy ra nếu có một ký tự đặc biệt như '%' hoặc '\ _' bằng $ name? Bạn nên thêm addcslashes ($ name, '% \ _') – Maxence

+0

'addcslashes();'? Không cần phải thêm dấu gạch chéo vào bất cứ điều gì cho các truy vấn, cho dù bạn đang sử dụng và ORM hoặc sql gốc. Doctrine chăm sóc thoát đúng cách mà không xả rác dữ liệu của bạn bằng dấu gạch chéo. Đối với mysql, sử dụng 'mysql_real_escape_string ($ string);'. Đối với những người khác, hãy kiểm tra tài liệu của bạn; chỉ cần không làm 'addcslashes();' – adlawson

+4

@adlawson: Doctrine ** sẽ không ** escapse '%' và '_' cho biểu thức' LIKE'. Điều này phải được xử lý bằng tay. – Crozin

Các vấn đề liên quan