Chèn SQL xung quanh mysql_real_escape_string ()

Apr 21 2011

Có khả năng chèn SQL ngay cả khi sử dụng mysql_real_escape_string()hàm không?

Hãy xem xét tình huống mẫu này. SQL được xây dựng bằng PHP như sau:

$login = mysql_real_escape_string(GetFromPost('login')); $password = mysql_real_escape_string(GetFromPost('password'));

$sql = "SELECT * FROM table WHERE login='$login' AND password='$password'";

Tôi đã nghe nhiều người nói với tôi rằng mã như vậy vẫn nguy hiểm và có thể bị hack ngay cả với mysql_real_escape_string()chức năng được sử dụng. Nhưng tôi không thể nghĩ ra bất kỳ khả năng khai thác nào?

Những cách tiêm cổ điển như thế này:

aaa' OR 1=1 --

đừng làm việc.

Bạn có biết về bất kỳ cách tiêm nào có thể xảy ra thông qua đoạn mã PHP ở trên không?

Trả lời

393 WesleyvanOpdorp Apr 21 2011 at 15:05

Hãy xem xét truy vấn sau:

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string()sẽ không bảo vệ bạn chống lại điều này. Thực tế là bạn sử dụng dấu nháy đơn ( ' ') xung quanh các biến bên trong truy vấn của bạn là điều bảo vệ bạn chống lại điều này. Sau đây cũng là một tùy chọn:

$iId = (int)"1 OR 1=1";
$sSql = "SELECT * FROM table WHERE id = $iId";
652 ircmaxell Aug 25 2012 at 09:08

Câu trả lời ngắn gọn là có, có, có một cách để giải quyết vấn đềmysql_real_escape_string() . # ĐỐI VỚI CÁC TRƯỜNG HỢP CẠNH TRANH RẤT ĐÁNG YÊU !!!

Câu trả lời dài không dễ dàng như vậy. Nó dựa trên một cuộc tấn công được chứng minh ở đây .

Cuộc tấn công

Vì vậy, hãy bắt đầu bằng cách thể hiện cuộc tấn công ...

