Câu lệnh chuẩn bị cập nhật php sql

Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách thực hiện các truy vấn như CHỌN, CẬP NHẬT, XÓA, v.v. với các điều kiện khác nhau với các câu lệnh chuẩn bị MYSQLI trong PHP. Trước khi chúng ta bắt đầu, tôi sẽ giới thiệu ngắn gọn với bạn về các câu lệnh đã chuẩn bị

Show

Các câu lệnh chuẩn bị MYSQLI là gì?

MYSQL là một hệ thống cơ sở dữ liệu quan hệ phổ biến. MYSQLI là một phần mở rộng PHP mạnh mẽ để kết nối với MYSQL. Các câu lệnh đã chuẩn bị dưới dạng các truy vấn được chuẩn bị trước và thực hiện sau với dữ liệu

Tại sao các báo cáo đã chuẩn bị lại quan trọng?

Đơn giản, các câu lệnh được chuẩn bị sẵn bảo vệ các trang web khỏi SQL Injection có thể được sử dụng để tấn công một trang web. Ngoài ra, các câu lệnh đã chuẩn bị có thể nhanh hơn các truy vấn thông thường theo một số nguồn (Tuy nhiên, theo kinh nghiệm của tôi, chúng gần như giống nhau khi thực hiện các truy vấn đơn giản. Tuy nhiên, đối với các truy vấn định kỳ, các câu lệnh đã chuẩn bị siêu nhanh hơn các truy vấn thông thường). Điều tốt nhất về báo cáo chuẩn bị là khả năng đọc. Chúng có thể dễ dàng được đọc, hiểu và quản lý

Trước khi tôi bắt đầu, nếu bạn muốn xem một cách thậm chí còn dễ dàng hơn để sử dụng các câu lệnh chuẩn bị sẵn của MySQLi, hãy xem lớp trình bao bọc của tôi. Ngoài ra, đây là một tài nguyên tuyệt vời để tìm hiểu các câu lệnh được chuẩn bị sẵn PDO, đây là lựa chọn tốt hơn cho người mới bắt đầu và hầu hết mọi người nói chung

Một nỗ lực hack gần đây đã được phát hiện và có vẻ như họ đang cố gắng đánh sập toàn bộ cơ sở dữ liệu. Một cuộc họp nhân viên ngẫu hứng đã được triệu tập vào lúc 2 giờ sáng, và mọi người trong công ty đang vô cùng lo lắng. Trớ trêu thay, với tư cách là người quản lý cơ sở dữ liệu, bạn lại là người bình tĩnh nhất. Tại sao? . Trên thực tế, bạn thấy điều này thật hài hước, vì những tin tặc này có thể sẽ khó chịu vì họ đã lãng phí thời gian vào những nỗ lực vô ích.

Hy vọng kịch bản này sẽ không bao giờ xảy ra với trang web của bạn. Tuy nhiên, chắc chắn là một ý tưởng tốt để thực hiện các biện pháp phòng ngừa thích hợp. Nếu được triển khai đúng cách, các câu lệnh đã chuẩn bị (còn gọi là truy vấn được tham số hóa) sẽ cung cấp khả năng bảo vệ vượt trội chống lại việc tiêm nhiễm SQL. Về cơ bản, bạn chỉ cần tạo mẫu truy vấn với các giá trị giữ chỗ, sau đó thay thế các đầu vào giả bằng các giá trị thực. Thoát là không cần thiết, vì nó sẽ coi các giá trị là chữ cái;

Những câu nói chuẩn bị ban đầu có vẻ đáng sợ, nhưng một khi bạn đã quen với nó, nó sẽ giống như bản chất thứ hai đối với bạn. Mục tiêu của hướng dẫn này là biến một người có ít hoặc không có kiến ​​thức về các câu chuẩn bị thành một chuyên gia

từ chối trách nhiệm. Đừng thực sự thoải mái như trình quản lý cơ sở dữ liệu này. Khi nói đến bảo mật, bạn không bao giờ nên tự mãn, bất kể bạn nghĩ hệ thống của mình an toàn đến mức nào

Cách thức hoạt động của SQL injection

Truyện tranh mang tính biểu tượng sau đây, được gọi là Bobby Tables, là một mô tả tuyệt vời về cách thức hoạt động của một cuộc tấn công SQL injection. Tất cả tín dụng cho hình ảnh được chuyển đến trang web này cho phần hùng biện cổ điển này

Câu lệnh chuẩn bị cập nhật php sql

Bây giờ chúng ta đã hoàn thành phần lý thuyết, hãy bắt tay vào thực hành. Trước khi tôi bắt đầu, nếu bạn đang thắc mắc chính xác cách thức hoạt động của "Bobby Table Attack", hãy xem phần giải thích này

Trong một cuộc gọi MySQL bình thường, bạn sẽ làm điều gì đó như

$name = $_POST['name'];
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");

