I have a comments box in my website
<td id="commentsBox" class="xec" size="200"></td>
I use Javascript to read the comments box and create an XML string.
<ROWS><COMMENTS>My comments</COMMENTS></ROWS>
The XML string is passed to a stored procedure via java. (I have simplified the SQL Code and XML String for the purposes of the question)
CREATE PROCEDURE [DB].[TEST$ExecuteXML] #doc VARCHAR(max)
,#P_Result VARCHAR(max) OUTPUT
AS
BEGIN
DECLARE #idoc INT;
EXEC sp_xml_preparedocument #idoc OUTPUT
,#doc;
INSERT INTO MyTable (
COMMENTS
,UPDATE_DATE
)
SELECT COMMENTS
,getDate()
FROM OPENXML(#idoc, '/ROWS', 1) WITH (
COMMENTS VARCHAR(200) 'COMMENTS'
);
SET #p_result = 1;
END
I have looked at sites dealing with SQL Injection such as https://technet.microsoft.com/en-us/library/ms161953(v=sql.105).aspx
Is it possible to enter something into the textbox that will be destructive to the database?
UPDATE
In response to #Linky I add here (part of) the Java code - although I am at a loss to understand how this could be problematic, as the premise of my question is that the SQL Server procedure could accept anything in the XML.
XMLObject br = new XMLObject(xmlString);
String result = br.update();
public class XMLObject {
public static final int RESULT_FAILED = 0;
public static final int RESULT_SUCCESS = 1;
protected DBConnection dbConn = null;
protected String theXML=null;
public XMLObject(String theXML) {
this.theXML=theXML;
}
public String update() {
String result;
ArrayList<DBField> fields = new ArrayList<>();
fields.add(new DBField(DBField.STRING, theXML, false));
result = DMLUtils.executeString("ExecuteXML", fields);
return result;
}
}
The best way to avoid SQL Injection attacks is to tie the content to a particular column. Directly entering the users comments into the database opens you up to cross site scripting attacks if that data is ever displayed to the user. I would suggest that you take this question to the Security Stack Exchange.
Injection comes from allowing SQL data to become or be treated as SQL commands. In your example you are keeping data and commands clearly delineated, and AFAIK, neither OPENXML nor sp_xml_preparedocumen can on their own be used cause injection.
So, in my professional opinion, this appears to be safe from injection.
Related
I'm calling a third party stored procedure with a lot of optional parameters. It's in MS SQL Server 2008 R2 and looks like this:
procedure [dbo].[pTest]
#ReportDate varbinary(max) out,
#Optional1 varchar(max) = null,
#Optional2 varbinary(max) = null,
#Date varchar(max) = null,
-- ~20 more optional params of various types
as
begin
select * from test
end
I'm calling it from java using Spring Data JPA 1.10.2 with com.microsoft.sqlserver's sqljdbc 4.2.
I only ever want to call the procedure with two params ReportDate and Date. The rest of the params can stay at the default value (i.e. null). My code is as follows:
#Entity
#NamedStoredProcedureQuery(name = "Test.get", procedureName = "pTest", parameters = {
#StoredProcedureParameter(mode = ParameterMode.OUT, name = "ReportData", type = byte[].class),
#StoredProcedureParameter(mode = ParameterMode.IN, name = "Date", type = String.class)
})
public class Test {
// serves no purpose other than to meet
// JPA requirement
#Id
private byte[] reportData;
}
Spring data repository:
public interface TestRepository extends Repository<Test, Long> {
#Procedure("pTest")
byte[] get(#Param("Date") String date);
}
Test code:
#RunWith(SpringRunner.class)
#SpringBootTest
#Transactional(transactionManager="transactionManager")
public class TestRepositoryTest {
#Autowired
private TestRepository testRepository;
#Test
public void testGet() throws SQLException {
byte[] blob = testRepository.get("hi");
//error occurs
}
}
I get the following error though:
o.h.engine.jdbc.spi.SqlExceptionHelper : Error preparing CallableStatement [pTest]
com.microsoft.sqlserver.jdbc.SQLServerException: The index 4 is out of range.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:191) ~[sqljdbc-4.2.jar:na]
I'm getting this because I did not specify Optional1 and Optional2. Now, if I add the Optional1 and Optional2 as #StoredProcedureParameters and as paramters to getTest method of the TestRepository, I no longer get the error. But then, I have to pass in a non-null values or otherwise I get another error complaining that I passed no value.
How can I call this stored procedure preferably without specifying Optional1 and Optional2?
In some cases, I need to call a stored procedure where the 15th parameter is all I need to set. I really prefer not having to define the other 14 parameters in my code. Even if I did that, I cannot set them to null.
It's not possible using Microsoft's JDBC driver. Two options:
Use jTDS (which is not an option if the stored procedure returns >8000 bytes)
Create a wrapper stored procedure that only has the params that are needed and have it call the other stored procedure. This may not be an option if you don't have permissions to create stored procedures on the database.
In my case, option 2 works because I do have permission to create stored procedures.
I am having a stored procedure (sp) which uses UniqueIntList type in the sql server. I want to call this sp from java using spring's NamedParameterJdbcTemplate.
sql server sp execution details
DECLARE #M dbo.UniqueIntList
INSERT INTO #M VALUES (1),(2),(6)
exec usp_mysp #M
GO
Below is how I am executing it using java
private static final String SQL_SP = "usp_mysp :myVar";
MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource();
mapSqlParameterSource.addValue("myVar", myList); // my list is List<Integer>
namedParameterJdbcTemplate.query(SQL_SP, mapSqlParameterSource, (q, i) -> {
// ... //
});
While doing this, I am getting Procedure or function has too many arguments specified and I figured out that this is due to the UniqueIntList type in the sql server.
So I would like to know how exactly shall I pass the values in the map for the NamedParameterJdbcTemplate?
As suggested by #David Browne in the comments, I had put some research on Table-valued parameter and this Link here helped to understand how can I achieve it using spring's NamedParameterJdbcTemplate for the ms-sql server
I am a rookie/newbie in the postgres data access api. I have worked a bit on oracle, sql server and trying to do what i have done with those dbms
The use is very simple
1) a stored procedure aka function with input params
2) Returning or more ref cursors
3) Using an ent lib wrapper to use the npgsql provider/database with it
4) Doing a data adapter fill and running into the issue with some cursor de-referencing.. it appears though i am inside a tran..
5) I just want to get some simple working sample with the latest npgsql provider..
Here is my function
CREATE OR REPLACE FUNCTION public.geterrorcategories(
v_organizationid integer)
RETURNS refcursor
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE cv_1 refcursor;
BEGIN
open cv_1 for
SELECT errorCategoryId, name, bitFlag
FROM ErrorCategories
ORDER BY name;
RETURN cv_1;
END;
$BODY$;
The code using the enterprise lib api/wrapper is as follows.
/// <summary>
/// Executes GetErrorCategories in case of SQL Server or GetErrorCategories for Oracle
/// </summary>
public static DataTable GetErrorCategoriesAsDataTable(string dbKey ,int? ORGANIZATIONID)
{
DataTable tbl = new DataTable();
Database db = Helper.GetDatabase(dbKey);
using (DbConnection con = db.CreateConnection()){
con.Open();
var tran = con.BeginTransaction();
using (DbCommand cmd = con.CreateCommand()){
cmd.Transaction = tran;
BuildGetErrorCategoriesCommand(db, cmd ,ORGANIZATIONID);
cmd.CommandText = "GetErrorCategories";
try {
Helper.FillDataTable(tbl, db, cmd);
con.Close();
} catch (DALException ) {
throw;
}
}
}
return tbl;
}
The command is built as follows.
private static void BuildGetErrorCategoriesCommand(Database db, DbCommand cmd ,int? ORGANIZATIONID){
Helper.InitializeCommand(cmd, 300, "GetErrorCategories");
db.AddReturnValueParameter(cmd);
db.AddInParameter(cmd, "organizationId", DbType.Int32, ORGANIZATIONID);
db.AddCursorOutParameter(cmd, "CV_1");
}
I am not getting any error. I get only 1 row back which i think is this un_named_portal_1 or something but not the results from my table which my query returns
It is frustrating as i would like to keep my application code the same as much as possible but would like to switch providers at run time. I am using a tweaked 'ent lib' contribution database that was created for npgsql.
Hope this helps to point me to the right areas to look for..
There is absolutely no reason above to declare your PostgreSQL function to return a cursor - you can simply return a table, see the PostgreSQL docs for more info.
Npgsql originally had a feature where it automatically "dereferenced" cursors returned from functions, but this has been removed. For more information about this see this issue (warning, it's long...). Some people are requesting that the feature be returned.
I read here (and elsewhere) that it's possible, in SQL Server 2008, to build a user-defined aggregate which can return a string longer than 8000 characters. This is exactly what I need.
Supposedly, the method is to set maxByteSize to -1 instead of a number btw 1 and 8000; this should allow any size up to 2GB.
For some reason, apparently, you can't deploy straight from Visual Studio 2008 if you use this setting; so you need to manually deploy.
So: I build my project - GroupConcat (which is supposed to simulate MySQL's group_concat aggregator) - which gives me, in the project's bin folder, a file "SqlClassLibrary.dll". Per the instructions on the above-linked page, I build the assembly in SQL Server. The command executes successfully. However, when I try to actually use the groupconcat aggregator:
select department, dbo.groupconcat(projectNumber) from projectleads group by department
...it says it can't be found. This all works fine if I set maxByteSize to 8000 and deploy directly from within VS2008, but I need >8000. Anybody know what I'm doing wrong?
Thanks
-dan
NOTE: I do specifically need to have a groupconcat aggregator function rather than using some of the SQL Server tricks I've often seen.
Alternately, you can use MaxSize property of SqlFacetAttribute to to indicate the varchar size. Note that in the example below I applied this attribute to the SqlString parameters in the Accumulate method and to the return value of the Terminate method. This results in the following SQL signature:
AGGREGATE [dbo].[Concatenate] (#value nvarchar(max), #order int, #seperator nvarchar(max)) RETURNS nvarchar(max)
[Serializable]
[SqlUserDefinedAggregate(
Format.UserDefined,
IsInvariantToOrder = true,
IsInvariantToNulls = true,
IsInvariantToDuplicates = false,
IsNullIfEmpty = false,
MaxByteSize = -1)]
public struct Concatenate : IBinarySerialize
{
public void Init();
public void Accumulate([SqlFacet(MaxSize = -1)] SqlString value,
SqlInt32 order,
[SqlFacet(MaxSize = -1)] SqlString seperator);
public void Merge(Concatenate group);
[return: SqlFacet(MaxSize = -1)]
public SqlString Terminate();
public void Read(BinaryReader r);
public void Write(BinaryWriter w);
}
I don't know if this is any more "correct" than what you ended up doing, but it seems more natural.
Figured it out... After building the solution in Vis Studio, assuming I've dropped the .dll it creates into c:\temp and called it GroupConcat.dll:
CREATE ASSEMBLY GroupConcat from 'C:\temp\GroupConcat.dll' with permission_set = safe
GO
CREATE AGGREGATE groupconcat(#input nvarchar(max))
RETURNS nvarchar(max)
EXTERNAL NAME GroupConcat
GO
That does it.
Is there any regular expression library written in T-SQL (no CLR, no extended SP, pure T-SQL) for SQL Server, and that should work with shared hosting?
Edit:
Thanks, I know about PATINDEX, LIKE, xp_ sps and CLR solutions
I also know it is not the best place for regex, the question is theoretical :)
Reduced functionality is also accepted
How about the PATINDEX function?
The pattern matching in TSQL is not a complete regex library, but it gives you the basics.
(From Books Online)
Wildcard Meaning
% Any string of zero or more characters.
_ Any single character.
[ ] Any single character within the specified range
(for example, [a-f]) or set (for example, [abcdef]).
[^] Any single character not within the specified range
(for example, [^a - f]) or set (for example, [^abcdef]).
If anybody is interested in using regex with CLR here is a solution. The function below (C# .net 4.5) returns a 1 if the pattern is matched and a 0 if the pattern is not matched. I use it to tag lines in sub queries. The SQLfunction attribute tells sql server that this method is the actual UDF that SQL server will use. Save the file as a dll in a place where you can access it from management studio.
// default using statements above
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text.RegularExpressions;
namespace CLR_Functions
{
public class myFunctions
{
[SqlFunction]
public static SqlInt16 RegexContain(SqlString text, SqlString pattern)
{
SqlInt16 returnVal = 0;
try
{
string myText = text.ToString();
string myPattern = pattern.ToString();
MatchCollection mc = Regex.Matches(myText, myPattern);
if (mc.Count > 0)
{
returnVal = 1;
}
}
catch
{
returnVal = 0;
}
return returnVal;
}
}
}
In management studio import the dll file via programability -- assemblies -- new assembly
Then run this query:
CREATE FUNCTION RegexContain(#text NVARCHAR(50), #pattern NVARCHAR(50))
RETURNS smallint
AS
EXTERNAL NAME CLR_Functions.[CLR_Functions.myFunctions].RegexContain
Then you should have complete access to the function via the database you stored the assembly in.
Then use in queries like so:
SELECT *
FROM
(
SELECT
DailyLog.Date,
DailyLog.Researcher,
DailyLog.team,
DailyLog.field,
DailyLog.EntityID,
DailyLog.[From],
DailyLog.[To],
dbo.RegexContain(Researcher, '[\p{L}\s]+') as 'is null values'
FROM [DailyOps].[dbo].[DailyLog]
) AS a
WHERE a.[is null values] = 0
There is some basic pattern matching available through using LIKE, where % matches any number and combination of characters, _ matches any one character, and [abc] could match a, b, or c...
There is more info on the MSDN site.
In case anyone else is still looking at this question, http://www.sqlsharp.com/ is a free, easy way to add regular expression CLR functions into your database.
If you are using SQL Server 2016 or above, you can use sp_execute_external_script along with R. It has functions for Regular Expression searches, such as grep and grepl.
Here's an example for email addresses. I'll query some "people" via the SQL Server database engine, pass the data for those people to R, let R decide which people have invalid email addresses, and have R pass back that subset of people to SQL Server. The "people" are from the [Application].[People] table in the [WideWorldImporters] sample database. They get passed to the R engine as a dataframe named InputDataSet. R uses the grepl function with the "not" operator (exclamation point!) to find which people have email addresses that don't match the RegEx string search pattern.
EXEC sp_execute_external_script
#language = N'R',
#script = N' RegexWithR <- InputDataSet;
OutputDataSet <- RegexWithR[!grepl("([_a-z0-9-]+(\\.[_a-z0-9-]+)*#[a-z0-9-]+(\\.[a-z0-9-]+)*(\\.[a-z]{2,4}))", RegexWithR$EmailAddress), ];',
#input_data_1 = N'SELECT PersonID, FullName, EmailAddress FROM Application.People'
WITH RESULT SETS (([PersonID] INT, [FullName] NVARCHAR(50), [EmailAddress] NVARCHAR(256)))
Note that the appropriate features must be installed on the SQL Server host. For SQL Server 2016, it is called "SQL Server R Services". For SQL Server 2017, it was renamed to "SQL Server Machine Learning Services".
Closing Thoughts
Microsoft's implementation of SQL (T-SQL) doesn't have native support for RegEx. This proposed solution may not be any more desirable to the OP than the use of a CLR stored procedure. But it does offer an additional way to approach the problem.
You can use VBScript regular expression features using OLE Automation. This is way better than the overhead of creating and maintaining an assembly. Please make sure you go through the comments section to get a better modified version of the main one.
http://blogs.msdn.com/b/khen1234/archive/2005/05/11/416392.aspx
DECLARE #obj INT, #res INT, #match BIT;
DECLARE #pattern varchar(255) = '<your regex pattern goes here>';
DECLARE #matchstring varchar(8000) = '<string to search goes here>';
SET #match = 0;
-- Create a VB script component object
EXEC #res = sp_OACreate 'VBScript.RegExp', #obj OUT;
-- Apply/set the pattern to the RegEx object
EXEC #res = sp_OASetProperty #obj, 'Pattern', #pattern;
-- Set any other settings/properties here
EXEC #res = sp_OASetProperty #obj, 'IgnoreCase', 1;
-- Call the method 'Test' to find a match
EXEC #res = sp_OAMethod #obj, 'Test', #match OUT, #matchstring;
-- Don't forget to clean-up
EXEC #res = sp_OADestroy #obj;
If you get SQL Server blocked access to procedure 'sys.sp_OACreate'... error, use sp_reconfigure to enable Ole Automation Procedures. (Yes, unfortunately that is a server level change!)
More information about the Test method is available here
Happy coding