mysql_query('SET NAMES gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*"); mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Trong một số trường hợp nhất định, điều đó sẽ trả về nhiều hơn 1 hàng. Hãy cùng mổ xẻ những gì đang diễn ra ở đây:

  1. Chọn một bộ ký tự

    mysql_query('SET NAMES gbk');
    

    Để cuộc tấn công này hoạt động, chúng ta cần mã hóa mà máy chủ mong đợi trên kết nối để mã hóa 'như trong ASCII tức là 0x27 có một số ký tự có byte cuối cùng là ASCII \tức là 0x5c. Khi nó quay ra, có 5 mã hóa như hỗ trợ trong MySQL 5.6 theo mặc định: big5, cp932, gb2312, gbksjis. Chúng tôi sẽ chọn gbkở đây.

    Bây giờ, điều rất quan trọng cần lưu ý là sử dụng SET NAMESở đây. Điều này đặt bộ ký tự TRÊN MÁY CHỦ . Nếu chúng tôi sử dụng lệnh gọi đến hàm API C mysql_set_charset(), chúng tôi sẽ ổn (trên các bản phát hành MySQL từ năm 2006). Nhưng nhiều hơn về lý do tại sao trong một phút ...

  2. Tải trọng

    Tải trọng mà chúng ta sẽ sử dụng cho lần tiêm này bắt đầu bằng chuỗi byte 0xbf27. Trong gbk, đó là một ký tự nhiềubyte không hợp lệ; trong latin1, đó là chuỗi ¿'. Lưu ý rằng trong latin1 gbk , 0x27riêng nó là một 'ký tự nghĩa đen .

    Chúng tôi đã chọn tải trọng này bởi vì, nếu chúng tôi gọi addslashes()nó, chúng tôi sẽ chèn một ASCII \tức là 0x5c, trước 'ký tự. Vì vậy, chúng tôi sẽ kết thúc với 0xbf5c27, trong đó gbklà một chuỗi hai ký tự: 0xbf5ctiếp theo là 0x27. Hay nói cách khác, một ký tự hợp lệ được theo sau bởi một ký tự không thoát '. Nhưng chúng tôi không sử dụng addslashes(). Vì vậy, đến bước tiếp theo ...

  3. mysql_real_escape_string ()

    Lệnh gọi API C mysql_real_escape_string()khác addslashes()ở chỗ nó biết bộ ký tự kết nối. Vì vậy, nó có thể thực hiện thoát đúng cách cho bộ ký tự mà máy chủ đang mong đợi. Tuy nhiên, cho đến thời điểm này, khách hàng nghĩ rằng chúng tôi vẫn đang sử dụng latin1cho kết nối, bởi vì chúng tôi chưa bao giờ nói điều đó bằng cách khác. Chúng tôi đã nói với máy chủ mà chúng tôi đang sử dụng gbk, nhưng khách hàng vẫn nghĩ đó là latin1.

    Do đó, lời kêu gọi mysql_real_escape_string()chèn dấu gạch chéo ngược và chúng ta có một 'ký tự treo miễn phí trong nội dung "thoát" của chúng ta! Trên thực tế, nếu chúng ta nhìn vào $varbộ gbkký tự, chúng ta sẽ thấy:

    縗 'HOẶC 1 = 1 / *

    Đó là chính xác những gì cuộc tấn công yêu cầu.

  4. Truy vấn

    Phần này chỉ là hình thức, nhưng đây là truy vấn được kết xuất:

    SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1
    

Xin chúc mừng, bạn vừa tấn công thành công một chương trình bằng mysql_real_escape_string()...

Những người xấu

Nó trở nên tồi tệ hơn. PDOmặc định để mô phỏng các câu lệnh đã chuẩn bị với MySQL. Điều đó có nghĩa là ở phía máy khách, về cơ bản nó thực hiện chạy nước rút mysql_real_escape_string()(trong thư viện C), có nghĩa là những điều sau sẽ dẫn đến việc tiêm thành công:

$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Bây giờ, cần lưu ý rằng bạn có thể ngăn chặn điều này bằng cách tắt các câu lệnh đã chuẩn bị mô phỏng:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Điều này thường sẽ dẫn đến một câu lệnh chuẩn bị thực sự (tức là dữ liệu được gửi trong một gói riêng biệt từ truy vấn). Tuy nhiên, hãy lưu ý rằng PDO sẽ âm thầm dự phòng cho các câu lệnh giả lập mà MySQL không thể chuẩn bị nguyên bản: những câu lệnh mà nó có thể được liệt kê trong sách hướng dẫn, nhưng hãy cẩn thận để chọn phiên bản máy chủ thích hợp).

Xấu xí

Tôi đã nói ngay từ đầu rằng chúng tôi có thể ngăn chặn tất cả những điều này nếu chúng tôi sử dụng mysql_set_charset('gbk')thay thế SET NAMES gbk. Và điều đó đúng với điều kiện bạn đang sử dụng bản phát hành MySQL từ năm 2006.

Nếu bạn đang sử dụng một phiên bản MySQL trước, sau đó là một lỗi trong mysql_real_escape_string()có nghĩa là ký tự nhiều byte không hợp lệ như những người trong Payload của chúng tôi bị đối xử như byte duy nhất cho mục đích thoát ngay cả khi khách hàng đã được thông báo một cách chính xác của mã hóa kết nối và do đó cuộc tấn công này sẽ vẫn thành công. Lỗi này đã được cố định trong MySQL 4.1.20 , 5.0.22 và 5.1.11 .

Nhưng điều tồi tệ nhất là nó PDOđã không hiển thị API C cho mysql_set_charset()đến 5.3.6, vì vậy trong các phiên bản trước, nó không thể ngăn chặn cuộc tấn công này cho mọi lệnh có thể! Bây giờ nó được hiển thị dưới dạng một tham số DSN .

Ơn cứu độ

Như chúng tôi đã nói ở phần đầu, để cuộc tấn công này hoạt động, kết nối cơ sở dữ liệu phải được mã hóa bằng cách sử dụng một bộ ký tự dễ bị tấn công. utf8mb4là không dễ bị tổn thương và chưa thể hỗ trợ tất cả các ký tự Unicode: vì vậy bạn có thể chọn để sử dụng mà thay vào đó, nhưng nó đã chỉ được đưa ra từ MySQL 5.5.3. Một giải pháp thay thế là utf8, cũng không dễ bị tấn công và có thể hỗ trợ toàn bộ Mặt phẳng đa ngôn ngữ cơ bản Unicode .