Vấn đề với điều này là nếu nó dựa trên đầu vào của người dùng, như trong ví dụ, thì một người dùng ác ý có thể làm

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
4. Bây giờ, tuyên bố này sẽ luôn được đánh giá là đúng, vì
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
5. Trong trường hợp này, người dùng độc hại hiện có quyền truy cập vào toàn bộ bảng của bạn. Chỉ cần tưởng tượng điều gì có thể xảy ra nếu thay vào đó là một truy vấn
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
6. Hãy nhìn vào những gì đang thực sự xảy ra với tuyên bố

SELECT * FROM myTable WHERE name='' OR '1'='1' 

Tin tặc có thể gây ra nhiều thiệt hại cho trang web của bạn nếu các truy vấn của bạn được thiết lập như thế này. Một sửa chữa dễ dàng cho điều này sẽ là làm

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");

Lưu ý rằng tương tự như ví dụ đầu tiên, tôi vẫn thêm dấu ngoặc kép vào giá trị cột. Không có dấu ngoặc kép, các chuỗi vẫn dễ bị SQL injection như nhau. Nếu bạn sẽ sử dụng mệnh đề THÍCH, thì bạn cũng nên làm như vậy với

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
7, vì
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
8 sẽ không làm điều này

Điều này bao gồm các chuỗi, như tên hàm ngụ ý, nhưng còn các số thì sao? . Nếu bạn đang truyền biến thành int, bạn không cần phải thoát khỏi bất kỳ thứ gì. Bạn đã bảo nó về cơ bản đảm bảo rằng giá trị sẽ là một số nguyên. Làm

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");
} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand
}
0 là đủ. Vì nó là một số nguyên nên rõ ràng bạn cũng không cần thêm dấu ngoặc kép vào cột sql
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");
} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand
}
1

Trong thực tế, nếu bạn làm theo các hướng dẫn này một cách hoàn hảo, thì chỉ cần sử dụng

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
8 cho chuỗi và
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");
} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand
}
3 cho số nguyên là đủ. Chỉ cần đừng quên đặt bộ ký tự mặc định. Điều này có thể được thiết lập trong php. ini (phải là giá trị mặc định) như
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");
} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand
}
4 và bằng cách sử dụng
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");
} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand
}
5 trên mỗi trang sử dụng
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");
} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand
}
6. Nhưng chỉ đối với những thứ hợp pháp trong các câu lệnh đã chuẩn bị, đó là các giá trị trong câu lệnh WHERE hoặc các giá trị cột;

Bất chấp điều đó, tôi vẫn thực sự khuyên bạn nên sử dụng các câu lệnh đã chuẩn bị sẵn, vì rõ ràng chúng phù hợp hơn để bảo vệ chống lại việc tiêm nhiễm SQL và ít mắc lỗi hơn, vì bạn không phải lo lắng về việc định dạng thủ công — thay vào đó, bạn chỉ cần thay thế các trình giữ chỗ giả bằng các giá trị của mình. Tất nhiên, bạn vẫn muốn lọc và vệ sinh đầu vào của mình để ngăn chặn XSS. Với các câu lệnh chuẩn bị sẵn, có ít khía cạnh cần xem xét hơn, cùng với một số

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");
} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand
}
6 (Không đặt đúng bộ ký tự là một trong những nguyên nhân. ). Tóm lại, hoàn toàn không có lý do chính đáng nào để sử dụng
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");
} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand
}
8 thay cho các câu đã chuẩn bị sẵn. Tôi chỉ trình bày cách định dạng thủ công các truy vấn của bạn với nó, để cho thấy rằng điều đó là có thể. Trong thực tế, sẽ thật ngu ngốc nếu không sử dụng các câu lệnh đã chuẩn bị sẵn để ngăn chặn SQL injection

Các câu lệnh đã chuẩn bị sẵn của MySQLi hoạt động như thế nào

Nói một cách dễ hiểu, đây là cách các câu lệnh được chuẩn bị sẵn của MySQLi hoạt động trong PHP

  1. Chuẩn bị một truy vấn SQL với các giá trị trống làm trình giữ chỗ (với một dấu chấm hỏi cho mỗi giá trị)
  2. Liên kết các biến với các trình giữ chỗ bằng cách nêu rõ từng biến, cùng với loại của nó
  3. Thực hiện kiểm tra

Bốn loại biến được phép

  • tôi - Số nguyên
  • d - Gấp đôi
  • s - Chuỗi
  • b - Đốm màu

Một câu lệnh chuẩn bị, đúng như tên gọi của nó, là một cách chuẩn bị lệnh gọi MySQL mà không cần lưu trữ các biến. Bạn nói với nó rằng các biến cuối cùng sẽ đến đó - chỉ là chưa. Cách tốt nhất để chứng minh điều đó là bằng ví dụ

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
8

Nếu bạn chưa từng thấy câu lệnh chuẩn bị trước, điều này có thể hơi lạ. Về cơ bản, điều đang xảy ra là bạn đang tạo một mẫu cho câu lệnh SQL sẽ là gì. Trong trường hợp này, chúng tôi đang chọn mọi thứ từ

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");
} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand
}
9, trong đó
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");
} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand
}
1 và
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
11 bằng
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
12. Dấu chấm hỏi chỉ là một trình giữ chỗ cho nơi các giá trị sẽ đi

