Làm thế nào để SQL SQL từ truyện tranh Bobby Bobby Bảng XKCD hoạt động?


1093

Chỉ cần nhìn vào:

Dải XKCD (Nguồn: https://xkcd.com/327/ )

SQL này làm gì:

Robert'); DROP TABLE STUDENTS; --

Tôi biết cả hai '--là cho ý kiến, nhưng không phải từ này cũng DROPđược nhận xét vì nó là một phần của cùng một dòng?


16
Nếu bạn nghe Stack Overflow Podcast # 31 (27 tháng 11 năm 2008), họ thực sự thảo luận về điều này.
EBGreen

93
Trong MySQL, 'không phải để bình luận . Ngay cả khi nó là, không có không gian trước nó để nó chỉ có thể kết thúc chuỗi trước nó.
Các cuộc đua nhẹ nhàng trong quỹ đạo

45
Theo như XKCD, nếu có bất kỳ câu hỏi nào về một số truyện tranh bạn luôn có thể đi đến Giải thích XKCD và tìm ra câu trả lời của bạn. Thậm chí còn có một wiki XKCD , rất hữu ích cho một số truyện tranh phức tạp như XKCD geohashing
Anatoli

13
Tôi tin rằng liên kết này phải được ghi lại ở đây: bulk-tables.com
Arioch '

2
beta.compianceshouse.gov.uk/company/10542519 là đăng ký tư vấn có tên; BẢNG DROP "CÔNG TY"; - LTD
Alex Dupuy

Câu trả lời:


1116

Nó làm rơi bàn học sinh.

Mã ban đầu trong chương trình của trường có thể trông giống như

q = "INSERT INTO Students VALUES ('" + FNMName.Text + "', '" + LName.Text + "')";

Đây là cách ngây thơ để thêm văn bản nhập vào truy vấn và rất tệ , như bạn sẽ thấy.

Sau các giá trị từ tên đầu tiên, hộp văn bản tên đệm FNMName.Text (đó là Robert'); DROP TABLE STUDENTS; --) và hộp văn bản tên cuối cùng LName.Text (hãy gọi nó Derper) được nối với phần còn lại của truy vấn, kết quả thực sự là hai truy vấn được phân tách bằng kết thúc câu lệnh (dấu chấm phẩy). Truy vấn thứ hai đã được đưa vào đầu tiên. Khi mã thực hiện truy vấn này đối với cơ sở dữ liệu, nó sẽ trông như thế này

INSERT INTO Students VALUES ('Robert'); DROP TABLE Students; --', 'Derper')

trong đó, bằng tiếng Anh, gần như dịch sang hai truy vấn:

Thêm bản ghi mới vào bảng Sinh viên với giá trị Tên là 'Robert'

Xóa bảng sinh viên

Tất cả mọi thứ qua truy vấn thứ hai được đánh dấu là một nhận xét : --', 'Derper')

Tên 'của học sinh không phải là một nhận xét, đó là dấu phân cách chuỗi kết thúc . Vì tên của học sinh là một chuỗi, nên về mặt cú pháp cần hoàn thành truy vấn giả định. Các cuộc tấn công tiêm chỉ hoạt động khi truy vấn SQL mà họ tiêm kết quả trong SQL hợp lệ .

Thay đổi nội dung một lần nữa theo dan04 's bình luận sắc sảo


3
Mmm, WHERE với dấu ngoặc đơn xung quanh các đối số là khá bất thường, nhưng ít nhất nó cũng tránh được lỗi cú pháp ... :-)
PhiLho

60
@PhiLho: Nếu tuyên bố ban đầu là một INSERT, thì dấu ngoặc đơn sẽ có ý nghĩa hơn. Nó cũng sẽ giải thích tại sao kết nối cơ sở dữ liệu không ở chế độ chỉ đọc.
dan04

3
Như @ dan04 giải thích, dấu ngoặc đơn có ý nghĩa hơn với một INSERT. Nghĩ về phía sau, SELECTdù sao đi nữa , nó sẽ không chạy vì các Bảng của Bobby nhỏ trong bảng sẽ làm rơi bàn.
ypercubeᵀᴹ

10
Trên thực tế, trong ví dụ này, truy vấn đầu tiên ("thêm bản ghi mới ...") sẽ thất bại vì Studentsmong đợi nhiều hơn chỉ một cột (câu lệnh gốc / chính xác cung cấp hai cột). Điều đó nói rằng, sự hiện diện của cột thứ hai là hữu ích để cho thấy tại sao cần phải bình luận; và vì người ta không thể thay đổi tên của Bobby, nên có lẽ tốt nhất là để lại ít hơn so với quan sát này như một chú thích.
eggyal

7
Họ của Bobby - hoặc ít nhất là mẹ của anh ấy, là Roberts , theo Giải thích XKCD . Tuy nhiên, tôi không chắc chắn rằng việc sửa lỗi sẽ cải thiện sự rõ ràng của câu trả lời.
WBT

611

Giả sử tên được sử dụng trong một biến , $Name.

Sau đó, bạn chạy truy vấn này :

INSERT INTO Students VALUES ( '$Name' )

Mã bị đặt nhầm bất cứ thứ gì mà người dùng cung cấp làm biến.

Bạn muốn SQL là:

XÁC NHẬN VÀO GIÁ TRỊ Học sinh (' Robert Tables`)

Nhưng một người dùng thông minh có thể cung cấp bất cứ thứ gì họ muốn:

XÁC NHẬN VÀO GIÁ TRỊ Học sinh (' Robert'); DROP TABLE Students; --')

Những gì bạn nhận được là:

INSERT INTO Students VALUES ( 'Robert' );  DROP TABLE STUDENTS; --' )

Các --ý kiến ​​duy nhất phần còn lại của dòng.


87
Điều này tốt hơn nhiều so với bình chọn cao nhất, bởi vì nó giải thích dấu ngoặc đơn đóng.
Tim Büthe

1
Nhân tiện, không có cách nào để giám đốc trường trong truyện tranh nhận thức được hoặc XSS kể từ khi bảng học sinh bị xóa, anh ta không thể biết ai đã làm điều này.
xryl669

@ xryl669 Nhật ký rất hữu ích trong các tình huống như thế này ... Đôi khi tất cả các truy vấn được ghi lại, và đôi khi thông tin đăng nhập khác có thể giúp bạn suy ra thủ phạm.
inemanja

165

Như mọi người khác đã chỉ ra, ');đóng câu lệnh gốc và sau đó là câu lệnh thứ hai. Hầu hết các khung công tác, bao gồm các ngôn ngữ như PHP, hiện có cài đặt bảo mật mặc định không cho phép nhiều câu lệnh trong một chuỗi SQL. Ví dụ, trong PHP, bạn chỉ có thể chạy nhiều câu lệnh trong một chuỗi SQL bằng cách sử dụng mysqli_multi_queryhàm.

Tuy nhiên, bạn có thể thao tác một câu lệnh SQL hiện có thông qua SQL tiêm mà không cần phải thêm câu lệnh thứ hai. Giả sử bạn có một hệ thống đăng nhập để kiểm tra tên người dùng và mật khẩu với lựa chọn đơn giản này:

$query="SELECT * FROM users WHERE username='" . $_REQUEST['user'] . "' and (password='".$_REQUEST['pass']."')";
$result=mysql_query($query);

Nếu bạn cung cấp peterlàm tên người dùng và secretmật khẩu, chuỗi SQL kết quả sẽ trông như thế này:

SELECT * FROM users WHERE username='peter' and (password='secret')

Mọi thứ đều ổn. Bây giờ hãy tưởng tượng bạn cung cấp chuỗi này làm mật khẩu:

' OR '1'='1

Sau đó, chuỗi SQL kết quả sẽ là:

SELECT * FROM users WHERE username='peter' and (password='' OR '1'='1')

Điều đó sẽ cho phép bạn đăng nhập vào bất kỳ tài khoản nào mà không cần biết mật khẩu. Vì vậy, bạn không cần phải có thể sử dụng hai câu lệnh để sử dụng SQL SQL, mặc dù bạn có thể làm nhiều việc phá hoại hơn nếu bạn có thể cung cấp nhiều câu lệnh.


71

Không, 'không phải là một nhận xét trong SQL, mà là một dấu phân cách.

Mẹ cho rằng lập trình viên cơ sở dữ liệu đưa ra yêu cầu như sau:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('$firstName', '$lastName');

(ví dụ) để thêm sinh viên mới, trong đó $xxxnội dung biến được lấy trực tiếp từ một biểu mẫu HTML, mà không kiểm tra định dạng cũng như không thoát các ký tự đặc biệt.

Vì vậy, nếu $firstNamechứa Robert'); DROP TABLE students; --chương trình cơ sở dữ liệu sẽ thực hiện yêu cầu sau trực tiếp trên DB:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('Robert'); DROP TABLE students; --', 'XKCD');

I E. nó sẽ chấm dứt sớm câu lệnh chèn, thực thi bất kỳ mã độc hại nào mà trình bẻ khóa muốn, sau đó nhận xét bất kỳ phần còn lại nào của mã có thể có.

Mmm, tôi quá chậm, tôi đã thấy 8 câu trả lời trước câu hỏi của tôi trong ban nhạc màu cam ... :-) Một chủ đề phổ biến, dường như.