Ngoài ra, bạn có thể bật NO_BACKSLASH_ESCAPESchế độ SQL, chế độ này (trong số những thứ khác) thay đổi hoạt động của mysql_real_escape_string(). Với chế độ này được bật, 0x27sẽ được thay thế bằng 0x2727chứ không phải 0x5c27và do đó quá trình thoát không thể tạo các ký tự hợp lệ trong bất kỳ mã hóa dễ bị tổn thương nào mà chúng không tồn tại trước đó (tức 0xbf27là vẫn còn, 0xbf27v.v.) - vì vậy máy chủ sẽ vẫn từ chối chuỗi là không hợp lệ . Tuy nhiên, hãy xem câu trả lời của @ eggyal để biết một lỗ hổng khác có thể phát sinh khi sử dụng chế độ SQL này.

Ví dụ an toàn

Các ví dụ sau là an toàn:

mysql_query('SET NAMES utf8');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*"); mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Bởi vì máy chủ đang mong đợi utf8...

mysql_set_charset('gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*"); mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Bởi vì chúng tôi đã đặt đúng bộ ký tự để máy khách và máy chủ khớp nhau.

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Bởi vì chúng tôi đã tắt các câu lệnh chuẩn bị giả lập.

$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password);
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Bởi vì chúng tôi đã đặt bộ ký tự đúng cách.

$mysqli->query('SET NAMES gbk');
$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "\xbf\x27 OR 1=1 /*"; $stmt->bind_param('s', $param); $stmt->execute();

Bởi vì MySQLi luôn thực hiện các câu lệnh chuẩn bị đúng.

Kết thúc

Nếu bạn:

  • Sử dụng các phiên bản hiện đại của MySQL (muộn 5.1, tất cả 5.5, 5.6, v.v.) Tham số bộ mã ký tự DSN của AND mysql_set_charset() / $mysqli->set_charset()/ PDO (trong PHP ≥ 5.3.6)

HOẶC LÀ

  • Không sử dụng bộ ký tự dễ bị tấn công để mã hóa kết nối (bạn chỉ sử dụng utf8/ latin1/ ascii/ vv)

Bạn an toàn 100%.

Nếu không, bạn sẽ dễ bị tổn thương ngay cả khi bạn đang sử dụngmysql_real_escape_string() ...

190 eggyal Apr 25 2014 at 02:15

TL; DR

mysql_real_escape_string()sẽ không cung cấp bất kỳ biện pháp bảo vệ nào (và hơn nữa có thể làm hỏng dữ liệu của bạn) nếu:

  • NO_BACKSLASH_ESCAPESChế độ SQL của MySQL được kích hoạt ( có thể là, trừ khi bạn chọn rõ ràng một chế độ SQL khác mỗi khi bạn kết nối ); và

  • các ký tự chuỗi SQL của bạn được trích dẫn bằng cách sử dụng các "ký tự dấu ngoặc kép .

Điều này đã được gửi dưới dạng lỗi # 72458 và đã được sửa trong MySQL v5.7.6 (xem phần có đầu " The Saving Grace ", bên dưới).

Đây là một trường hợp khác, (có lẽ ít hơn?) Tối nghĩa EDGE !!!

Để tỏ lòng kính trọng với câu trả lời xuất sắc của @ ircmaxell (thực sự, đây được coi là lời tâng bốc chứ không phải đạo văn!), Tôi sẽ áp dụng định dạng của anh ấy:

Cuộc tấn công

Bắt đầu bằng một cuộc trình diễn ...

mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"'); // could already be set
$var = mysql_real_escape_string('" OR 1=1 -- '); mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