Phương thức

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
13 là nơi bạn đính kèm các biến vào các giá trị giả trong mẫu đã chuẩn bị. Lưu ý cách có hai chữ cái trong dấu ngoặc kép trước các biến. Điều này cho cơ sở dữ liệu biết các loại biến.
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
14 chỉ định tên đó sẽ là một giá trị chuỗi, trong khi
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
15 buộc tuổi phải là một số nguyên. Đây chính là lý do tại sao tôi không thêm dấu ngoặc kép xung quanh dấu chấm hỏi cho tên, giống như cách tôi thường làm đối với một chuỗi trong lệnh gọi SQL. Bạn có thể nghĩ rằng tôi vừa quên, nhưng thực tế là đơn giản là không cần (Thực tế, nó thực sự sẽ không hoạt động nếu bạn đặt dấu ngoặc kép xung quanh
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
12, vì nó sẽ được coi là một chuỗi ký tự, thay vì . ). Bạn đã nói với nó rằng nó sẽ là một chuỗi ký tự khi bạn gọi
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
13, vì vậy ngay cả khi một người dùng độc hại cố gắng chèn SQL vào đầu vào của người dùng của bạn, thì nó vẫn sẽ được coi là một chuỗi.
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
18 sau đó thực sự chạy mã; . Chúng tôi sẽ đề cập đến việc tìm nạp kết quả trong phần Chọn

Tạo kết nối MySQLi mới

Tạo một MySQLi mới khá đơn giản. Tôi khuyên bạn nên đặt tên tệp có tên là

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
19 và đặt tệp này trực tiếp bên ngoài thư mục gốc của bạn (html, public_html) để thông tin đăng nhập của bạn được bảo mật. Chúng tôi cũng sẽ sử dụng xử lý ngoại lệ, bằng cách sử dụng
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
00. Điều này có thể lạ đối với bạn, đặc biệt nếu bạn chưa bao giờ sử dụng toán tử bitwise trước đây. Nhưng tất cả những gì nó đang làm là báo cáo tất cả các lỗi, trong khi chuyển đổi chúng thành ngoại lệ, sử dụng lớp mysqli_sql_Exception

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");

Rất nhiều hướng dẫn, bao gồm cả hướng dẫn sử dụng PHP, chỉ ra cách sử dụng

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
01 bằng cách in nó bằng
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
02 hoặc
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
03. Nhưng điều này không thực sự cần thiết (chưa kể là cực kỳ ngu ngốc, vì bạn sẽ in thông tin này ra thế giới), vì thông báo lỗi sẽ được thêm vào nhật ký lỗi của bạn. Thông báo trong
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
02 phải là thứ mà người dùng bình thường có thể hiểu được, chẳng hạn như
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
05

Bạn sẽ nghĩ rằng việc đặt bộ ký tự thành

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
06 trong php của bạn. ini sẽ đủ, cùng với
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
07 cho toàn bộ cơ sở dữ liệu của bạn, nhưng đôi khi sẽ xảy ra lỗi lạ nếu bạn không đặt nó trong tệp php của mình,

Ngoài ra, bạn có thể khởi tạo nó trong khối thử/bắt nếu bạn bật báo cáo nội bộ, điều mà tôi đã đề cập trong phần xử lý lỗi. Vui lòng không bao giờ báo cáo lỗi trực tiếp trên trang web của bạn trong quá trình sản xuất. Bạn sẽ tự chuốc họa vào thân vì một sai lầm ngớ ngẩn như vậy, vì nó sẽ in ra thông tin cơ sở dữ liệu nhạy cảm của bạn (tên người dùng, mật khẩu và tên cơ sở dữ liệu). Đây là những gì php của bạn. ini sẽ giống như trong sản xuất. làm cả

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
08 và
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
09. Ngoài ra, không lặp lại lỗi trong quá trình sản xuất

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");
} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand
}

Nếu bạn thích sử dụng set_Exception_handler() thay vì

SELECT * FROM myTable WHERE name='' OR '1'='1' 
20, bạn có thể làm như sau để tránh lồng nhau. Nếu bạn đang sử dụng phương pháp này, bạn cần hiểu rằng nó sẽ ảnh hưởng đến mọi trang được bao gồm trong. Do đó, bạn phải sử dụng lại chức năng này với một thông báo tùy chỉnh cho mỗi trang hoặc sử dụng restore_exception_handler() để hoàn nguyên về phiên bản PHP tích hợp. Nếu bạn tạo nhiều cái, nó sẽ chuyển đến cái trước đó bạn đã tạo

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
1

Có một hậu quả rất nghiêm trọng khi sử dụng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
21, đó là nó sẽ báo cáo thông tin cơ sở dữ liệu nhạy cảm của bạn. Bạn có ba tùy chọn để vẫn sử dụng nó nhưng không báo cáo mật khẩu của mình

  1. Bạn cũng có thể sử dụng hoàn toàn
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    21 cho mọi thứ ngoại trừ việc tạo kết nối nếu bạn thực hiện theo phương pháp đầu tiên mà tôi đã trình bày với
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    23 (mật khẩu không được hiển thị) và chỉ cần đặt
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    21 sau
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    25
  2. Nếu bạn gọi
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    21 trước khi tạo kết nối, thì bạn cần đảm bảo rằng nó nằm trong khối try/catch và bạn in cụ thể trong nhật ký lỗi của mình là
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    27, chứ không phải
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    28, vốn vẫn chứa thông tin nhạy cảm của bạn. Điều này rõ ràng áp dụng nghiêm ngặt cho các nhà xây dựng
  3. Sử dụng
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    29 theo cách tương tự như 2 và sử dụng
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    27