39

TL; DR

- Ứng dụng chấp nhận đầu vào, trong trường hợp này là 'Nancy', mà không cố gắng - vệ sinh đầu vào, chẳng hạn như bằng cách thoát 
trường ký tự đặc biệt => XÁC NHẬN VÀO học sinh GIÁ TRỊ ( 'Nancy' ); XÁC NHẬN 0 1
   
  

- Lỗi SQL xảy ra khi đầu vào vào lệnh cơ sở dữ liệu bị thao túng - khiến máy chủ cơ sở dữ liệu thực thi 
trường SQL tùy ý => XÁC NHẬN VÀO sinh viên GIÁ TRỊ ( 'Robert' ); Học sinh DROP BẢNG ; - '); INSERT 0 1 thả TABLE
      
  
 

- Hồ sơ học sinh đã biến mất - nó thậm chí còn tồi tệ hơn! 
trường học => CHỌN * TỪ học sinh ; 
LỖI :   mối quan hệ "sinh viên" không không tồn tại   
LINE 1 : CHỌN * TỪ học sinh ; ^   
                      

Điều này làm giảm (xóa) bảng sinh viên.

( Tất cả các ví dụ mã trong câu trả lời này đã được chạy trên máy chủ cơ sở dữ liệu PostgreSQL 9.1.2. )

