我有一个会员网站,会员可以使用预付卡添加信用卡。添加信用页面只包含一个文本字段,使成员可以输入卡号,然后我将CardNumber参数传递给函数以验证卡,并通过MS SQL 2008数据库上的存储过程充值成员帐户。
几天前,一名攻击者成功使用该卡为其帐户添加了信用额,而未更改卡的使用状态和UserId。他能够多次使用同一张卡来增加信用额度。
我想知道他是否使用Firebug或Fiddler软件,但我知道它不会影响存储过程代码。
以下是我的存储过程代码:
ALTER procedure [dbo].[UseCard]
(@CardNumber varchar(10), @Used bit, @UserId bigint)
as
if (@CardNumber NOT in (SELECT CardNumber FROM CardsList)) and
RAISERROR('Card not found',16,1)
else if (@Used in (select Used from CardsList WHERE CardNumber = @CardNumber))
RAISERROR('Card used before',16,1)
else
begin
Update CardsList
set Used = 1, UserId = @UserId
where (CardNumber = @CardNumber)
UPDATE [Users]
SET UserBalance = UserBalance + (select Amount from CardsList where CardNumber = @CardNumber)
WHERE (ID = @UserId)
end
我的功能是
Public Sub UpdateCard(ByVal CardNumber As String, ByVal Used As Boolean)
Dim Command As New SqlClient.SqlCommand
With Command
.CommandText = "UseCard"
.CommandType = CommandType.StoredProcedure
.Parameters.AddWithValue("@CardNumber", CardNumber)
.Parameters.AddWithValue("@Used", Used)
.Parameters.AddWithValue("@UserId", UserId)
End With
DBProvider.ExecuteNonQuery(Command)
End Sub
我的HTML代码是:
<h3>Account TopUp</h3>
<telerik:RadAjaxLoadingPanel ID="RadAjaxLoadingPanel1" runat="server"
Skin="Web20">
</telerik:RadAjaxLoadingPanel>
<telerik:RadAjaxPanel ID="RadAjaxPanel1" runat="server" Height="200px"
Width="300px" HorizontalAlign="NotSet" LoadingPanelID="RadAjaxLoadingPanel1">
<asp:Label ID="lbresult" runat="server"></asp:Label>
<label for="your_name">Card Number</label><br/>
<asp:TextBox ID="txtCardNumber" runat="server" Width="200px"></asp:TextBox>
<asp:RegularExpressionValidator Text=" * " ID="RegularExpressionValidator1" runat="server" ValidationExpression="[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{2}"
ControlToValidate="txtCardNumber" />
<asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server"
ControlToValidate="txtCardNumber" ErrorMessage="*"
Font-Bold="True"></asp:RequiredFieldValidator>
<asp:Button ID="btnAddCredits" runat="server" Text="ADD" />
</telerik:RadAjaxPanel>
答案 0 :(得分:1)
如果我为存储过程的@Used
参数传入0,我可以一遍又一遍地重复使用同一张卡。
我会从那里开始 - 如果@Used
参数暴露给网络,那么你就有一个SQL注入漏洞。
答案 1 :(得分:1)
您应确保您的SP在交易中,否则检查该卡是否已被使用的查询可以在更新之前由两个单独的线程执行。
这将保护您UserBalance
的更新,因此每张卡只能更新一次,如果在CardsList
更新后但在UserBalance
更新之前有任何失败,则会更新,回滚。
e.g。想象使用相同卡的以下事件序列
Request 1 Request 2
Check card 123 has not been used
Card has not been used
Check card 123 has not been used
Card has not been used
Update card to mark as used
Update card to mark as used
Update balance
Update balance
正如您所看到的,根据并发请求发生的事件顺序,同一张卡可能会多次发生更新。
事务将锁定数据库表,直到提交事务为止,因此事件序列现在为:
Request 1 Request 2
BEGIN TRANSACTION
Check card 123 has not been used
Card has not been used
BEGIN TRANSACTION
Check card 123 has not been used - SQL
Server will
suspend (pause) this thread as this
record
has been
locked by the other thread
Update card to mark as used
Update balance
COMMIT TRANSACTION Thread resumed
Check card 123 has not been used -
it has so
RAISERROR
TRANSACTION aborted (rolled back, but as no
changes were made it is simply marked as
complete)
同样Used
也不应该是参数,只是SP中的变量。
ALTER procedure [dbo].[UseCard]
(@CardNumber varchar (10) , @UserId bigint)
AS
DECLARE @Used bit;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
if (@CardNumber NOT in (SELECT CardNumber FROM CardsList)) and
RAISERROR('Card not found',16,1)
else if (@Used in (select Used from CardsList WHERE CardNumber = @CardNumber))
RAISERROR('Card used before',16,1)
else
begin
Update CardsList
set
Used = 1 ,UserId = @UserId
where (CardNumber = @CardNumber)
UPDATE [Users]
SET
UserBalance = UserBalance + (select Amount from CardsList where CardNumber = @CardNumber)
WHERE (ID = @UserId)
end
COMMIT TRANSACTION;