Tôi thực sự khuyên bạn nên thực hiện một trong những phương pháp này. Ngay cả khi bạn siêng năng và đảm bảo rằng tất cả các lỗi của bạn chỉ xuất hiện trong nhật ký lỗi của bạn, cá nhân tôi không hiểu tại sao mọi người cần đăng nhập mật khẩu của họ. Dù sao thì bạn cũng đã biết vấn đề là gì

Chèn, cập nhật và xóa

Chèn, cập nhật và xóa có cú pháp giống nhau nên sẽ được kết hợp với nhau

Chèn

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
0
SELECT * FROM myTable WHERE name='' OR '1'='1' 
2

Xóa bỏ

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
6

Nhận số hàng bị ảnh hưởng

Bạn cũng có thể muốn kiểm tra trạng thái của một hàng mà bạn đã chèn, cập nhật hoặc xóa. Đây là cách bạn sẽ làm nếu bạn đang cập nhật một hàng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
0

Trong trường hợp này, chúng tôi đã kiểm tra xem có hàng nào được cập nhật không. Để tham khảo, đây là cách sử dụng cho các giá trị trả về của

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
61

-1 - truy vấn trả về lỗi;

0 - không có bản ghi nào được cập nhật trên

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
63, không có hàng nào khớp với mệnh đề
$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
64 hoặc không có truy vấn nào được thực hiện

Lớn hơn 0 - trả về số hàng bị ảnh hưởng;

Nhận hàng phù hợp

Một vấn đề phổ biến với

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
67 là không thể biết tại sao nó lại trả về 0 khi CẬP NHẬT. Điều này là do nó in số lượng hàng đã thay đổi, do đó, sẽ không rõ ràng nếu bạn cập nhật (các) giá trị của mình với cùng một dữ liệu

Một tính năng tuyệt vời chỉ có ở MySQLi và không tồn tại trong PDO, là khả năng nhận thêm thông tin về một truy vấn. Về mặt kỹ thuật, bạn có thể đạt được điều đó trong PDO, nhưng điều đó chỉ có thể được thực hiện trong kết nối, do đó bạn không thể chọn

SELECT * FROM myTable WHERE name='' OR '1'='1' 
1

Điều này sẽ in

SELECT * FROM myTable WHERE name='' OR '1'='1' 
2

Tôi thấy đây là một triển khai khá thiếu thận trọng, vì việc sử dụng nó như hiện tại là cực kỳ không phù hợp. May mắn thay, chúng ta có thể thay đổi điều đó, bằng cách chuyển đổi nó thành một mảng kết hợp. Tất cả tín dụng sẽ làm điều này trên các tài liệu PHP. Mặc dù sử dụng

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
68 để CẬP NHẬT cho đến nay là trường hợp sử dụng phổ biến nhất của nó, nhưng nó cũng có thể được sử dụng cho một số trường hợp

SELECT * FROM myTable WHERE name='' OR '1'='1' 
3

Bây giờ điều này sẽ xuất ra một mảng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
4

Nhận khóa chính mới nhất được chèn

SELECT * FROM myTable WHERE name='' OR '1'='1' 
5

Kiểm tra xem mục trùng lặp

Điều này rất hữu ích nếu bạn tạo một ràng buộc duy nhất trên một bảng, vì vậy không được phép sao chép. Bạn thậm chí có thể làm điều này cho nhiều cột, vì vậy nó sẽ phải là hoán vị chính xác. Nếu tắt chức năng xử lý ngoại lệ, bạn sẽ kiểm tra mã lỗi bằng

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
69. Với việc xử lý ngoại lệ được bật, bạn có thể chọn giữa phương pháp đó hoặc phương pháp ngoại lệ chung
SELECT * FROM myTable WHERE name='' OR '1'='1' 
00. Lưu ý, điều này khác với PDOException, nó sẽ in SQLSTATE, thay vì mã lỗi

Đây là một. Mã lỗi cho mục nhập hàng trùng lặp từ bản cập nhật hoặc phần chèn là 1062 và SQLSTATE là 23000. Để kiểm tra cụ thể SQLSTATE, bạn phải sử dụng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
01

SELECT * FROM myTable WHERE name='' OR '1'='1' 
6

Đây là cách bạn sẽ thiết lập một ràng buộc duy nhất

SELECT * FROM myTable WHERE name='' OR '1'='1' 
7

Lựa chọn

Tất cả các câu lệnh chọn trong các truy vấn được tham số hóa sẽ bắt đầu giống nhau. Tuy nhiên, có một sự khác biệt chính để thực sự lưu trữ và tìm nạp kết quả. Hai phương pháp tồn tại là

