I need some help regarding SQL-injection. Ive been reading about it, but still don’t understand how to protect myself against it.
I have a simple Access database with a table (“course”) containing names and dates among other things. I then have a old ASP page with a search form. Below the search form I have a div (“schema”) that I want to populate with the search result through AJAX. Everything works fine, but is there something I need to do to protect me from an SQL-injection? Here is a simplified version of my code:
ASP Searchpage with AJAX and form (index.asp)
$("#search").keyup(function() {
var varSearch;
varSearch = $("#search").attr("value").replace(/\s/g,"+");
$("#schema").load('ajax.asp?q=' + varSearch + '');
});
<input type="text" name="search" id="search" value="Search.." />
<div id=”schema”></div>
ASP resultpage (ajax.asp):
q = request.querystring("q")
SQL = "SELECT * FROM course WHERE startDate >= #" & Now & "# AND (courseName like '%" & q & "%');"
While Not dbRS.EOF
str courseName = dbRS("courseName ")
Respone.Write courseName
dbRS.MoveNext
Wend
Any help would be much appreciated. Thanks.
Update:
Here is what I've got so far. This is the whole page. I cant see whats missing. Do I need to do something with the Access database, or is it working straight away?
<!--#include file="includes/adovbs.inc" -->
<%
q = request.querystring("q")
SQL = "SELECT * FROM info WHERE (cNamn Like '%?%');"
Set cn = Server.CreateObject("ADODB.Connection")
set comm = CreateObject("ADODB.Command")
set parameter1 = CreateObject("ADODB.Parameter")
cn.Open ("db.mdb")
Set cn.ActiveConnection = cn
parameter1.Type=adVarChar 'for example
parameter1.Size=25 'for example
parameter1.Direction=adParamInput
parameter1.Value=q
comm.Parameters.Append parameter1
Set recordset = comm.Execute
recordset.Close
cn.Close
Set recordset= Nothing
Set comm = Nothing
Set cn = Nothing
%>
The error I get right now is "[Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified" on the line "cn.Open ("db.mdb")". I've tried with "cn.Open server.mappath("db.mdb")" but the error I get then is "[Microsoft][ODBC Driver Manager] Data source name too long"
SQL injection occurs when you do what you have there: taking user input and directly putting it into a SQL command. Someone with malicious intent could try and submit text that would append additional SQL statements to the one you are trying to run.
The first line of defense would be to try and parse for characters that would allow injection. For example, someone adding a single quote ( ' ) would end your string in the SQL statement, and allow them to try and begin a new statement. If you run your q variable through a parse function you can try and prevent that. Replacing one single-quote with two single-quotes will pass the single-quote through as intended by the user, and prevent SQL injection from someone malicious.
Additionally, you are better served by using stored procedures and parameters if you can.
The danger is in allowing the q variable to be processed by SQL as an instruction.
you can protect yourself by doing:
q = request.querystring("q")
SQL = "SELECT * FROM course WHERE startDate >= #?# AND (courseName like '%?%');"
ConnStr = "driver=Microsoft Access Driver (*.mdb);uid=;dbq=" & Server.MapPath ("db.mdb")
Set cn = Server.CreateObject("ADODB.Connection")
set comm = CreateObject("ADODB.Command")
set parameter1 = CreateObject("ADODB.Parameter")
set parameter2 = CreateObject("ADODB.Parameter")
cn.Open ConnStr
Set comm.ActiveConnection = cn
comm.CommandText = SQL
parameter1.Type=adVarChar 'for example
parameter1.Size=25 'for example
parameter1.Direction=adParamInput
parameter1.Value=value1
parameter2.Type=adVarChar 'for example
parameter2.Size=25 'for example
parameter2.Direction=adParamInput
parameter2.Value=value2
comm.Parameters.Append parameter1
comm.Parameters.Append parameter2
Set recordset = comm.Execute
...you ASP code here...
'when your done dont forget to clean up the resources
recorderset.Close
cn.Close
Set recordset= Nothing
Set comm = Nothing
Set cn = Nothing
Also, if you know a parameter is going to be a number , or a date, checking that it is indeed so , is good protection too.
Related
I have a page where a database lookup is done, and if a value is found some response is sent back to the user and then the database value is deleted.
I'm calling my delete sub after I've checked the variable but it's like this never happened, although i know the sub ran since the value is now gone from the database.
It seems like the page is loading twice and on the second pass the database value has gone and so it shows as if the database value was never found, but it had to have been found in order to have deleted it (as this was a condition for running the delete sub).
I realize this sounds insane but I've been on this for 2 days and have no idea what's going on. I've tried disabling caching from within IIS and also changing my code so that a value is posted to the database instead of deleting a record and I still get the same thing where my server seems to check the future value of the database before calling the routine that changes it.
Has anyone seen similar behavior to this when reading and writing to the same record on a single page?
Code:
referer = Request.ServerVariables ("HTTP_REFERER")
session("testcode") = right(referer,16)
testcode = session("testcode")
set objcommand = server.createObject("adodb.command")
objcommand.activeconnection = strconnect2
objcommand.commandtext="SELECT codeval,stamp,used from code where codeval like '" & testcode & "'"
objcommand.commandtype = 1
set objrs = objcommand.Execute
set objcommand = nothing
countvar = 0
While not objrs.EOF
if not objrs("used") = true then
foundcode = true
countvar = countvar+1
end if
objrs.MoveNext
wend
if foundcode = true then
response.write "Found you!"
set objcomm5 = server.createobject("adodb.command")
objcomm5.activeconnection = strconnect2
objcomm5.commandtext = "update code set used = true where codeval like '"& testcode &"' "
objcomm5.commandtype = &H0001
objcomm5.execute
else
response.write "Can't be found!"
end if
I have two sets of code, that are the same I just change variables to another set that exist and now with the ones I changed I get an error saying "Run-time error '3061': Too few parameters. Expected 6."
This is the changed code:
Dim rec As Recordset
Dim db As Database
Dim X As Variant
Set db = CurrentDb
Set rec = db.OpenRecordset("UnitMoreInfoQ")
Const msgTitle As String = "Open Explorer"
Const cExplorerPath As String = "C:\WINDOWS\EXPLORER.EXE"
Const cExplorerSwitches As String = " /n,/e"
cFilePath = rec("ProjFilePath")
It highlights this line:
Set rec = db.OpenRecordset("UnitMoreInfoQ")
This is the first code:
Dim rec As Recordset
Dim db As Database
Dim X As Variant
Set db = CurrentDb
Set rec = db.OpenRecordset("ProjectMoreInfoQ")
Const msgTitle As String = "Open Explorer"
Const cExplorerPath As String = "C:\WINDOWS\EXPLORER.EXE"
Const cExplorerSwitches As String = " /n,/e"
cFilePath = rec("ProjFilePath")
As you can see, the line has the same amount of parameters:
Set rec = db.OpenRecordset("ProjectMoreInfoQ")
This has gotten me quite confused for awhile because of this. How do I fix this error?
I didn't get the same result as you when testing your db, and I still don't understand the difference. However, maybe we can still get you something which works in spite of my confusion.
The query contains 6 references to form controls, such as [Forms]![WorkOrderDatabaseF]![Text71]. Although you're certain that form is open in Form View when you hit the "too few parameters" error at db.OpenRecordset("UnitMoreInfoQ"), Access doesn't retrieve the values and expects you to supply them.
So revise the code to supply those parameter values.
Dim rec As DAO.Recordset
Dim db As DAO.database
Dim prm As DAO.Parameter
Dim qdf As DAO.QueryDef
Dim X As Variant
Set db = CurrentDb
'Set rec = db.OpenRecordset("UnitMoreInfoQ")
Set qdf = db.QueryDefs("UnitMoreInfoQ")
For Each prm In qdf.Parameters
prm.value = Eval(prm.Name)
Next
Set rec = qdf.OpenRecordset(dbOpenDynaset) ' adjust options as needed
I'm leaving the remainder of this original answer below in case it may be useful for anyone else trying to work through a similar problem. But my best guess is this code change will get you what you want, and it should work if that form is open in Form View.
Run this statement in the Immediate window. (You can use Ctrl+g to open the Immediate window.)
DoCmd.OpenQuery "UnitMoreInfoQ"
When Access opens the query, it will ask you to supply a value for the first parameter it identifies. The name of that parameter is included in the parameter input dialog. It will ask for values for each of the parameters.
Compare those "parameter names" to your query's SQL. Generally something is misspelled.
Using the copy of your db, DoCmd.OpenQuery("UnitMoreInfoQ") asks me for 6 parameters.
Here is what I see in the Immediate window:
? CurrentDb.QueryDefs("UnitMoreInfoQ").Parameters.Count
6
for each prm in CurrentDb.QueryDefs("UnitMoreInfoQ").Parameters : _
? prm.name : next
[Forms]![WorkOrderDatabaseF]![Text71]
[Forms]![WorkOrderDatabaseF]![ClientNameTxt]
[Forms]![WorkOrderDatabaseF]![WorkOrderNumberTxt]
[Forms]![WorkOrderDatabaseF]![TrakwareNumberTxt]
[Forms]![WorkOrderDatabaseF]![WorkOrderCompleteChkBx]
[Forms]![WorkOrderDatabaseF]![WorkOrderDueDateTxt]
Make sure there is a form named WorkOrderDatabaseF open in Form View when you run this code:
Set rec = db.OpenRecordset("UnitMoreInfoQ")
Does the [UnitMoreInfoQ] query execute properly on its own? If you mistype a field in access it will treat that field as a parameter.
ProjectMoreInfoQ and UnitMoreInfoQ are different queries... it sounds like one takes 6 parameters and the other doesn't. Look at the queries in Access and see if either have parameters defined.
I am trying to get a cell drop-down values in Excel from a SQL Server. I don't want to use the method of putting all the data to another sheet and the use data validation to control the drop down values. That always give my a bunch of empty lines towards the end since I want to make sure I have room for any addition in the DB.
Is there a way to retrieve the drop-down values directly from SQL Server? Using a statement something like:
Select name from employees
Thanks for your help...
Use ADODB to retrieve the values you want, and use the retrieved values to populate a dropdown shape in Excel which you can create dynamically.
In a similar situation, since the source data was basically static, I populated a global array from an ADODB recordset when the application started and used that array when populating the items in the dropdown. Here's a snippet of that code:
Dim InstrumentIDs() As String
Dim InstrumentIDReader As Integer
Dim InstrumentIDCount As Integer
Public PositionRange As String
Public Sub GetInstrumentIDs()
'
'Populate InstrumentIDs array from current contents of Instrument table in EMS database
'
Dim conn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim sql As String
Dim loader As Integer, sn As String
InstrumentIDReader = 0
On Error GoTo GetInstrumentError
conn.ConnectionString = "Provider=sqloledb; Data Source=myServer; Initial Catalog=myDatabase; User ID=myUser;Password=myPassword"
conn.Open
sql = "Select Count([SerialNo]) As [Number] From [Instrument]"
rs.Open sql, conn, adOpenStatic
InstrumentIDCount = CInt(rs![Number])
ReDim InstrumentIDs(InstrumentIDCount - 1)
rs.Close
sql = "Select [SerialNo] From [Instrument] Order By [SerialNo]"
rs.Open sql, conn, adOpenForwardOnly
loader = 0
rs.MoveFirst
Do While Not rs.EOF
sn = CStr(rs![SerialNo])
InstrumentIDs(loader) = sn
loader = loader + 1
rs.MoveNext
Loop
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
Exit Sub
GetInstrumentError:
MsgBox "Error loading instruments: " & Err.Description
End Sub
You must set a reference to Microsoft ActiveX Data Objects m.n Library (latest version on my computer is 2.8) from Tools > References in VBA editor.
See article
http://www.thespreadsheetguru.com/blog/2014/5/14/vba-for-excels-form-control-combo-boxes for tips on how to manage dropdown boxes in Excel.
You can use the MS Query Wizard in Excel to store a query and use it's data any time.
This this link for details http://www.techrepublic.com/article/use-excels-ms-query-wizard-to-query-access-databases/
I hope I am asking a reasonably easy question - just trying to save myself some time to be honest. :)
All I want is to call a PL/SQL procedure from a SQL Server job.
I already have the connection string in a registry string and I have the procedure built.
I dont know if I can just specify T-SQL and grab the registry setting somehow, but I suspect that I need an ActiveX script (if this is possible).
Does anyone have this (admittedly quite specific) code snippet handy?
Thanks in advance!
Frosty
got there in the end. This worked for me under an ActiveX job:
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
Dim oConnection, oRegistry, sConnectionString, sPlsqlCall, sRegistryKey, sValueName, sParam1, sParam2
' Get connection string from registry
Set oRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
sRegistryKey = "Software\OracleConnectionString"
sValueName = "Value"
oRegistry.GetStringValue HKEY_CURRENT_USER, sRegistryKey, sValueName, sConnectionString
' Set up PLSQL query
sPlsqlCall = "my_package.my_procedure(" & sParam1 & ", " & sParam2 & ")"
' Call PLSQL
Set oConnection = CreateObject("ADODB.Connection")
oConnection.ConnectionString = sConnectionString
oConnection.Open
oConnection.Execute sPlsqlCall
oConnection.Close
Set oConnection = Nothing
Set oRegistry = Nothing
I have an ASP form that needs to submit data to two different systems. First the data needs to go into an MS SQL database, which will get an ID. I then need to submit all that form data to an external system, along with that ID.
Pretty much everything in the code works just fine, the data goes into the database, and the data will go to the external system. The problem is I am not getting my ID back from SQL when I execute that query. I am under the impression this is happening because of how fast everything occurs in the code. The database is adding it's row at the same time my post page runs it's query to get the ID back, I think.
I need to know of a way to wait until SQL finished the insert or wait for a specific amount of time maybe. I already tried using the hacks to "sleep" with ASP, that did not help.
I am sure I could accomplish this in .Net, my background is more .Net than ASP, but this is what I have to work with on my current project.
Any ideas?
EDIT: Code from the the function writing to the DB.
driis - That was my understanding of how this should be working, but my follow up query for the ID returns nothing, so my though is that the row hasn't finished being inserted or updated yet. Maybe I am wrong on that, if so, that complicates this more. :(
Either way here is the code from the function to update the DB. Mind you this code is inherited, the rest of my project is being written by me, but I am stuck using these functions from a previous developer.
Sub DBWriteResult
Dim connLeads
Dim sSQL
Dim rsUser
Dim sErrorMsg
Dim sLeads_Connection
' Connect to the Leads database
' -------------------------------------------------------------------
sLeads_Connection = strDatabaseConnection
Set connLeads = CreateObject("ADODB.Connection")
connLeads.Provider = "SQLOLEDB.1"
On Error Resume Next
connLeads.Open sLeads_Connection
If Err.number <> 0 Then
' Bad connection display error
' -----------------------------------------------------------------
Response.Write "Database Write Error: 001 Contact Programmer"
Set connLeads = Nothing
Exit Sub
Else
' Verify the transaction does not already exist.
' -----------------------------------------------------------------------
Set rsUser = Server.CreateObject("ADODB.Recordset")
rsUser.LockType = 3
rsUser.CursorLocation = 3
sSQL = "SELECT * "
sSQL = sSQL & " FROM Leads;"
rsUser.Open sSQL, connLeads, adOpenDynamic
Response.Write Err.Description
If Err.number = 0 Then
' Add the record
' -----------------------------------------------------------
rsUser.AddNew
rsUser.Fields("LeadDate") = Date()&" "&Time()
rsUser.Fields("StageNum") = ESM_StageNum
rsUser.Fields("MarketingVendor") = ESMSourceData
rsUser.Fields("FirstName") = ESM_FirstName
rsUser.Fields("Prev_LName") = Request.Form ("Prev_LName")
rsUser.Fields("LastName") = ESM_LastName
rsUser.Fields("ProgramType") = ESM_ProgramType
rsUser.Fields("ProgramofInterest") = ESM_ProgramofInterest
rsUser.Fields("Phone1") = Phonenumber
rsUser.Fields("Phone2") = ESM_Phonenumber2
rsUser.Fields("Address1") = ESM_Address
rsUser.Fields("Address2") = ESM_Address2
rsUser.Fields("City") = ESM_City
rsUser.Fields("State") = ESM_State
rsUser.Fields("Region") = ESM_Region
rsUser.Fields("Zip") = ESM_Zip
rsUser.Fields("Country") = ESM_Country
rsUser.Fields("Email") = ESM_Email
rsUser.Fields("MilitaryBranch") = ESM_MilitaryBranch
rsUser.Fields("MilitaryStatus") = ESM_MilitaryStatus
rsUser.Fields("BestTimeToCall") = ESM_BestTimeToCall
rsUser.Fields("DateofBirth") = ESM_DateofBirth
rsUser.Update
Else
' There was an error
Response.Write "There was an error. Error code is: "&Err.number&" "&Err.Desc
End if
End If
' Close the recordset
' ---------------------------------------------------------------
Call rsUser.Close
Set rsUser.ActiveConnection = Nothing
Set rsUser = Nothing
' Destroy the connection to the database
' -------------------------------------------------------------------
Set connLeads = Nothing
End Sub
It sounds like you're trying to do this:
Insert some data in DB 1
Retrieve an ID from the inserted data
Send data + the ID to DB 2
It's been a good five years but I believe it looked something like this:
dim connStr1
connStr1 = "[connection string 1]"
dim conn1
set conn1 = server.createobject("adodb.connection")
conn1.open connStr1
dim sql
sql = " SET NOCOUNT ON " & vbCrLf & _
" INSERT FOO (a, b, c) VALUES (1, 2, 3) " & vbCrLf & _
" SET NOCOUNT OFF " & vbCrLf & _
" SELECT SCOPE_IDENTITY() "
dim rs
set rs = conn1.execute(sql)
rs.close
dim id
set id = CInt(rs(0))
conn1.close
dim connStr2
connStr2 = "[connection string 2]"
dim conn2
set conn2 = server.createobject("adodb.connection")
conn2.open connStr2
conn2.execute("INSERT FOO (id, a, b, c) VALUES (" & id & ", 1, 2, 3)")
conn2.close
Good luck, and get off my lawn!
Ok, so I figured this one out. The problem was insane, a typo. I am spoiled with .Net and the fact that if I use a variable that doesn't really exist, I get errors. I guess ASP doesn't care so much.
On the up side, driis was correct. The code does not continue until the database transaction is completed. That was my major concern, that had incorrectly assumed that was the case. I am glad I was right.
Thanks for the help, and hopefully the next time I post it'll be something better than a tyop.
;)