2008-11-20 18 views
5

Tôi đang kết hợp một ứng dụng web nhỏ viết vào cơ sở dữ liệu (Perl CGI & MySQL). Kịch bản CGI lấy một số thông tin từ một biểu mẫu và ghi nó vào một cơ sở dữ liệu. Tuy nhiên, tôi nhận thấy rằng nếu tôi nhấn 'Tải lại' hoặc 'Quay lại' trên trình duyệt web, nó sẽ ghi dữ liệu vào cơ sở dữ liệu một lần nữa. Tôi không muốn điều này.Làm thế nào tôi có thể ngăn chặn cơ sở dữ liệu được ghi lại khi trình duyệt tải lại/quay lại?

Cách tốt nhất để bảo vệ chống lại dữ liệu được viết lại trong trường hợp này là gì?

Trả lời

17

Không sử dụng yêu cầu GET để thực hiện sửa đổi! Hãy là RESTful; sử dụng POST (hoặc PUT) thay vì trình duyệt nên cảnh báo người dùng không tải lại yêu cầu. Chuyển hướng (using HTTP redirection) đến trang biên nhận bằng yêu cầu GET bình thường sau khi yêu cầu POST/PUT sẽ làm cho trang có thể làm mới trang mà không bị cảnh báo về việc gửi lại.

EDIT:

tôi giả sử người dùng đăng nhập bằng cách nào đó, và do đó bạn allready có một số cách để theo dõi người sử dụng, ví dụ phiên hoặc tương tự.

Bạn có thể tạo dấu thời gian (hoặc băm ngẫu nhiên, v.v.) khi hiển thị biểu mẫu lưu trữ cả hai dưới dạng trường ẩn (bên cạnh mã thông báo yêu cầu chống chéo trang web tôi chắc chắn rằng bạn đã có sẵn) và trong biến phiên (được lưu trữ an toàn trên máy chủ của bạn), khi bạn nhận được yêu cầu POST/PUT cho biểu mẫu này, bạn kiểm tra xem dấu thời gian có giống như dấu thời gian trong phiên không. Nếu có, bạn đặt dấu thời gian trong phiên làm việc cho một biến nào đó và khó đoán (dấu thời gian nối với một chuỗi ký tự bí mật), sau đó bạn có thể lưu dữ liệu biểu mẫu. Nếu ai đó lặp lại yêu cầu ngay bây giờ, bạn sẽ không tìm thấy cùng một giá trị trong biến phiên và từ chối yêu cầu.

Vấn đề với việc này là biểu mẫu không hợp lệ nếu người dùng nhấp lại để thay đổi thứ gì đó và có thể hơi khó khăn, trừ khi đó là số tiền bạn đang cập nhật. Vì vậy, nếu bạn có vấn đề với người dùng "ngu ngốc" làm mới và nhấp vào nút quay lại do đó vô tình đăng lại thứ gì đó, chỉ cần sử dụng POST sẽ nhắc họ không làm điều đó và chuyển hướng sẽ làm cho nó ít có khả năng hơn. Nếu bạn gặp vấn đề với người dùng độc hại, bạn nên sử dụng dấu thời gian quá dù đôi khi người dùng sẽ nhầm lẫn, mặc dù người dùng cố tình đăng cùng một tin nhắn nhiều lần và bạn có thể cần phải tìm cách cấm họ. Sử dụng POST, có timestam và thậm chí thực hiện so sánh toàn bộ cơ sở dữ liệu để kiểm tra các bài đăng trùng lặp, sẽ không giúp gì nếu người dùng độc hại chỉ viết kịch bản để tải biểu mẫu và gửi ngẫu nhiên rác. (Nhưng bảo vệ yêu cầu qua nhiều trang web khiến khó hơn rất nhiều)

+0

OP đang phàn nàn về người dùng của mình đang làm rối loạn hệ thống. Nói "làm cho trình duyệt của họ cố gắng và ngăn chặn chúng" là một giải pháp cho giải pháp. –

+2

Không, nhưng nó làm cho người dùng lành tính ít có khả năng làm như vậy, tôi đã thêm một số ý tưởng để tiếp tục giảm vấn đề. –

4

Tôi thấy tiện dụng để theo dõi số lần gửi biểu mẫu mà người dùng đã thực hiện trong phiên của họ. Sau đó, khi kết xuất biểu mẫu, tôi tạo một trường ẩn chứa số đó. Nếu người dùng sau đó gửi lại biểu mẫu bằng cách nhấn vào nút quay lại, nó sẽ gửi # cũ và máy chủ có thể cho biết rằng người dùng đã gửi biểu mẫu bằng cách kiểm tra nội dung trong phiên cho biết biểu mẫu đang nói gì.

Chỉ 2 xu của tôi.

6

