Security
1.SQL Injection
SQL Injection là một trong những kiểu hack web đang dần trở nên phổ biến hiện nay. Bằng cách inject các mã SQL query/command vào input trước khi chuyển cho ứng dụng web xử lí, bạn có thể login mà không cần username và password
1. SQL Injection là gì?
SQL injection là một kỹ thuật cho phép những kẻ tấn công lợi dụng lỗ hổng của việc kiểm tra dữ liệu đầu vào trong các ứng dụng web và các thông báo lỗi của hệ quản trị cơ sở dữ liệu trả về để inject (tiêm vào) và thi hành các câu lệnh SQL bất hợp pháp. SQL injection có thể cho phép những kẻ tấn công thực hiện các thao tác, delete, insert, update, v.v. trên cơ sở dữ liệu của ứng dụng, thậm chí là server mà ứng dụng đó đang chạy. SQL injection thường được biết đến như là một vật trung gian tấn công trên các ứng dụng web có dữ liệu được quản lý bằng các hệ quản trị cơ sở dữ liệu như SQL Server, MySQL, Oracle, DB2, Sysbase...
Các kiểu tấn công phổ thông
-Tấn công bằng mệnh đề luôn đúng
Những lỗi lập trình này thường xảy ra ở các chức năng tìm kiếm hoặc đăng nhập tài khoản. Ví dụ một câu lệnh SQL như sau:
SELECT * FROM accounts WHERE userName = '{$userName}'
Câu lệnh này đầy sơ hở, chắc là ít người có thể liều lĩnh mà code như vậy, tuy nhiên thực tế thì nó cũng gần như thế mà thôi, rất nhiều trang mắc lỗi này, giờ chúng ta thử xem hacker sẽ thêm cái gì:
' or '1' = '1'
Thì khi truy vấn, câu lệnh của chúng ta sẽ trở thành mệnh đề luôn đúng như dưới đây
SELECT * FROM accounts WHERE userName = '{$userName}' OR '1' = '1'
Câu lệnh này có vế luôn trả về đúng, cho nên câu lệnh luôn thực hiện được, trong trường hợp này thì tài khoản bị mất là cái chắc chắn, hoặc hacker sẽ đăng nhập như admin mà nghịch ngợm phá hoại …
-Tấn công phá hoại dữ liệu
Giả sử câu lệnh cũng như trên, nhưng không nhập ‘1’ = ‘1’ nữa mà thêm như sau:
SELECT * FROM accounts WHERE userName = '{$userName}'; DROP TABLE accounts; ''
Dấu ( ; ) dùng để phân tách câu lệnh trong SQL, điều này đồng nghĩa với việc hacker có thể hành động thêm bất kì lệnh gì vào trong câu lệnh trên, rất nguy hiểm, nếu các bạn không có bản backup thì nguy to.
Phòng chống SQl Injection
Hầu hết các mã nguồn mở bây giờ đều không quá lo về lỗi này, tuy nhiên plugin hay module được viết bởi nhiều lập trình viên khác nhau lại hay để ra sơ hở tạo điều kiện cho lỗi phát sinh.
Cách phòng tránh thì có nhiều cách, trong đó:
1. Viết lại đường dẫn
Viết lại đường dẫn của website có thể phòng được lỗi SQL Injection, vì hacker thường phải tìm một sơ hở của website trên đường link hoặc trên form nhập liệu của người dùng, khi không nhìn thấy biến query thì hacker phải tìm cách khác để thử lỗi.
2. Lọc kĩ những gì người dùng nhập
Bài học là đừng bao giờ tin người dùng, kể cả bản thân sẽ giúp bạn phòng lỗi này cực kì hiệu quả, giả dụ như sau:
Với số người dùng nhập vào, để ăn chắc các bạn lọc qua các hàm như:
$num = ( int ) $_POST['num'];
$num = is_numeric ( $num ) ? $num : 0;
Ví dụ sử dụng sprinf:
$data = sprintf('select * from %s where data = %d limit 1', $table, $must_be_number );
Sử dụng kết hợp với mysql_real_escape_string() để câu SQL được an toàn:
$data = sprintf('select * from %s where data = %d limit 1', $table, mysql_real_escape_string($must_be_number ) );
Với các cách trên, bạn có thể yên tâm là câu lệnh của mình khó bị bóp méo qua lỗi SQL Injection !
Tổng kết
Lỗi SQL Injection là lỗi rất phổ thông, tuy nhiên phòng chống nó là việc hoàn toàn không khó, chỉ cần bạn luôn tự nhủ là với những gì người dùng nhập ta không thể tin bất kì điều gì, phải xác định lại chúng hoặc sử dụng phương pháp ép kiểu hoặc thêm dấu “” vào trong biến người dùng nhập ( addslashes() ) sẽ hạn chế tối đa lỗi này !
Với các mã nguồn mở, chúng ta có thể an tâm phần nào vì hầu hết các câu query đều được lọc qua lọc lại một cách kĩ lưỡng, chỉ phát sinh lỗi này khi chúng ta viết không đúng quy chuẩn mà thôi, các bạn cũng nên để ý sử dụng plugin và module của người dùng xác định được download trên trang chủ nhé !
2. Statements
-Khi làm việc với JDBC, bạn không thể không biết đến các interface hỗ trợ cho quá trình khai thác và lưu trữ thông tin trên cơ sở dữ liệu của chương trình chính là Statement,PreparedStatement và CallableStatement. Tùy theo nhu cầu, bạn có thể tạo ra các đối tượng của một trong các interface trên để thực hiện nhu cầu khai thác thông tin của chương trình tại thời điểm cần thiết.
-Việc sử dụng các câu lệnh SQL để truyền giữa client và database server sẽ làm cho lưu lượng truyền thông trên mạng rất lớn. Điều này làm tăng traffic trên mạng dẫn đến tốc độ thực thi của chương trình giảm, tốn kém tài nguyên tại database server vì cứ luôn phải cấp phát cho việc tạo và giải phóng bộ nhớ khi thực thi các lệnh SQL, cho dù là lệnh đó được thực thi nhiều lần.
-Stored procedure là giải pháp tốt cho tình huống này, việc sử dụng stored procedure để hỗ trợ cho quá trình xử lý thông tin trong database tỏ ra khá hiệu quả bởi 1 số ưu điểm sau:
+Có thể gọi thực thi nhiều công việc tác động lên Database chỉ thông qua 1 cái tên.
+Giảm traffic do chỉ truyền 1 chuỗi ký tự ngắn chính là tên của stored procedure mà không phải truyền đi về những câu lệnh SQL dài dòng, đôi khi lên tới cả trăm ký tự.
+Stored procedure được tạo và đặt tại database server và việc khai thác chỉ thông qua tên của nó cho nên có thể ngăn ngừa được những kẻ tò mò muốn biết chương trình của chúng ta đang tác động như thế nào lên Database.
+Stored procedure được biên dịch trước và tồn tại mãi trong quá trình DBMS hoạt động nên khả năng thực thi và hồi đáp thông tin cho client rất nhanh, tăng hiệu suất thực thi của chương trình. Stored procedure được tái sử dụng nhiều lần.
+Tận dụng việc thực thi và giải quyến các yêu cầu của chương trình ở đồng thời cả 2 nơi :client – server. Vì thế tăng hiệu suất thực thi chương trình.
-Stored procedure có nhiều ưu điểm như thế, vậy để gọi sử dụng các stored procedure trong Database ta sẽ dùng đối tượng nào ? Chắc các bạn cũng đã đoán ra, đúng thế Java cung cấp cho chúng ta interface CallableStatement để có thể gọi các stored proceduretrong Database.
II. Hướng dẫn sử dụng Statement, PreparedStatement và CallableStatement
Trong hướng dẫn này tôi sẽ dùng 1 database có sẵn với 1 bảng tên là Logon có các cột UserName(primary key), Password, FullName, Role có phát sinh đoạn coad như sau:
CREATE TABLE [dbo].[Logon]( [UserName] [varchar](50) NOT NULL PRIMARY KEY, [Password] [varchar](50) NOT NULL, [FullName] [varchar](50) NOT NULL, [Role] [varchar](50) NOT NULL, )
Trường hợp 1: Câu truy vấn dữ liệu.
1.Sử dụng Statement
Để sử dụng đối tượng của interface Statement:
_Tạo 1 thể hiện của interface này thông qua phương thức createStatement() của đối tượng Connection.
_Tạo 1 câu truy vấn đến DB.
_Gọi phương thức executeQuery() để trả về kết quả là 1 ResultSet.
Như vậy, ta xây dựng 1 hàm như sau:
1.Sử dụng Statement
Để sử dụng đối tượng của interface Statement:
_Tạo 1 thể hiện của interface này thông qua phương thức createStatement() của đối tượng Connection.
_Tạo 1 câu truy vấn đến DB.
_Gọi phương thức executeQuery() để trả về kết quả là 1 ResultSet.
Như vậy, ta xây dựng 1 hàm như sau:
public ResultSet getAccounts() throws Exception{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;database=Sinhvien", "sa", "");
String strSQL = "Select * from Logon";
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(strSQL);
return rs;
}
2. Sử dụng PreparedStatement
Để sử dụng đối tượng của interface PreparedStatement:
_Tạo 1 thể hiện của interface này thông qua phương thức prepareStatement() của đối tượng Connection.
_Tạo 1 câu truy vấn đến DB
_Gọi phương thức executeQuery() để trả về kết quả là 1 ResultSet.
Để sử dụng đối tượng của interface PreparedStatement:
_Tạo 1 thể hiện của interface này thông qua phương thức prepareStatement() của đối tượng Connection.
_Tạo 1 câu truy vấn đến DB
_Gọi phương thức executeQuery() để trả về kết quả là 1 ResultSet.
public ResultSet getAccounts(String role) throws Exception{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;database=Sinhvien", "sa", "");
String strSQL = "Select * from Logon";
PreparedStatement ps = con.prepareStatement(strSQL);
ResultSet rs = ps.executeQuery();
return rs;
}
3. Sử dụng CallableStatement
Để sử dụng đối tượng của interface CallableStatement:
_Tạo 1 Store Procedure như sau:
Để sử dụng đối tượng của interface CallableStatement:
_Tạo 1 Store Procedure như sau:
CREATE PROCEDURE getAccounts AS BEGIN SELECT * FROM Logon END
_Tạo 1 chuỗi mô tả cho việc gọi Stored procedure trong cơ sở dữ liệu theo cú pháp {call <tên_sp>(?,?,?,…) } (Trong đó tương ứng với mỗi dấu ? là 1 tham số của store procedure đã tạo trong Database )
_Tạo 1 thể hiện của interface CallableStatement thông qua phương thức prepareCall() dựa trên đối tượng Connection
_Gọi phương thức executeQuery() để trả về kết quả là 1 ResultSet.
_Tạo 1 thể hiện của interface CallableStatement thông qua phương thức prepareCall() dựa trên đối tượng Connection
_Gọi phương thức executeQuery() để trả về kết quả là 1 ResultSet.
public ResultSet getAccounts() throws Exception{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;database=Sinhvien", "sa", "");
String strCall = "{call getAccounts}";
CallableStatement caSt = con.prepareCall(strCall);
ResultSet rs = caSt.executeQuery();
return rs;
}
Trường hợp 2: Thao tác dữ liệu cho DB
1.Sử dụng Statement
Tôi sẽ demo thao tác insert dữ liệu vào bảng Logon.
1.Sử dụng Statement
Tôi sẽ demo thao tác insert dữ liệu vào bảng Logon.
public void insertAccount(String user, String pass, String fullName,
String role) throws Exception{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con = DriverManager.getConnection("jdbc:sqlserver://"
+ "localhost:1433;database=Sinhvien", "sa", "");
String strSQL = "insert into Logon values('"+user+"','"+pass+"','"+
fullName+"','"+role+"')";
Statement st = con.createStatement();
st.execute(strSQL);
}
2.Sử dụng PreparedStatement
Tôi sẽ demo thao tác update dữ liệu có trong bảng Logon.
Tôi sẽ demo thao tác update dữ liệu có trong bảng Logon.
public void updateAccount(String user, String pass, String fullName,
String role) throws Exception{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con = DriverManager.getConnection("jdbc:sqlserver://"
+ "localhost:1433;database=Sinhvien", "sa", "");
String strSQL = "update Logon set Password=?, FullName=?, Role=?"
+ "where UserName=?";
PreparedStatement ps = con.prepareStatement(strSQL);
ps.setString(1, pass);
ps.setString(2, fullName);
ps.setString(3, role);
ps.setString(4, user);
ps.execute();
}
3.Sử dụng CallablStatement
Tôi sẽ demo thao tác delete record có trong bảng Logon
_Tôi có Store Procedure như sau:
Tôi sẽ demo thao tác delete record có trong bảng Logon
_Tôi có Store Procedure như sau:
CREATE PROCEDURE deleteAccount @username VARCHAR(50) AS BEGIN DELETE FROM Logon WHERE [UserName]=@username END
_Tôi gọi Store Procedure như sau:
public void deleteAccounts(String user) throws Exception{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con = DriverManager.getConnection("jdbc:sqlserver://"
+ "localhost:1433;database=Sinhvien", "sa", "");
String strSQL = "{call deleteAccount(?)}";
CallableStatement cs = con.prepareCall(strSQL);
cs.setString(1, user);
cs.execute();
}
Không có nhận xét nào:
Đăng nhận xét