Điều này sẽ trả về tất cả các bản ghi từ testbảng. Một cuộc mổ xẻ:

  1. Chọn một chế độ SQL

    mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"');
    

    Như được ghi lại trong String Literals :

    Có một số cách để bao gồm các ký tự trích dẫn trong một chuỗi:

    • Dấu “ '” bên trong chuỗi được trích dẫn bằng “ '” có thể được viết là “ ''”.

    • Dấu “ "” bên trong chuỗi được trích dẫn bằng “ "” có thể được viết là “ ""”.

    • Bắt đầu ký tự trích dẫn bằng ký tự thoát (“ \”).

    • Dấu “ '” bên trong chuỗi được trích dẫn bằng “ "” không cần xử lý đặc biệt và không cần nhân đôi hoặc thoát ra. Theo cách tương tự, “ "” bên trong chuỗi được trích dẫn bằng “ '” không cần xử lý đặc biệt.

    Nếu chế độ SQL của máy chủ bao gồm NO_BACKSLASH_ESCAPES, thì tùy chọn thứ ba trong số các tùy chọn này — là cách tiếp cận thông thường được áp dụng mysql_real_escape_string()— không khả dụng: một trong hai tùy chọn đầu tiên phải được sử dụng thay thế. Lưu ý rằng tác dụng của dấu đầu dòng thứ tư là người ta nhất thiết phải biết ký tự sẽ được sử dụng để trích dẫn nghĩa đen để tránh làm xáo trộn dữ liệu của một người.

  2. Tải trọng

    " OR 1=1 -- 
    

    Trọng tải bắt đầu quá trình tiêm này theo nghĩa đen với "ký tự. Không có mã hóa cụ thể. Không có ký tự đặc biệt. Không có byte kỳ lạ.

  3. mysql_real_escape_string ()

    $var = mysql_real_escape_string('" OR 1=1 -- ');
    

    May mắn thay, mysql_real_escape_string()không kiểm tra chế độ SQL và điều chỉnh hành vi của nó cho phù hợp. Xem libmysql.c:

    ulong STDCALL
    mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
                 ulong length)
    {
      if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
        return escape_quotes_for_mysql(mysql->charset, to, 0, from, length);
      return escape_string_for_mysql(mysql->charset, to, 0, from, length);
    }
    

    Do đó, một hàm cơ bản khác escape_quotes_for_mysql(), được gọi nếu NO_BACKSLASH_ESCAPESchế độ SQL đang được sử dụng. Như đã đề cập ở trên, một hàm như vậy cần biết ký tự nào sẽ được sử dụng để trích dẫn nghĩa đen để lặp lại nó mà không làm cho ký tự trích dẫn khác được lặp lại theo nghĩa đen.

    Tuy nhiên, hàm này tự ý giả định rằng chuỗi sẽ được trích dẫn bằng cách sử dụng 'ký tự dấu nháy đơn . Xem charset.c:

    /*
      Escape apostrophes by doubling them up
    
    // [ deletia 839-845 ]
    
      DESCRIPTION
        This escapes the contents of a string by doubling up any apostrophes that
        it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
        effect on the server.
    
    // [ deletia 852-858 ]
    */
    
    size_t escape_quotes_for_mysql(CHARSET_INFO *charset_info,
                                   char *to, size_t to_length,
                                   const char *from, size_t length)
    {
    // [ deletia 865-892 ]
    
        if (*from == '\'')
        {
          if (to + 2 > to_end)
          {
            overflow= TRUE;
            break;
          }
          *to++= '\'';
          *to++= '\'';
        }
    

    Vì vậy, nó để lại các "ký tự trong dấu ngoặc kép không bị đụng chạm (và nhân đôi tất cả các 'ký tự trong dấu ngoặc đơn ) bất kể ký tự thực được sử dụng để trích dẫn nghĩa đen ! Trong trường hợp của chúng tôi, $varvẫn hoàn toàn giống như đối số được cung cấp mysql_real_escape_string()- cứ như thể không có cuộc chạy trốn nào diễn ra cả .

  4. Truy vấn

    mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');
    

    Một cái gì đó có hình thức, truy vấn được hiển thị là:

    SELECT * FROM test WHERE name = "" OR 1=1 -- " LIMIT 1
    

Như một người bạn uyên bác của tôi đã nói: xin chúc mừng, bạn vừa tấn công thành công một chương trình bằng cách sử dụng mysql_real_escape_string()...

Những người xấu

mysql_set_charset()không thể giúp đỡ, vì điều này không liên quan gì đến các bộ ký tự; cũng không thể mysqli::real_escape_string(), vì đó chỉ là một trình bao bọc khác xung quanh cùng một chức năng này.

Vấn đề, nếu chưa rõ ràng, là lệnh gọi mysql_real_escape_string() không thể biết ký tự nào sẽ được trích dẫn, vì điều đó để nhà phát triển quyết định sau. Vì vậy, trong NO_BACKSLASH_ESCAPESchế độ, thực sự không có cách nào mà hàm này có thể thoát khỏi mọi đầu vào một cách an toàn để sử dụng với trích dẫn tùy ý (ít nhất, không phải là không có nhân đôi các ký tự không yêu cầu nhân đôi và do đó trộn dữ liệu của bạn).

Xấu xí

Nó trở nên tồi tệ hơn. NO_BACKSLASH_ESCAPEScó thể không phổ biến trong tự nhiên do sự cần thiết của việc sử dụng nó để tương thích với SQL tiêu chuẩn (ví dụ: xem phần 5.3 của đặc tả SQL-92 , cụ thể là <quote symbol> ::= <quote><quote>sản xuất ngữ pháp và thiếu bất kỳ ý nghĩa đặc biệt nào cho dấu gạch chéo ngược). Hơn nữa, việc sử dụng nó một cách rõ ràng được khuyến cáo như là một cách giải quyết với (dài kể từ cố định) lỗi mà bài ircmaxell của mô tả. Ai biết được, một số DBA thậm chí có thể định cấu hình nó để được bật theo mặc định như một phương tiện không khuyến khích sử dụng các phương pháp thoát không chính xác như addslashes().

Ngoài ra, chế độ SQL của một kết nối mới được máy chủ đặt theo cấu hình của nó ( SUPERngười dùng có thể thay đổi bất kỳ lúc nào); do đó, để chắc chắn về hành vi của máy chủ, bạn phải luôn chỉ định rõ ràng chế độ mong muốn của mình sau khi kết nối.

Ơn cứu độ

Vì vậy, miễn là bạn luôn đặt chế độ SQL một cách rõ ràng không bao gồm NO_BACKSLASH_ESCAPEShoặc trích dẫn các ký tự chuỗi MySQL bằng cách sử dụng ký tự trích dẫn đơn, lỗi này không thể tạo ra cái đầu xấu xí của nó: tương ứng escape_quotes_for_mysql()sẽ không được sử dụng hoặc giả định về ký tự trích dẫn nào yêu cầu lặp lại sẽ đúng.

Vì lý do này, tôi khuyên bất kỳ ai sử dụng NO_BACKSLASH_ESCAPEScũng bật ANSI_QUOTESchế độ, vì nó sẽ buộc sử dụng theo thói quen các ký tự chuỗi được trích dẫn đơn. Lưu ý rằng điều này không ngăn chặn việc chèn SQL trong trường hợp các ký tự được trích dẫn kép xảy ra được sử dụng — nó chỉ làm giảm khả năng điều đó xảy ra (vì các truy vấn thông thường, không độc hại sẽ không thành công).

Trong PDO, cả chức năng tương đương PDO::quote()và trình giả lập câu lệnh đã chuẩn bị của nó đều được gọi mysql_handle_quoter()- mà nó thực hiện chính xác điều này: nó đảm bảo rằng ký tự thoát được trích dẫn trong dấu ngoặc kép, vì vậy bạn có thể chắc chắn rằng PDO luôn miễn nhiễm với lỗi này.

Kể từ MySQL v5.7.6, lỗi này đã được sửa. Xem nhật ký thay đổi :

Chức năng được Thêm hoặc Thay đổi

  • Thay đổi không tương thích: Một hàm API C mớimysql_real_escape_string_quote(), đã được triển khai để thay thếmysql_real_escape_string()vì hàm thứ hai có thể không mã hóa đúng các ký tự khiNO_BACKSLASH_ESCAPESchế độ SQL được bật. Trong trường hợp này,mysql_real_escape_string()không thể thoát các ký tự trích dẫn ngoại trừ bằng cách nhân đôi chúng và để làm điều này đúng cách, nó phải biết nhiều thông tin về ngữ cảnh trích dẫn hơn là có sẵn. mysql_real_escape_string_quote()có thêm một đối số để chỉ định ngữ cảnh trích dẫn. Để biết chi tiết sử dụng, hãy xem mysql_real_escape_string_quote () .

     Ghi chú

    Các ứng dụng nên được sửa đổi để sử dụng mysql_real_escape_string_quote(), thay vì mysql_real_escape_string(), ứng dụng này hiện không thành công và tạo ra CR_INSECURE_API_ERRlỗi nếu NO_BACKSLASH_ESCAPESđược bật.

    Tài liệu tham khảo: Xem thêm Lỗi # 19211994.

Ví dụ an toàn

Kết hợp với lỗi được giải thích bởi ircmaxell, các ví dụ sau hoàn toàn an toàn (giả sử rằng một trong hai đang sử dụng MySQL sau 4.1.20, 5.0.22, 5.1.11; hoặc một trong hai không sử dụng mã hóa kết nối GBK / Big5) :

mysql_set_charset($charset);
mysql_query("SET SQL_MODE=''");
$var = mysql_real_escape_string('" OR 1=1 /*'); mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

... bởi vì chúng tôi đã chọn một cách rõ ràng một chế độ SQL không bao gồm NO_BACKSLASH_ESCAPES.

mysql_set_charset($charset); $var = mysql_real_escape_string("' OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

... bởi vì chúng tôi đang trích dẫn chuỗi của chúng tôi theo nghĩa đen bằng dấu ngoặc đơn.

$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $stmt->execute(["' OR 1=1 /*"]);

... bởi vì các câu lệnh được chuẩn bị sẵn của PDO miễn nhiễm với lỗ hổng này (và ircmaxell cũng vậy, miễn là bạn đang sử dụng PHP≥5.3.6 và bộ ký tự đã được đặt chính xác trong DSN; hoặc mô phỏng câu lệnh chuẩn bị đó đã bị vô hiệu hóa) .

$var = $pdo->quote("' OR 1=1 /*");
$stmt = $pdo->query("SELECT * FROM test WHERE name = $var LIMIT 1");

... bởi vì quote()hàm của PDO không chỉ thoát khỏi nghĩa đen, mà còn trích dẫn nó (trong các 'ký tự dấu nháy đơn ); lưu ý rằng để tránh lỗi của ircmaxell trong trường hợp này, bạn phải sử dụng PHP≥5.3.6 đã đặt đúng bộ ký tự trong DSN.

$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $param = "' OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();

... vì các câu lệnh chuẩn bị của MySQLi là an toàn.

Kết thúc

Do đó, nếu bạn:

  • sử dụng các câu lệnh chuẩn bị sẵn

HOẶC LÀ

  • sử dụng MySQL v5.7.6 trở lên

HOẶC LÀ

  • trong Ngoài ra để sử dụng một trong những giải pháp trong tóm tắt ircmaxell của, sử dụng tại một thiểu số:

    • PDO;
    • chuỗi ký tự được trích dẫn đơn; hoặc là
    • một chế độ SQL được đặt rõ ràng không bao gồm NO_BACKSLASH_ESCAPES

... thì bạn sẽ hoàn toàn an toàn (các lỗ hổng nằm ngoài phạm vi của chuỗi thoát sang một bên).

19 Slava Apr 21 2011 at 15:01

Chà, không có gì thực sự có thể vượt qua điều đó, ngoài %ký tự đại diện. Nó có thể nguy hiểm nếu bạn đang sử dụng LIKEtuyên bố vì kẻ tấn công có thể %đăng nhập giống như đăng nhập nếu bạn không lọc ra và sẽ phải thực thi mật khẩu của bất kỳ người dùng nào của bạn. Mọi người thường đề xuất sử dụng các câu lệnh chuẩn bị sẵn để làm cho nó an toàn 100%, vì dữ liệu không thể can thiệp vào chính truy vấn theo cách đó. Nhưng đối với những truy vấn đơn giản như vậy, có lẽ sẽ hiệu quả hơn nếu thực hiện những việc như$login = preg_replace('/[^a-zA-Z0-9_]/', '', $login);