2012-03-11 28 views
10

Tôi đang tạo một số SQL động và muốn đảm bảo rằng mã của tôi được an toàn từ SQL injection.Vệ sinh tên bảng/cột trong SQL động trong .NET? (Ngăn chặn các cuộc tấn công SQL injection)

Đối với lợi ích của lập luận đây là một ví dụ nhỏ về cách nó được tạo ra:

var sql = string.Format("INSERT INTO {0} ({1}) VALUES (@value)", 
    tableName, columnName); 

Ở phía trên, tableName, columnName, và bất cứ điều gì là ràng buộc để @value đến từ một nguồn không tin cậy. Vì trình giữ chỗ đang được sử dụng @value là an toàn từ các cuộc tấn công SQL injection và có thể bỏ qua. (Các lệnh được thực thi thông qua SqlCommand.)

Tuy nhiên, tableNamecolumnNamekhông thể bị ràng buộc như placeholders và do dễ bị tổn thương để tấn công tiêm. Vì đây là một kịch bản "thực sự năng động", không có danh sách trắng nào cho số tableName hoặc columnName.

Câu hỏi đặt ra là như sau:

Có một tiêu chuẩn , built-in cách để kiểm tra và/hoặc khử trùng tableNamecolumnName? (SqlConnection, hoặc một lớp trợ giúp, vv) Nếu không, cách tốt nhất để thực hiện nhiệm vụ này là mà không cần sử dụng thư viện của bên thứ ba là gì?

Ghi chú:

  • Tất cả các định danh SQL, bao gồm các lược đồ, nên bằng cách chấp nhận: ví dụ: [schema].[My Table].column chỉ là "an toàn" là table1.
  • Có thể khử trùng số nhận dạng hoặc phát hiện số nhận dạng không hợp lệ. (Nó không cần phải đảm bảo rằng các bảng/cột là thực sự có giá trị trong bối cảnh; SQL kết quả có thể là không hợp lệ, nhưng phải "an toàn".)

Cập nhật:

Chỉ cần tìm thấy điều này, và nghĩ rằng nó có phần thú vị: Có một hàm SqlFunctions.QuoteName trong .NET4 (EF4?). Ok, nó không thực sự giúp tôi ở đây ...

Trả lời

5

Vì bạn đang sử dụng SqlConnection, giả thiết là đây là cơ sở dữ liệu SQL Server.

Giả sử rằng bạn có thể xác thực tên bảng và trường bằng cụm từ thông dụng tuân theo quy tắc định danh SQL Server như được định nghĩa in MSDN. Trong khi tôi là một người mới hoàn thành và hoàn toàn ở biểu thức thông thường, tôi đã tìm thấy một này mà nên đến gần:

[\p{L}{\p{Nd}}$#_][\p{L}{\p{Nd}}@$#_]* 

Tuy nhiên, một biểu thức chính quy sẽ không giải quyết máy chủ từ khóa SQL và nó không đảm bảo rằng bàn và/hoặc cột thực sự tồn tại (mặc dù bạn đã chỉ ra rằng đó không phải là vấn đề lớn).

Nếu đây là ứng dụng của tôi, trước hết tôi sẽ đảm bảo rằng người dùng cuối không cố gắng thực hiện tiêm bằng cách từ chối mọi yêu cầu chứa dấu chấm phẩy (;).

Tiếp theo, tôi sẽ xác thực sự tồn tại của bảng bằng cách xóa các dấu phân tách tên hợp lệ (", ', [,]), tách tên bảng theo một khoảng thời gian để xem lược đồ đã được chỉ định chưa và thực hiện truy vấn đối với INFORMATION_SCHEMA. bẢNG để xác định sự tồn tại của bảng

Ví dụ:..

SELECT 1 
FROM INFORMATION_SCHEMA.TABLES 
WHERE TABLE_NAME = 'tablename' 
AND TABLE_SCHEMA = 'tableschema' 

Nếu bạn tạo truy vấn này sử dụng các thông số, sau đó bạn nên tiếp tục bảo vệ mình khỏi tiêm

Cuối cùng, tôi sẽ xác nhận sự tồn tại của mỗi tên cột bằng cách thực hiện một loạt các bước tương tự, chỉ sử dụng INFORMATION_SCHEMA.COLUMNS để xác định tính hợp lệ của (các) cột khi bảng đã được xác định là hợp lệ.

Tôi có thể lấy danh sách các cột hợp lệ cho bảng này từ SQL Server, sau đó xác minh rằng mỗi cột yêu cầu nằm trong danh sách trong mã của tôi. Bằng cách đó, bạn có thể cho biết chính xác cột nào bị lỗi và cung cấp phản hồi đó cho người dùng.

+0

Bạn chính xác, đó thực sự là SQL Server. Tôi đang hướng tới một tuyến đường biểu hiện thường xuyên * tối thiểu * được chấp nhận, nhưng tôi đã hy vọng rằng một cái gì đó premade (và thử nghiệm ;-) tồn tại. Tôi thực sự thích ý tưởng bổ sung về việc kiểm tra nó dựa vào siêu dữ liệu lược đồ. –

+0

Nếu bạn sử dụng truy vấn tham số để kiểm tra bảng/lược đồ, hãy kiểm tra sự tồn tại của từng tên cột trong mã với danh sách đầy đủ các tên cột cho bảng, sau đó bạn thực sự không cần thực hiện bất kỳ kiểm tra tính hợp lệ nào trên các giá trị đến , đó sẽ là tối ưu trong mức tối thiểu :). –

+0

Vâng, đó là triển khai * nhỏ * lớn hơn :) –