SELECT * FROM myTable WHERE name='' OR '1'='1' 
02 và
SELECT * FROM myTable WHERE name='' OR '1'='1' 
03

get_result()

Đây là cách linh hoạt hơn trong cả hai, vì nó có thể được sử dụng cho mọi tình huống. Cần lưu ý rằng điều này yêu cầu mysqlnd, đã được đưa vào PHP từ 5. 3 và là trình điều khiển gốc mặc định kể từ 5. 4, như đã nêu ở đây. Tôi nghi ngờ nhiều người đang sử dụng các phiên bản cũ hơn thế, vì vậy bạn thường nên gắn bó với

SELECT * FROM myTable WHERE name='' OR '1'='1' 
02

Điều này về cơ bản cho thấy api mysqli_result thông thường, không chuẩn bị. Có nghĩa là một khi bạn thực hiện

SELECT * FROM myTable WHERE name='' OR '1'='1' 
05, bạn có thể sử dụng nó chính xác như cách bạn sử dụng
SELECT * FROM myTable WHERE name='' OR '1'='1' 
06

Giờ đây, bạn có thể sử dụng các phương thức sau để tìm nạp từng hàng một hoặc tất cả cùng một lúc. Đây chỉ là một số trong những cái phổ biến nhất, nhưng bạn có thể xem toàn bộ lớp mysqli_result để biết tất cả các phương thức của nó

Một hàng

  • SELECT * FROM myTable WHERE name='' OR '1'='1' 
    07 - Tìm nạp một mảng kết hợp
  • SELECT * FROM myTable WHERE name='' OR '1'='1' 
    08 - Tìm nạp một mảng số
  • SELECT * FROM myTable WHERE name='' OR '1'='1' 
    09 - Tìm nạp một mảng đối tượng

Tất cả các

  • SELECT * FROM myTable WHERE name='' OR '1'='1' 
    10 - Tìm nạp một mảng kết hợp
  • SELECT * FROM myTable WHERE name='' OR '1'='1' 
    11 - Tìm nạp một mảng số
SELECT * FROM myTable WHERE name='' OR '1'='1' 
8

đầu ra

SELECT * FROM myTable WHERE name='' OR '1'='1' 
9

bind_result()

Bạn có thể thắc mắc, tại sao lại sử dụng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
03? . Ngoài ra, trước khi
SELECT * FROM myTable WHERE name='' OR '1'='1' 
02 tồn tại và mysqlnd được tích hợp vào PHP, đây là lựa chọn duy nhất của bạn, đó là lý do tại sao nhiều mã kế thừa có thể đang sử dụng nó

Phần khó chịu nhất khi sử dụng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
03 là bạn phải liên kết mọi cột mà bạn chọn và sau đó duyệt qua các giá trị trong một vòng lặp. Điều này rõ ràng là không lý tưởng cho rất nhiều giá trị hoặc để sử dụng với
SELECT * FROM myTable WHERE name='' OR '1'='1' 
16. Bộ chọn dấu sao đặc biệt khó sử dụng với
SELECT * FROM myTable WHERE name='' OR '1'='1' 
03, vì bạn thậm chí không biết những giá trị đó là gì nếu không tìm trong cơ sở dữ liệu. Ngoài ra, điều này làm cho mã của bạn cực kỳ không thể duy trì được với các thay đổi đối với bảng. Điều này thường không thành vấn đề, vì dù sao thì bạn cũng không nên sử dụng bộ chọn ký tự đại diện trong chế độ sản xuất (nhưng bạn biết đấy)

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
0

đầu ra

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
1

Tìm nạp mảng liên kết

Tôi thấy đây là trường hợp sử dụng phổ biến nhất. Tôi cũng sẽ sử dụng chuỗi sau đây, mặc dù điều đó rõ ràng là không cần thiết

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
2

Nếu bạn cần sửa đổi tập kết quả, thì có lẽ bạn nên sử dụng vòng lặp while với

SELECT * FROM myTable WHERE name='' OR '1'='1' 
18 và tìm nạp từng hàng một

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
3

đầu ra

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
4

Bạn thực sự có thể làm điều này bằng cách sử dụng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
03, mặc dù nó rõ ràng không được thiết kế cho nó. Đây là , mặc dù cá nhân tôi cảm thấy điều đó thật tuyệt khi biết là có thể, nhưng thực tế không nên sử dụng

Tìm nạp mảng số

Điều này tuân theo định dạng giống như một mảng kết hợp. Để có được toàn bộ mảng trong một lệnh, không có vòng lặp, bạn sẽ sử dụng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
20. Nếu bạn cần tìm nạp kết quả trong một vòng lặp, bạn phải sử dụng
SELECT * FROM myTable WHERE name='' OR '1'='1' 
21

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
5

Và tất nhiên, việc điều chỉnh vòng lặp while

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
6

đầu ra

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
7

Tìm nạp một hàng

Cá nhân tôi thấy việc sử dụng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
03 đơn giản hơn khi tôi biết thực tế rằng tôi sẽ chỉ tìm nạp một hàng, vì tôi có thể truy cập các biến theo cách rõ ràng hơn

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
8