Để làm rõ những gì đang xảy ra, hãy thử điều này với một bảng đơn giản chỉ chứa trường tên và thêm một hàng:

trường => CREATE TABLE sinh viên ( tên TEXT TIỂU KEY ); 
THÔNG BÁO : CREATE TABLE / TIỂU KEY sẽ tạo tiềm ẩn chỉ số "students_pkey" cho bảng "sinh viên" CREATE TABLE 
trường             
  => CHÈN VÀO sinh viên GIÁ TRỊ ( 'John' ); XÁC NHẬN 0 1   
  

Giả sử ứng dụng sử dụng SQL sau để chèn dữ liệu vào bảng:

XÁC NHẬN VÀO sinh viên GIÁ TRỊ ( 'foobar' );  

Thay thế foobarbằng tên thật của học sinh. Một hoạt động chèn bình thường sẽ trông như thế này:

- Đầu vào: 
trường Nancy => XÁC NHẬN VÀO học sinh GIÁ TRỊ ( 'Nancy' ); XÁC NHẬN 0 1   
  

Khi chúng tôi truy vấn bảng, chúng tôi nhận được điều này:

trường học => CHỌN * TỪ học sinh ;   
 Tên
-------
 John
 Nancy
( 2 hàng ) 

Điều gì xảy ra khi chúng ta chèn tên của Little Bobby Table vào bảng?

- Đầu vào: Robert '); Học sinh DROP BẢNG; - 
trường học => XÁC NHẬN VÀO sinh viên GIÁ TRỊ ( 'Robert' ); Học sinh DROP BẢNG ; - ');      
INSERT 0 1 thả TABLE  
 

Việc tiêm SQL ở đây là kết quả của tên của sinh viên chấm dứt câu lệnh và bao gồm một DROP TABLElệnh riêng ; hai dấu gạch ngang ở cuối đầu vào được dự định để nhận xét bất kỳ mã còn sót lại nào có thể gây ra lỗi. Dòng cuối cùng của đầu ra xác nhận rằng máy chủ cơ sở dữ liệu đã bỏ bảng.

Điều quan trọng cần lưu ý là trong quá trình INSERThoạt động, ứng dụng không kiểm tra đầu vào cho bất kỳ ký tự đặc biệt nào và do đó cho phép nhập dữ liệu tùy ý vào lệnh SQL. Điều này có nghĩa là người dùng độc hại có thể chèn, vào một trường thường dành cho đầu vào của người dùng, các ký hiệu đặc biệt như dấu ngoặc kép cùng với mã SQL tùy ý để khiến hệ thống cơ sở dữ liệu thực thi nó, do đó SQL bị  tiêm .

Kết quả?

trường học => CHỌN * TỪ học sinh ;   
LỖI :   mối quan hệ "sinh viên" không không tồn tại
LINE 1 : CHỌN *   TỪ học sinh ; ^
                      