20

Tôi không chắc chắn nếu bạn vẫn đang xem xét điều này, nhưng lớp DbCommandBuilder cung cấp phương thức QuoteIdentifier cho mục đích này. Những lợi ích chính của việc này là nó độc lập với cơ sở dữ liệu và không liên quan đến bất kỳ mớ hỗn độn RegEx nào.

Tính đến .NET 4.5, bạn có mọi thứ bạn cần để khử trùng bảng và cột tên chỉ sử dụng đối tượng DbConnection của bạn:

DbConnection connection = GetMyConnection(); // Could be SqlConnection 
DbProviderFactory factory = DbProviderFactories.GetFactory(connection); 

// Sanitize the table name 
DbCommandBuilder commandBuilder = factory.CreateCommandBuilder(); 

string tableName = "This Table Name Is Long And Bad"; 
string sanitizedTableName = commandBuilder.QuoteIdentifier(tableName); 

IDbCommand command = connection.CreateCommand(); 
command.CommandText = "SELECT * FROM " + sanitizedTableName; 

// Becomes 'SELECT * FROM [This Table Name Is Long And Bad]' in MS-SQL, 
// 'SELECT * FROM "This Table Name Is Long And Bad"' in Oracle, etc. 

(Pre-4.5, bạn sẽ cần một số cách khác để có được DbProviderFactory của bạn . - có lẽ từ tên nhà cung cấp dữ liệu trong cấu hình ứng dụng của bạn hoặc mã hóa cứng ở đâu đó)

+0

Cảm ơn, tôi thậm chí không biết lớp đó đã tồn tại. Tôi vẫn đang sử dụng .NET3.5 tuy nhiên, do đó, không có nhà máy trợ giúp ma thuật ở đó: (Tôi chắc chắn nó sẽ được sử dụng cho người khác, mặc dù (hoặc có lẽ đối với tôi trong một vài năm) –

+0

Ồ, bạn vẫn có thể làm điều đó trong .NET 3.5, bạn chỉ cần một số cách khác để có được bàn tay của bạn trên một đối tượng 'DbProviderFactory' hoặc thậm chí chỉ cần tạo một' DbCommandBuilder' theo cách thủ công.Nếu bạn chắc chắn bạn sẽ luôn sử dụng MS-SQL, bạn chỉ có thể làm 'DbCommandBuilder commandBuilder = new SqlCommandBuilder();' và bỏ qua tất cả các mess của nhà máy. –

+0

Ahh, vâng. Điều đó làm cho tinh thần - Tôi đang sử dụng LINQ2SQL vì vậy .. yeah, luôn luôn SQL Server :) –

1

Đối với SQL server, nó khá đơn giản để khử trùng một định danh:

// To make a string safe to use as an SQL identifier : 
// 1. Escape single closing bracket with double closing bracket 
// 2. Wrap in square brackets 
string.Format("[{0}]", identifier.Replace("]", "]]")); 

Sau khi được bọc trong các dấu ngoặc và thoát, điều duy nhất không hoạt động như một mã định danh là một chuỗi rỗng/rỗng.

+0

Tôi không biết tại sao bạn lại bị downvoted, vì đây chính xác là những gì 'DbCommandBuilder.QuoteIdentifier' thực hiện. – Stijn

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