2009-11-04 32 views
15

Đối với một số báo cáo sql Tôi không thể sử dụng một Statment chuẩn bị, ví dụ:Làm thế nào để khử trùng SQL mà không sử dụng chuẩn bị phát biểu

SELECT MAX(AGE) FROM ? 

Ví dụ khi tôi muốn thay đổi bàn. Có một tiện ích sanitizes sql trong Java? Có một trong ruby.

+6

Nếu tên bảng đến ** trực tiếp ** từ người dùng nhập vào, bạn đã có vấn đề lớn hơn nhiều lo lắng về vệ sinh hơn SQL của bạn (và nếu nó không có gì để khử trùng là) – ChssPly76

Trả lời

2

Không thể. Tốt nhất những gì bạn có thể làm là sử dụng String#format().

String sql = "SELECT MAX(AGE) FROM %s"; 
sql = String.format(sql, tablename); 

Lưu ý rằng điều này không tránh rủi ro về SQL injection. Nếu tablename là giá trị do người dùng kiểm soát/khách hàng, bạn cần vệ sinh nó bằng cách sử dụng String#replaceAll().

tablename = tablename.replaceAll("[^\\w]", ""); 

Hy vọng điều này sẽ hữu ích.

[Chỉnh sửa] Tôi nên thêm: KHÔNG sử dụng tính năng này cho các giá trị cột nơi bạn có thể sử dụng PreparedStatement cho. Chỉ cần tiếp tục sử dụng nó theo cách thông thường cho bất kỳ giá trị cột nào. Tốt nhất là không cho phép người dùng/khách hàng có thể nhập tablename theo cách mà họ muốn, nhưng tốt hơn trình bày một dropdown có chứa tất cả các tablenames hợp lệ (mà bạn có thể có được bởi DatabaseMetaData#getCatalogs()) trong giao diện người dùng để người dùng/khách hàng có thể chọn nó. Đừng quên kiểm tra ở phía máy chủ nếu lựa chọn là hợp lệ bởi vì người ta có thể giả mạo các tham số yêu cầu.

+0

@BalusC - 1 cho tham chiếu SQL Injection. –

0

Trong trường hợp này, bạn có thể xác thực tên bảng dựa vào danh sách các bảng có sẵn, bằng cách lấy danh sách bảng từ DatabaseMetaData. Trong thực tế nó có lẽ sẽ dễ dàng hơn để sử dụng một regex để dải không gian, có lẽ cũng có một số từ dành riêng sql, ";", vv từ chuỗi trước khi sử dụng một cái gì đó liek String.format để xây dựng câu lệnh sql hoàn chỉnh của bạn.

Lý do bạn không thể sử dụng PrepareStatement là vì nó có thể đang đặt tên bảng trong '' và thoát nó như một chuỗi.

17

Các tham số truy vấn câu lệnh đúng, được chuẩn bị chỉ có thể được sử dụng ở nơi bạn sẽ sử dụng một giá trị bằng giá trị đơn. Bạn không thể sử dụng tham số cho tên bảng, tên cột, danh sách giá trị hoặc bất kỳ cú pháp SQL nào khác.

Vì vậy, bạn phải nội suy biến ứng dụng của bạn thành chuỗi SQL và trích dẫn chuỗi thích hợp. Đừng sử dụng trích dẫn để phân định tên bảng của bạn, và thoát khỏi chuỗi trích dẫn bằng cách nhân đôi nó:

java.sql.DatabaseMetaData md = conn.getMetaData(); 
String q = md.getIdentifierQuoteString(); 
String sql = "SELECT MAX(AGE) FROM %s%s%s"; 
sql = String.format(sql, q, tablename.replaceAll(q, q+q), q); 

Ví dụ, nếu tên bảng của bạn là theo nghĩa đen table"name, và RDBMS nhân vật nhận dạng báo giá của bạn là ", sau đó sql nên chứa một chuỗi như:

SELECT MAX(AGE) FROM "table""name" 

tôi cũng đồng ý với bình luận @ ChssPly76 - đó là tốt nhất nếu người dùng nhập vào của bạn là thực sự không phải là tên bảng đen, nhưng một signifier rằng mã của bạn bản đồ vào một tên bảng, mà bạn sau đó liên polate vào truy vấn SQL. Điều này mang lại cho bạn nhiều sự đảm bảo hơn rằng không có sự tiêm SQL nào có thể xảy ra.

HashMap h = new HashMap<String,String>(); 
/* user-friendly table name maps to actual, ugly table name */ 
h.put("accounts", "tbl_accounts123"); 

userTablename = ... /* user input */ 
if (h.containsKey(userTablename)) { 
    tablename = h.get(userTablename); 
} else { 
    throw ... /* Exception that user input is invalid */ 
} 
String sql = "SELECT MAX(AGE) FROM %s"; 
/* we know the table names are safe because we wrote them */ 
sql = String.format(sql, tablename); 
+0

+1 cho nhận xét của bạn về bản đồ mã. Đó chắc chắn là con đường để đi. Tất cả các tablenames có thể được lấy bởi DatabaseMetaData # getCatalogs() và được biểu diễn như một trình đơn thả xuống trong giao diện người dùng. – BalusC

+0

Nhưng nếu bạn đặt tên bảng thực trong trình đơn thả xuống, bạn vẫn nên sử dụng bản đồ để chuyển đổi đầu vào của người dùng thành tên bảng, vì đầu vào có thể bị giả mạo. Ví dụ. Tôi có thể nhập URL bằng bất kỳ thứ gì tôi muốn trong thông số yêu cầu, bất kể URL nào xuất hiện trong trình đơn thả xuống. Việc sử dụng bản đồ nhằm lọc đầu vào sau khi nó nhận được yêu cầu, không phải trước khi nó xuất ra biểu mẫu giao diện người dùng. –

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