Bây giờ bạn có thể sử dụng chỉ đơn giản là sử dụng các biến trong

SELECT * FROM myTable WHERE name='' OR '1'='1' 
03, như
SELECT * FROM myTable WHERE name='' OR '1'='1' 
24 vì bạn biết chúng sẽ chỉ chứa một giá trị, không phải một mảng

Đây là phiên bản

SELECT * FROM myTable WHERE name='' OR '1'='1' 
02

$name = $mysqli->real_escape_string($_POST['name']);
$mysqli->query("SELECT * FROM myTable WHERE name='$name'");
9

Sau đó, bạn sẽ sử dụng biến như

SELECT * FROM myTable WHERE name='' OR '1'='1' 
26 chẳng hạn

đầu ra

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
80

Tìm nạp mảng đối tượng

Điều này rất giống với việc tìm nạp một mảng kết hợp. Sự khác biệt chính duy nhất là bạn sẽ truy cập nó như

SELECT * FROM myTable WHERE name='' OR '1'='1' 
27. Ngoài ra, trong trường hợp bạn chưa biết, các đối tượng được truyền theo giá trị, trong khi mảng theo tham chiếu

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
81

đầu ra

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
82

Bạn thậm chí có thể thêm các giá trị thuộc tính vào một lớp hiện có. Tuy nhiên, cần lưu ý rằng có một vấn đề tiềm ẩn, theo tài liệu PHP. Vấn đề là nếu bạn có một giá trị mặc định trong hàm tạo của mình với một tên biến trùng lặp, nó sẽ tìm nạp đối tượng trước rồi đặt giá trị của hàm tạo, do đó ghi đè lên kết quả đã tìm nạp. Thật kỳ lạ, có một "lỗi" từ PHP 5. 6. 21 đến 7. 0. 6 nơi điều này sẽ không xảy ra. Mặc dù điều này vi phạm các nguyên tắc của OOP, một số người vẫn thích tính năng này, mặc dù nó có lỗi trong một số phiên bản nhất định. Một cái gì đó như

SELECT * FROM myTable WHERE name='' OR '1'='1' 
28 trong PDO nên được triển khai trong MySQLi để cung cấp cho bạn tùy chọn để chọn

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
83

Như nhận xét, đây là cách bạn sẽ làm điều đó một cách chính xác. Tất cả những gì bạn cần là một điều kiện if đơn giản để kiểm tra xem biến có bằng giá trị của hàm tạo hay không — nếu không, chỉ cần không đặt nó trong hàm tạo. Điều này về cơ bản giống như sử dụng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
28 trong PDO

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
84

Một hành vi bất ngờ nhưng có khả năng hữu ích khác khi sử dụng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
30 là bạn có thể sửa đổi các biến riêng tư. Tôi thực sự không chắc mình cảm thấy thế nào về điều này, vì điều này dường như vi phạm các nguyên tắc đóng gói

Phần kết luận

bind_result() - được sử dụng tốt nhất để tìm nạp một hàng mà không có quá nhiều cột hoặc

SELECT * FROM myTable WHERE name='' OR '1'='1' 
16;

get_result() - là cái được ưu tiên cho hầu hết mọi trường hợp sử dụng

Thích

Bạn có thể nghĩ rằng bạn có thể làm điều gì đó như

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
85

Nhưng điều này không được phép. Trình giữ chỗ

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
12 phải là toàn bộ giá trị chuỗi hoặc số nguyên. Đây là cách bạn sẽ làm điều đó một cách chính xác

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
86

Ở đâu trong mảng

Đây chắc chắn là điều tôi muốn thấy được cải thiện trong MySQLi. Hiện tại, có thể sử dụng các câu lệnh được chuẩn bị sẵn của MySQLi với

SELECT * FROM myTable WHERE name='' OR '1'='1' 
33, nhưng cảm thấy hơi dài dòng

lưu ý bên lề. Hai ví dụ sau sử dụng giải nén đối số for, yêu cầu PHP 5. 6+. Nếu bạn đang sử dụng phiên bản thấp hơn, thì bạn có thể thay thế nó bằng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
34

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
87

Với các trình giữ chỗ khác

Ví dụ đầu tiên cho thấy cách sử dụng mệnh đề

SELECT * FROM myTable WHERE name='' OR '1'='1' 
33 với trình giữ chỗ giả chỉ bên trong nó. Điều gì sẽ xảy ra nếu bạn muốn sử dụng các trình giữ chỗ khác ở những nơi khác nhau?

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
88

Nhiều báo cáo đã chuẩn bị trong giao dịch

Điều này có vẻ kỳ lạ tại sao nó thậm chí sẽ đảm bảo phần riêng của nó, vì theo nghĩa đen, bạn có thể chỉ cần sử dụng lần lượt các câu lệnh đã chuẩn bị. Mặc dù điều này chắc chắn sẽ hoạt động, nhưng điều này không đảm bảo rằng các truy vấn của bạn là nguyên tử. Điều này có nghĩa là nếu bạn chạy mười truy vấn và một truy vấn không thành công thì chín truy vấn còn lại sẽ thành công. Nếu bạn muốn các truy vấn SQL của mình chỉ thực thi nếu tất cả chúng đều thành công, thì bạn phải sử dụng các giao dịch

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
89