Sử dụng yêu cầu POST sẽ khiến trình duyệt cố gắng ngăn người dùng gửi lại yêu cầu đó, nhưng tôi khuyên bạn nên sử dụng theo dõi giao dịch dựa trên phiên để một số loại bỏ qua cảnh báo từ trình duyệt và gửi lại truy vấn của mình, ứng dụng của bạn sẽ ngăn sao chép các thay đổi đối với cơ sở dữ liệu. Bạn có thể bao gồm một đầu vào bị ẩn trong biểu mẫu gửi với giá trị được đặt thành một băm mã hóa và ghi lại băm đó nếu yêu cầu được gửi và xử lý không có lỗi.

1

Nếu bạn chưa sử dụng một dạng quản lý phiên nào đó, một giải pháp đơn giản sẽ bao gồm một số loại định danh duy nhất trong biểu mẫu (dưới dạng phần tử ẩn) đó là một phần của chính giao dịch DB chính, hoặc được theo dõi trong một bảng DB riêng biệt. Sau đó, khi bạn được gửi biểu mẫu, bạn kiểm tra ID duy nhất để xem liệu nó đã được xử lý chưa. Và mỗi lần bản thân biểu mẫu được hiển thị, bạn chỉ cần đảm bảo rằng bạn có một ID duy nhất.

+0

Bạn phát hành một số vé, sau đó chỉ đục vé một lần. – dkretz

1

Trước hết, bạn không thể tin tưởng trình duyệt, do đó, bất kỳ cuộc nói chuyện nào về việc sử dụng POST thay vì GET chủ yếu là nerd flim-flam. Có, khách hàng có thể nhận được một cảnh báo dọc theo dòng "Bạn có ý định gửi lại dữ liệu này một lần nữa không?", Nhưng họ có thể sẽ nói "Có, bây giờ để tôi một mình, máy tính ngu ngốc".

Và đúng như vậy: nếu bạn không muốn gửi bản sao, thì đó là vấn đề của bạn để giải quyết, chứ không phải của người dùng.

Bạn có lẽ có ý tưởng nào đó về việc gửi trùng lặp. Có thể đó là cùng một IP trong vòng vài giây, có thể đó là cùng một tiêu đề của bài đăng trên blog hoặc URL đã được gửi gần đây. Có thể đó là sự kết hợp của các giá trị - ví dụ: Địa chỉ IP, địa chỉ email và tiêu đề chủ đề của việc gửi biểu mẫu liên hệ. Dù bằng cách nào, nếu bạn đã phát hiện một số bản sao trong dữ liệu của mình theo cách thủ công, bạn sẽ có thể tìm cách xác định một bản sao theo lập trình tại thời điểm gửi và gắn cờ để sao chép thủ công (nếu bạn không chắc chắn), hoặc chỉ nói với người gửi "Bạn đã nhấp đúp chưa?" (Nếu thông tin không được bảo mật một cách đáng kinh ngạc, bạn có thể trình bày hồ sơ hiện có mà bạn có cho họ và nói "Đây có phải là điều bạn muốn gửi cho chúng tôi không? Nếu vậy, bạn đã thực hiện nó - hooray")

1

I không nên dựa vào các cảnh báo POST từ trình duyệt. Người dùng chỉ cần nhấp vào OK để làm cho tin nhắn biến mất.

Bất cứ lúc nào bạn sẽ có yêu cầu chỉ cần một lần, ví dụ: 'thực hiện thanh toán', gửi mã thông báo duy nhất xuống, được gửi lại theo yêu cầu. Vứt bỏ mã thông báo sau khi nó trở lại, và vì vậy bây giờ bạn có thể cho biết khi nào đó là một bản đệ trình hợp lệ (bất kỳ thứ gì có mã thông báo không phải là 'hoạt động'). Hết hạn mã thông báo hoạt động sau khoảng thời gian X, ví dụ: khi phiên người dùng kết thúc.

(luân phiên theo dõi các thẻ đã trở lại, và nếu bạn đã nhận được nó trước sau đó nó là không hợp lệ.)

+0

Vẫn còn một vấn đề với điều kiện chủng tộc. Ngay sau khi bạn nhận được mã thông báo, bạn cần phải kiểm tra xem mã không được sử dụng trước đó, ví dụ: đưa ra một lệnh cập nhật SQL và kiểm tra xem giá trị trả về là 1 (bạn đã thay đổi một cái gì đó) hoặc 0E0 (bạn đã cố gắng thay đổi nó thành những gì nó đã có). –

0

Thực hiện POST mỗi khi bạn thay đổi dữ liệu, nhưng không bao giờ quay trở lại một phản ứng HTML từ một bài đăng. .. thay vì trả về một chuyển hướng đến một GET để truy xuất dữ liệu cập nhật dưới dạng trang xác nhận. Bằng cách đó, không có lo lắng về việc họ làm mới trang. Nếu họ làm mới, tất cả những gì sẽ xảy ra là truy xuất khác, không bao giờ là hành động thay đổi dữ liệu.

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