SQL tiêm là cơ sở dữ liệu tương đương với lỗ hổng thực thi mã tùy ý từ xa trong một hệ điều hành hoặc ứng dụng. Tác động tiềm tàng của một cuộc tấn công SQL SQL thành công không thể được đánh giá thấp - tùy thuộc vào hệ thống cơ sở dữ liệu và cấu hình ứng dụng, kẻ tấn công có thể sử dụng nó để gây mất dữ liệu (như trong trường hợp này), truy cập trái phép vào dữ liệu hoặc thậm chí thực thi mã tùy ý trên chính máy chủ.

Như truyện tranh XKCD đã lưu ý, một cách để bảo vệ chống lại các cuộc tấn công SQL SQL là vệ sinh các đầu vào cơ sở dữ liệu, chẳng hạn như thoát các ký tự đặc biệt, để chúng không thể sửa đổi lệnh SQL cơ bản và do đó không thể thực thi mã SQL tùy ý. Nếu bạn sử dụng các truy vấn được tham số hóa, chẳng hạn như bằng cách sử dụng SqlParametertrong ADO.NET, tối thiểu, đầu vào sẽ được tự động vệ sinh để bảo vệ chống lại việc tiêm SQL.

Tuy nhiên, vệ sinh đầu vào ở cấp ứng dụng có thể không dừng các kỹ thuật tiêm SQL nâng cao hơn. Ví dụ, có nhiều cách để phá vỡ mysql_real_escape_stringhàm PHP . Để bảo vệ thêm, nhiều hệ thống cơ sở dữ liệu hỗ trợ các câu lệnh được chuẩn bị . Nếu được triển khai đúng cách trong phần phụ trợ, các câu lệnh được chuẩn bị có thể khiến SQL tiêm không thể thực hiện được bằng cách xử lý các đầu vào dữ liệu tách biệt về mặt ngữ nghĩa với phần còn lại của lệnh.


30

Nói rằng bạn ngây thơ đã viết một phương pháp sáng tạo sinh viên như thế này:

void createStudent(String name) {
    database.execute("INSERT INTO students (name) VALUES ('" + name + "')");
}

Và ai đó nhập tên Robert'); DROP TABLE STUDENTS; --

Những gì được chạy trên cơ sở dữ liệu là truy vấn này:

INSERT INTO students (name) VALUES ('Robert'); DROP TABLE STUDENTS --')

Dấu chấm phẩy kết thúc lệnh chèn và bắt đầu một dấu chấm khác; các - ý kiến ​​ra phần còn lại của dòng. Lệnh DROP TABLE được thực thi ...

Đây là lý do tại sao các tham số ràng buộc là một điều tốt.


26

Một trích dẫn là bắt đầu và kết thúc của một chuỗi. Dấu chấm phẩy là kết thúc của một tuyên bố. Vì vậy, nếu họ đang thực hiện một lựa chọn như thế này:

Select *
From Students
Where (Name = '<NameGetsInsertedHere>')

SQL sẽ trở thành:

Select *
From Students
Where (Name = 'Robert'); DROP TABLE STUDENTS; --')
--             ^-------------------------------^

Trên một số hệ thống, selectsẽ được chạy đầu tiên theo sau bởi droptuyên bố! Thông báo là: KHÔNG ĐƯỢC GIÁ TRỊ VÀO SQL CỦA BẠN. Thay vào đó hãy sử dụng các tham số!


18

Kết ');thúc truy vấn, nó không bắt đầu một bình luận. Sau đó, nó thả bảng sinh viên và nhận xét phần còn lại của truy vấn được cho là được thực thi.


17

Người viết cơ sở dữ liệu có lẽ đã làm một

sql = "SELECT * FROM STUDENTS WHERE (STUDENT_NAME = '" + student_name + "') AND other stuff";
execute(sql);

Nếu student_name là cái được đưa ra, thì đó là lựa chọn có tên "Robert" và sau đó bỏ bảng. Phần "-" thay đổi phần còn lại của truy vấn đã cho thành một nhận xét.


Đó là suy nghĩ đầu tiên của tôi, nhưng bạn có một lỗi cú pháp với dấu ngoặc đơn đóng dấu, không?
PhiLho

3
Đó là lý do tại sao có một - ở cuối, chỉ ra văn bản còn lại là một bình luận và nên được bỏ qua.

17

Trong trường hợp này, 'không phải là một nhân vật bình luận. Nó được sử dụng để phân định chuỗi ký tự. Họa sĩ truyện tranh đang nghiên cứu về ý tưởng rằng ngôi trường đang nói đến có hình vuông động ở đâu đó trông giống như thế này:

$sql = "INSERT INTO `Students` (FirstName, LastName) VALUES ('" . $fname . "', '" . $lname . "')";