Sử dụng lại cùng một mẫu, các giá trị khác nhau

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
0

Xử lý lỗi

Lỗi nghiêm trọng. Lỗi chưa xử lý. Gọi hàm thành viên bind_param() trên boolean

Bất kỳ ai đã sử dụng các câu lệnh được chuẩn bị sẵn của MySQLi đều đã thấy thông báo này vào một thời điểm nào đó, nhưng nó có nghĩa là gì? . Vì vậy, làm thế nào để bạn khắc phục điều này, bạn có thể yêu cầu?

Xử lý ngoại lệ

Tất cả các chức năng mysqli trả về false khi thất bại, vì vậy bạn có thể dễ dàng kiểm tra tính trung thực của từng chức năng và báo cáo lỗi với

SELECT * FROM myTable WHERE name='' OR '1'='1' 
37. Tuy nhiên, điều này rất tẻ nhạt và có một cách hay hơn để thực hiện việc này nếu bạn bật báo cáo nội bộ. Tôi khuyên bạn nên làm theo cách này, vì nó dễ mang theo hơn nhiều từ quá trình phát triển đến sản xuất

Điều này cũng có thể được sử dụng trong sản xuất, miễn là bạn đã thiết lập nhật ký lỗi cho tất cả các lỗi; . ban đầu. Vui lòng không bao giờ báo cáo lỗi trực tiếp trên trang web của bạn trong quá trình sản xuất. Bạn sẽ tự đá mình vì một sai lầm ngớ ngẩn như vậy. Vị trí của

SELECT * FROM myTable WHERE name='' OR '1'='1' 
21 cũng quan trọng. nếu bạn đặt nó trước khi tạo kết nối mới thì nó cũng sẽ xuất mật khẩu của bạn;

Đây là những gì php của bạn. ini sẽ giống như trong sản xuất. làm cả

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
08 và
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
09. Ngoài ra, hãy nhớ rằng mỗi trang thực sự chỉ nên sử dụng một khối
SELECT * FROM myTable WHERE name='' OR '1'='1' 
20 duy nhất, chung, thay vì gói từng truy vấn riêng lẻ. Ngoại lệ duy nhất cho điều này là với các giao dịch, sẽ được lồng vào nhau, nhưng đưa ra ngoại lệ của chính nó, do đó, toàn cầu
SELECT * FROM myTable WHERE name='' OR '1'='1' 
20 có thể "bắt" nó

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
1

Trình xử lý ngoại lệ tùy chỉnh

, bạn cũng có thể sử dụng

SELECT * FROM myTable WHERE name='' OR '1'='1' 
29 trên mỗi trang (hoặc chuyển hướng toàn cầu). Thao tác này sẽ loại bỏ lớp dấu ngoặc nhọn lồng vào nhau. Nếu bạn đang sử dụng các giao dịch, bạn vẫn nên sử dụng thử nắm bắt với điều đó, nhưng sau đó ném ngoại lệ của riêng bạn

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
2

Gotcha với Xử lý ngoại lệ

Bạn muốn tất cả các lỗi MySQLi được chuyển đổi thành ngoại lệ với

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
00. Thật kỳ lạ, tôi nhận thấy rằng nó vẫn đưa ra một lỗi cảnh báo khi
$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
13 có quá nhiều hoặc quá ít biến hoặc loại bị ràng buộc. Thông báo xuất ra như sau

Cảnh báo. mysqli_stmt. bind_param(). Số biến không khớp với số tham số trong câu lệnh đã chuẩn bị

Giải pháp cho vấn đề này là sử dụng trình xử lý lỗi toàn cầu để kích hoạt ngoại lệ. Một ví dụ về điều này có thể là

$mysqli = new mysqli("localhost", "username", "password", "databaseName");
if($mysqli->connect_error) {
  exit('Error connecting to database'); //Should be a message a typical user could understand in production
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli->set_charset("utf8mb4");
3

Điều này chỉ xảy ra trên các cảnh báo thời gian chạy, nhưng tôi đã chuyển đổi tất cả các lỗi thành ngoại lệ. Tôi thấy không có vấn đề gì khi làm điều này, nhưng có một số người phản đối mạnh mẽ

Tôi có cần $stmt->đóng() không?

Câu hỏi tuyệt vời. Cả

SELECT * FROM myTable WHERE name='' OR '1'='1' 
46 và
SELECT * FROM myTable WHERE name='' OR '1'='1' 
47 về cơ bản đều có tác dụng giống nhau. Cái trước đóng kết nối MySQLi, trong khi cái sau đóng câu lệnh đã chuẩn bị. TLDR; . Ngoài ra còn có một chức năng đơn giản là giải phóng bộ nhớ được liên kết với kết quả MySQLi và câu lệnh đã chuẩn bị, tương ứng.
SELECT * FROM myTable WHERE name='' OR '1'='1' 
48 và
SELECT * FROM myTable WHERE name='' OR '1'='1' 
49. Bản thân tôi, có thể sẽ không bao giờ sử dụng nó, nhưng nếu bạn quan tâm, đây là cái cho kết quả và cho truy vấn được tham số hóa. Những điều sau đây cũng cần được lưu ý. cả
SELECT * FROM myTable WHERE name='' OR '1'='1' 
47 và kết thúc thực thi tập lệnh sẽ giải phóng bộ nhớ

Phán quyết cuối cùng. Tôi thường chỉ làm

SELECT * FROM myTable WHERE name='' OR '1'='1' 
46 và
SELECT * FROM myTable WHERE name='' OR '1'='1' 
47, mặc dù có thể lập luận rằng điều đó hơi thừa. Nếu bạn dự định sử dụng lại cùng một biến
SELECT * FROM myTable WHERE name='' OR '1'='1' 
53 cho một câu lệnh đã chuẩn bị khác, thì bạn phải đóng biến đó hoặc sử dụng một tên biến khác, chẳng hạn như
SELECT * FROM myTable WHERE name='' OR '1'='1' 
54. Cuối cùng, tôi chưa bao giờ thấy cần phải giải phóng chúng mà không cần đóng chúng

Các lớp học. mysqli so với. mysqli_stmt so với. mysqli_result

Một điều bạn có thể đã nhận ra trong quá trình này là có một số phương thức nhất định tồn tại trong hai trong số các lớp, chẳng hạn như một bí danh gần như. Cá nhân tôi tin rằng sẽ tốt hơn nếu chỉ có một phiên bản, như trong PDO, để tránh nhầm lẫn

  • $name = $mysqli->real_escape_string($_POST['name']);
    $mysqli->query("SELECT * FROM myTable WHERE name='$name'");
    
    61 hoặc
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    56 - Thuộc về
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    57. Hoạt động tương tự với một trong hai, nhưng sẽ có lỗi nếu được gọi sau khi câu lệnh được đóng bằng một trong hai phương thức
  • $name = $mysqli->real_escape_string($_POST['name']);
    $mysqli->query("SELECT * FROM myTable WHERE name='$name'");
    
    65 hoặc
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    59 - Chỉ có thể sử dụng
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    60 với
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    02, trong khi
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    62 chỉ có thể sử dụng với
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    03
  • SELECT * FROM myTable WHERE name='' OR '1'='1' 
    64 hoặc
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    65 - Thuộc về
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    66. Tốt hơn nên sử dụng
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    67, vì nó vẫn hoạt động ngay cả sau khi sử dụng
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    47. Ngoài ra còn có một tài liệu PHP từ năm 2011 nói rằng
    SELECT * FROM myTable WHERE name='' OR '1'='1' 
    69 sẽ chỉ nhận truy vấn được thực hiện đầu tiên. Tôi đã thử điều này trên phiên bản 7 hiện tại của mình. 1 và đây dường như không phải là trường hợp. Dù sao thì phiên bản được khuyến nghị sử dụng là phiên bản lớp mysqli

Vì vậy, sử dụng báo cáo đã chuẩn bị có nghĩa là tôi an toàn trước những kẻ tấn công?

Mặc dù bạn an toàn trước SQL injection, nhưng bạn vẫn cần xác thực và làm sạch dữ liệu do người dùng nhập của mình. Bạn có thể sử dụng một chức năng như filter_var() để xác thực trước khi chèn nó vào cơ sở dữ liệu và htmlspecialchars() để khử trùng sau khi truy xuất nó

Làm cách nào để sử dụng câu lệnh đã chuẩn bị cho truy vấn cập nhật trong PHP?

Cách tạo câu lệnh chuẩn bị cho truy vấn CẬP NHẬT .
Xác định một mảng với các giá trị được phép
Lặp lại mảng nguồn và tự động tạo câu lệnh SET cho SQL, dựa trên danh sách các trường được phép
Các giá trị tương ứng phải được thêm vào mảng được chỉ định sẽ được sử dụng trong hàm thực thi ()

Làm cách nào để cập nhật MySQL bằng câu lệnh đã chuẩn bị?

Quy trình .
Gọi kết nối. Chuẩn bị phương pháp để tạo một đối tượng PreparedStatement
setXXX phương pháp để truyền giá trị cho các biến đầu vào. .
execUpdate để cập nhật bảng với các giá trị biến
phương thức đóng để đóng đối tượng PreparedStatement khi bạn sử dụng xong đối tượng đó

Làm cách nào để cập nhật trong PDO PHP?

Để chạy truy vấn CẬP NHẬT với PDO, chỉ cần làm theo các bước bên dưới. .
tạo một câu lệnh CẬP NHẬT SQL chính xác
thay thế tất cả các giá trị thực tế bằng trình giữ chỗ
chuẩn bị truy vấn kết quả
thực hiện câu lệnh, gửi tất cả các giá trị thực tế để thực thi() ở dạng mảng

Việc sử dụng chuẩn bị () trong PHP là gì?

Hàm chuẩn bị() / mysqli_prepare() được sử dụng để chuẩn bị thực thi câu lệnh SQL .