Vì vậy, bây giờ ký tự 'kết thúc chuỗi ký tự trước khi lập trình viên mong đợi nó. Kết hợp với; để kết thúc câu lệnh, một kẻ tấn công giờ đây có thể thêm bất cứ thứ gì họ muốn. Nhận xét - ở cuối là để đảm bảo mọi sql còn lại trong câu lệnh gốc không ngăn truy vấn biên dịch trên máy chủ.

FWIW, tôi cũng nghĩ rằng truyện tranh trong câu hỏi có một chi tiết quan trọng sai: nếu bạn đang nghĩ về việc vệ sinh các đầu vào cơ sở dữ liệu của bạn, như truyện tranh gợi ý, bạn vẫn đang làm sai. Thay vào đó, bạn nên suy nghĩ về việc cách ly các đầu vào cơ sở dữ liệu của bạn và cách chính xác để thực hiện việc này là thông qua các truy vấn được tham số hóa.


16

'tự trong SQL được sử dụng cho các hằng chuỗi. Trong trường hợp này, nó được sử dụng để kết thúc chuỗi không đổi và không cho nhận xét.


7

Đây là cách nó hoạt động: Giả sử quản trị viên đang tìm kiếm hồ sơ của sinh viên

Robert'); DROP TABLE STUDENTS; --

Vì tài khoản quản trị viên có đặc quyền cao nên việc xóa bảng khỏi tài khoản này là có thể.

Mã để lấy tên người dùng từ yêu cầu là

Bây giờ truy vấn sẽ là một cái gì đó như thế này (để tìm kiếm bảng sinh viên)

String query="Select * from student where username='"+student_name+"'";

statement.executeQuery(query); //Rest of the code follows

Truy vấn kết quả trở thành

Select * from student where username='Robert'); DROP TABLE STUDENTS; --

Do đầu vào của người dùng không được vệ sinh, nên truy vấn trên được thao tác thành 2 phần

Select * from student where username='Robert'); 

DROP TABLE STUDENTS; --

Dấu gạch ngang kép (-) sẽ chỉ nhận xét phần còn lại của truy vấn.

Điều này là nguy hiểm vì nó có thể vô hiệu hóa xác thực mật khẩu, nếu có

Người đầu tiên sẽ thực hiện tìm kiếm bình thường.

Người thứ hai sẽ bỏ học sinh bảng nếu tài khoản có đủ đặc quyền (Nói chung, tài khoản quản trị viên của trường sẽ chạy truy vấn đó và sẽ có các đặc quyền được nói ở trên).


SELECT* FROM sutdents ...- bạn đã quên một "s". Đây là những gì bạn thả. DROP TABLE STUDENTS;
DevWL

4

Bạn không cần nhập dữ liệu biểu mẫu để thực hiện SQL tiêm.

Không ai chỉ ra điều này trước đây vì vậy tôi có thể cảnh báo một số bạn.

Chủ yếu chúng tôi sẽ cố gắng vá các hình thức đầu vào. Nhưng đây không phải là nơi duy nhất bạn có thể bị tấn công bằng SQL tiêm. Bạn có thể thực hiện tấn công rất đơn giản với URL gửi dữ liệu thông qua yêu cầu GET; Hãy xem xét ví dụ về sự sụp đổ:

<a href="/show?id=1">show something</a>

Url của bạn sẽ trông http://yoursite.com/show?id=1

Bây giờ ai đó có thể thử một cái gì đó như thế này

http://yoursite.com/show?id=1;TRUNCATE table_name

Cố gắng thay thế tên_bảng bằng tên bảng thực. Nếu anh ta lấy đúng tên bảng của bạn, họ sẽ làm trống bàn của bạn! (Rất dễ dàng để đánh bại URL này bằng tập lệnh đơn giản)

Truy vấn của bạn sẽ trông giống như thế này ...

"SELECT * FROM page WHERE id = 4;TRUNCATE page"

Ví dụ về mã dễ bị tổn thương PHP khi sử dụng PDO:

<?php
...
$id = $_GET['id'];

$pdo = new PDO($database_dsn, $database_user, $database_pass);
$query = "SELECT * FROM page WHERE id = {$id}";
$stmt = $pdo->query($query);
$data = $stmt->fetch(); 
/************* You have lost your data!!! :( *************/
...

Giải pháp - sử dụng các phương thức PDO chuẩn bị () & bindParam ():

<?php
...
$id = $_GET['id'];

$query = 'SELECT * FROM page WHERE id = :idVal';
$stmt = $pdo->prepare($query);
$stmt->bindParam('idVal', $id, PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetch();
/************* Your data is safe! :) *************/
...
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.