Preventing Injection Attacks with Excel Export - c#

I have posted my code below for an Excel exporting class (most of it anyway). The issue I am incurring is that this export is not safe when it comes to injection attacks. I was easily able to mask the initial command with parameters, however, when I passed the parameterized command to the Export(string) method, it loses the values previously set within the parameters; it only passes the literal string (i.e. SELECT * FROM TABLE WHERE COLUMN_NAME = #Parameter1).
What I am trying to figure out is a way to prevent this code from being unsafe. I really need this functionality. It has been fine in the past because of the desired audience of the applications, but I cannot use this specific code considering it will be customer facing and more less open to public use. :/
Any suggestions of how I can achieve this goal?
public static void Export(string CrossoverStatement = "SELECT * FROM TABLE")
{
// ##Parameters
//
// CrossoverStatement string :
// This is the string representation of the procedure executed to obtain the desired crossover query results.
//Create our database connection as well as the excel reference and workbook/worksheet we are using to export the data
string ODBCConnection = "SERVER INFO";
Application xls = new Application();
xls.SheetsInNewWorkbook = 1;
// Create our new excel application and add our workbooks/worksheets
Workbook Workbook = xls.Workbooks.Add();
Worksheet CrossoverPartsWorksheet = xls.Worksheets[1];
// Hide our excel object if it's visible.
xls.Visible = false;
// Turn off screen updating so our export will process more quickly.
xls.ScreenUpdating = false;
CrossoverPartsWorksheet.Name = "Crossover";
if (CrossoverStatement != string.Empty)
{
CrossoverPartsWorksheet.Select();
var xlsheet = CrossoverPartsWorksheet.ListObjects.AddEx(SourceType: XlListObjectSourceType.xlSrcExternal,
Source: ODBCConnection,
Destination: xls.Range["$A$1"]).QueryTable;
xlsheet.CommandText = CrossoverStatement;
xlsheet.RowNumbers = false;
xlsheet.FillAdjacentFormulas = false;
xlsheet.PreserveColumnInfo = true;
xlsheet.PreserveFormatting = true;
xlsheet.RefreshOnFileOpen = false;
xlsheet.BackgroundQuery = false;
xlsheet.SavePassword = false;
xlsheet.AdjustColumnWidth = true;
xlsheet.RefreshPeriod = 0;
xlsheet.RefreshStyle = XlCellInsertionMode.xlInsertEntireRows;
xlsheet.Refresh(false);
xlsheet.ListObject.ShowAutoFilter = false;
xlsheet.ListObject.TableStyle = "TableStyleMedium16";
// Unlink our table from the server and convert to a range.
xlsheet.ListObject.Unlink();
// Freeze our column headers.
xls.Application.Rows["2:2"].Select();
xls.ActiveWindow.FreezePanes = true;
xls.ActiveWindow.DisplayGridlines = false;
// Autofit our rows and columns.
xls.Application.Cells.EntireColumn.AutoFit();
xls.Application.Cells.EntireRow.AutoFit();
// Select the first cell in the worksheet.
xls.Application.Range["$A$2"].Select();
// Turn off alerts to prevent asking for 'overwrite existing' and 'save changes' messages.
xls.DisplayAlerts = false;
}
// Make our excel application visible
xls.Visible = true;
// Release our resources.
Marshal.ReleaseComObject(Workbook);
Marshal.ReleaseComObject(CrossoverPartsWorksheet);
Marshal.ReleaseComObject(xls);
}

Here is the best way to complete this request that I could come up with. Perform a parameterized query and pass the resulting data to a table which you can use when calling Export(datatable).
Problem resolved.
public static void Export(System.Data.DataTable CrossoverDataTable)
{
// ##Parameters
//
// CrossoverDataTable DataTable :
// This is a data table containing information to be exported to our excel application.
// Requested as a way to circumvent sql injection opposed to the initial overload accepting only a string .commandtext.
Application xls = new Application();
xls.SheetsInNewWorkbook = 1;
// Create our new excel application and add our workbooks/worksheets
Workbook Workbook = xls.Workbooks.Add();
Worksheet CrossoverPartsWorksheet = xls.Worksheets[1];
// Hide our excel object if it's visible.
xls.Visible = false;
// Turn off screen updating so our export will process more quickly.
xls.ScreenUpdating = false;
// Turn off calculations if set to automatic; this can help prevent memory leaks.
xls.Calculation = xls.Calculation == XlCalculation.xlCalculationAutomatic ? XlCalculation.xlCalculationManual : XlCalculation.xlCalculationManual;
// Create an excel table and fill it will our query table.
CrossoverPartsWorksheet.Name = "Crossover Data";
CrossoverPartsWorksheet.Select();
{
// Create a row with our column headers.
for (int column = 0; column < CrossoverDataTable.Columns.Count; column++)
{
CrossoverPartsWorksheet.Cells[1, column + 1] = CrossoverDataTable.Columns[column].ColumnName;
}
// Export our datatable information to excel.
for (int row = 0; row < CrossoverDataTable.Rows.Count; row++)
{
for (int column = 0; column < CrossoverDataTable.Columns.Count; column++)
{
CrossoverPartsWorksheet.Cells[row + 2, column + 1] = (CrossoverDataTable.Rows[row][column].ToString());
}
}
}
// Freeze our column headers.
xls.Application.Rows["2:2"].Select();
xls.ActiveWindow.FreezePanes = true;
xls.ActiveWindow.DisplayGridlines = false;
// Autofit our rows and columns.
xls.Application.Cells.EntireColumn.AutoFit();
xls.Application.Cells.EntireRow.AutoFit();
// Select the first cell in the worksheet.
xls.Application.Range["$A$2"].Select();
// Turn off alerts to prevent asking for 'overwrite existing' and 'save changes' messages.
xls.DisplayAlerts = false;
// ******************************************************************************************************************
// This section is commented out for now but can be enabled later to have excel sheets show on screen after creation.
// ******************************************************************************************************************
// Make our excel application visible
xls.Visible = true;
// Turn screen updating back on
xls.ScreenUpdating = true;
// Turn automatic calulation back on
xls.Calculation = XlCalculation.xlCalculationAutomatic;
// Release our resources.
Marshal.ReleaseComObject(Workbook);
Marshal.ReleaseComObject(CrossoverPartsWorksheet);
Marshal.ReleaseComObject(xls);
}

Related

export data in a list and array to excel using c#

I have a list of numbers in one class. I am trying to export the data in the list and the data in array[4,4] to Excel. I would like the list as one column with the header and the array as a table with headers (column name) and row names.
I did not find a proper solution to try
public void Data_to_Excel()
{
//start excel
NsExcel.Application excapp = new Microsoft.Office.Interop.Excel.Application();
//if you want to make excel visible
excapp.Visible = true;
//create a blank workbook
var workbook = excapp.Workbooks.Add(NsExcel.XlWBATemplate.xlWBATWorksheet);
//or open one - this is no pleasant, but yue're probably interested in the first parameter
string workbookPath = " filepath here ";
workbook = excapp.Workbooks.Open(workbookPath, 0, false, 5, "", "", false, Excel.XlPlatform.xlWindows, "", true, false, 0, true, false, false);
//Not done yet. You have to work on a specific sheet - note the cast
//You may not have any sheets at all. Then you have to add one with NsExcel.Worksheet.Add()
var sheet = (NsExcel.Worksheet)workbook.Sheets[1]; //indexing starts from 1
//do something usefull: you select now an individual cell
//var range = sheet.get_Range("A1", "A1");
//range.Value2 = "test"; //Value2 is not a typo
//now the list
string cellName;
int counter = 1;
foreach (var item in Sim_Obj.Bottom_Rows_Count_Stack)
{
cellName = "A" + counter.ToString();
var range = sheet.get_Range(cellName, cellName);
range.Value2 = item.ToString();
++counter;
}
workbook.SaveAs("Cash_Surge_Sim_Results.xlsx");
}
I have the 20 results in the rich text box in a list.
I have table layout data in an array[4,4]
I want the array data as a table form in Excel and
the list data as one column.

How to copy templated sheet from excel file to active workbook

I'm writing a VSTO for excel. I have the following problem:
All this is going in the excel application
I need to programmatically add a new worksheet that must look the same as a template. I've got a template from which I can copy cells and formatting to the new added cell.
How can I do this?
I have tried the following way, to open an excel application making it invisible to user and opening the necessary template in that application. I'm going through the used range rows and trying to copy row by row. However, I encounter problems in opening the template. Once it gets opened, the other time it throws COM Exception(don't know what kind of that is).
var activeExcel = (Workbook)Globals.ThisAddIn.Application.ActiveWorkbook;
Sheet = (Worksheet) activeExcel.Worksheets.Add();
Sheet.Name = "Счёт-фактура";
var sourcePath = LocationHelperTool.GetTemplatePathByName("SystemInvoice.xlsx");
try
{
var excelApp = new Application() { Visible = false };
var workbook = excelApp.Workbooks.Open(sourcePath);
var workSheets = workbook.Worksheets;
const string sourceSheetName = "Счёт-фактура";
var sourceSheet = (Worksheet)workSheets.Item[sourceSheetName];
var sourceRange = sourceSheet.UsedRange;
for (var i = 1; i <= sourceRange.Rows.Count; i++)
{
var soRange = sourceRange.Rows[i];
var deRange = Sheet.Rows[i];
soRange.Copy(Type.Missing);
deRange.pasteSpecial(XlPasteType.xlPasteFormats);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Clipboard.Clear();
excelApp.Quit();
}
I want to open a new sheet in the excel instance that the user is interacting and that sheet should be an exact clone of the template
The reason the code runs only once appears to be because the COM objects it instantiates are not being released - so they can't be re-used:
var excelApp = new Application() { Visible = false };
var workbook = excelApp.Workbooks.Open(sourcePath);
var workSheets = workbook.Worksheets;
const string sourceSheetName = "Счёт-фактура";
var sourceSheet = (Worksheet)workSheets.Item[sourceSheetName];
In the clean-up for this section of code you need something along these lines, where the objects are released in the reverse order they were instantiated (when the later depends on the earlier). Then the released objects need to be garbage-collected in order to ensure the code can no longer "see" them.
sourceRange = null;
soRange = null;
deRange = null;
workbook.Close(false); //do not save changes
sourceSheet = null;
workSheets = null;
workbook = null;
excelApp.Quit();
excelApp = null;
GC.Collect();
GC.AwaitPendingFinalizers();
GC.Collect();
GC.AwaitPendingFinalizers();

How to protect a row of data(whole row content not column wise) in an excel using epplus & c#

worksheet.Protection.IsProtected = true;
worksheet.ProtectedRanges.Add("editable", new ExcelAddress(2, 1, RowCount1, 3));
but i have to protect few rows of data from editable fields
Try using the Style.Locked property of the ExcelRange
// Protect the worksheet
ws.Protection.IsProtected = true; // NOTE: This locks all cells by default
// Make all cells editable
ws.Cells.Style.Locked = false;
// Lock just the first row
ws.Row(1).Style.Locked = true;
// Lock a range of rows
ws.Cells["4:6"].Style.Locked = true;

Save datatable to large excel file having rows 100k in c#

I working with windows application and processing large excel file. I need to save 100k rows from datatable to excel file.
Currently my create excel function only support 65,500 rows only?
But I need to save excel file more than that. Is it possible?
If yes then, Kindly give the source?
Here is my code
public static void ExportDataSetToExcel(DataTable dt, int index, string strFilePathName)
{
Console.WriteLine("Creating Output Excel file");
string fileFomat = getExcelFileName(index) + (DateTime.Now.ToString("yyyyMMddTHHmmss"));
Microsoft.Office.Interop.Excel.Application objXL = null;
Microsoft.Office.Interop.Excel.Workbook objWB = null;
try
{
objXL = new Microsoft.Office.Interop.Excel.Application();
objWB = objXL.Workbooks.Add(1);
int sheetcount = 1;
Microsoft.Office.Interop.Excel.Worksheet objSHT = (Microsoft.Office.Interop.Excel.Worksheet)objWB.Sheets.Add();
Microsoft.Office.Interop.Excel.Range cells = objSHT.Cells;
cells.NumberFormat = "#";
//formatRange = objSHT.get_Range("b1",Type.Missing);
//formatRange.EntireRow.Font.Bold = true;
objSHT.Name = "RunOrderSheet";
for (int j = 0; j < dt.Rows.Count; j++)
{
for (int i = 0; i < dt.Columns.Count; i++)
{
//Condition to put column names in 1st row
//Excel work book indexes start from 1,1 and not 0,0
if (j == 0)
{
objSHT.Cells[1, i + 1] = dt.Columns[i].ColumnName.ToString();
}
//Writing down data
objSHT.Cells[j + 2, i + 1] = dt.Rows[j][i].ToString();
}
}
sheetcount++;
objWB.Saved = true;
objWB.SaveCopyAs(strFilePathName.Trim() + fileFomat.Trim() + ".xlsx");
objWB.Close();
objXL.Quit();
Console.WriteLine("Process done");
}
catch (Exception ex)
{
objWB.Saved = true;
objWB.Close();
objXL.Quit();
log.Error(ex.Message);
}
}
Excel and the COM interface with C# absolutely will support more than 65k lines. Just to prove that it's possible, run the following code:
public static void ExportDataSetToExcel()
{
Excel.Application objXL = null;
Excel.Workbook objWB = null;
objXL = new Microsoft.Office.Interop.Excel.Application();
objXL.Visible = true;
objWB = objXL.Workbooks.Add(1);
Excel.Worksheet objSHT = objWB.Sheets.Add();
for (int row = 1; row < 100000; row++)
{
objSHT.Cells[row, 1].Value2 = row;
}
objWB.SaveAs("c:\test\test.xlsx", Excel.XlFileFormat.xlOpenXMLWorkbook);
objWB.Close();
objXL.Quit();
}
Which leads me to several possibilities as to what your issue might be, in order of my suspicions (first being what I think is the most likely):
Since you have a try/catch block that is very nicely trapping errors and making sure that anything that happens within your loop while still saving the file, something is tripping the proverbial circuit breaker. While I don't see any glaring errors, it could be anything. Put a breakpoint within the catch block and see what ex contains. Also, make note of the specific record that is causing the error so you can step through just that one record and see where it occurs.
I noticed you are using SaveCopyAs rather than SaveAs. SaveCopyAs automatically saves in the exact same format as the original. I would think your Excel comes up with the default xlsx, but I certainly can't guarantee that. Using SaveAs with the explicit file format will guarantee it is saved in a format that supports more than 65k lines:
.
objWB.SaveAs("c:\test\test.xlsx", Excel.XlFileFormat.xlOpenXMLWorkbook);
Without knowing how large your datatable is, it might simply be a memory issue also that's tripping the try/catch.

Selecting a range in a worksheet C#

I currently have the following code opening and reading in an excel spreadsheet:
var connectionString = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=Excel 12.0;", fileNameTextBox.Text);
var queryString = String.Format("SELECT * FROM [{0}]",DETAILS_SHEET_NAME);
var adapter = new OleDbDataAdapter(queryString, connectionString);
var ds = new DataSet();
adapter.Fill(ds, DETAILS_SHEET_NAME);
DataTable data = ds.Tables[DETAILS_SHEET_NAME];
dataGridView1.DataSource = data;
dataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader);
This is all good and well except I'm not interested in the first row (Possibly first two rows as row 2 is headers) of the worksheet. How can I modify the select Query to select a range like I would in excel?
I'm interested in reading in say columns A-N in rows all rows from 2 onwards that contain data.
I also need to access a couple of specific cells on a different worksheet, I assume I have to build another adaptor with a different query string for each of those cells?
Modify Select statement including just the columns you need instead of wildcard "*" like in the following example:
("SELECT Column1, Column2 FROM DETAILS_SHEET_NAME");
You can apply additional logic in order to remove unnecessary rows, for example, a "paging solution" (i.e. selecting rows from N to M) like the following one:
Assuming the Database Table "TBL_ITEM" contains two columns (fields) of interest: “Item” column, representing the unique ID and “Rank”, which is used for sorting in ascending order, the general paging problem is stated as following: Select N-rows from the table ordered by Rank offsetting (i.e. skipping) (M-N) rows:
SELECT TOP N Item,
Rank FROM (SELECT TOP M Rank, Item FROM TBL_ITEM ORDER BY Rank)
AS [SUB_TAB] ORDER BY Rank DESC
This solution and its extensions/samples are thoroughly discussed in my article Pure SQL solution to Database Table Paging (link: http://www.codeproject.com/Tips/441079/Pure-SQL-solution-to-Database-Table-Paging)
Finally, you can use a code snippet shown below in Listing 2 to export a content of DataTable object in Excel file with plenty of customization features that could be added to a code;
Listing 2. Export DataTable to Excel File (2007/2010):
internal static bool Export2Excel(DataTable dataTable, bool Interactive)
{
object misValue = System.Reflection.Missing.Value;
// Note: don't include Microsoft.Office.Interop.Excel in reference (using),
// it will cause ambiguity w/System.Data: both have DataTable obj
Microsoft.Office.Interop.Excel.Application _appExcel = null;
Microsoft.Office.Interop.Excel.Workbook _excelWorkbook = null;
Microsoft.Office.Interop.Excel.Worksheet _excelWorksheet = null;
try
{
// excel app object
_appExcel = new Microsoft.Office.Interop.Excel.Application();
// make it visible to User if Interactive flag is set
_appExcel.Visible = Interactive;
// excel workbook object added to app
_excelWorkbook = _appExcel.Workbooks.Add(misValue);
_excelWorksheet = _appExcel.ActiveWorkbook.ActiveSheet
as Microsoft.Office.Interop.Excel.Worksheet;
// column names row (range obj)
Microsoft.Office.Interop.Excel.Range _columnsNameRange;
_columnsNameRange = _excelWorksheet.get_Range("A1", misValue);
_columnsNameRange = _columnsNameRange.get_Resize(1, dataTable.Columns.Count);
// data range obj
Microsoft.Office.Interop.Excel.Range _dataRange;
_dataRange = _excelWorksheet.get_Range("A2", misValue);
_dataRange = _dataRange.get_Resize(dataTable.Rows.Count, dataTable.Columns.Count);
// column names array to be assigned to columnNameRange
string[] _arrColumnNames = new string[dataTable.Columns.Count];
// 2d-array of data to be assigned to _dataRange
string[,] _arrData = new string[dataTable.Rows.Count, dataTable.Columns.Count];
// populate both arrays: _arrColumnNames and _arrData
// note: 2d-array structured as row[idx=0], col[idx=1]
for (int i = 0; i < dataTable.Columns.Count; i++)
{
for (int j = 0; j < dataTable.Rows.Count; j++)
{
_arrColumnNames[i] = dataTable.Columns[i].ColumnName;
_arrData[j, i] = dataTable.Rows[j][i].ToString();
}
}
//assign column names array to _columnsNameRange obj
_columnsNameRange.set_Value(misValue, _arrColumnNames);
//assign data array to _dataRange obj
_dataRange.set_Value(misValue, _arrData);
// save and close if Interactive flag not set
if (!Interactive)
{
// Excel 2010 - "14.0"
// Excel 2007 - "12.0"
string _ver = _appExcel.Version;
string _fileName ="TableExport_" +
DateTime.Today.ToString("yyyy_MM_dd") + "-" +
DateTime.Now.ToString("hh_mm_ss");
// check version and select file extension
if (_ver == "14.0" || _ver == "12.0") { _fileName += ".xlsx";}
else { _fileName += ".xls"; }
// save and close Excel workbook
_excelWorkbook.Close(true, "{DRIVE LETTER}:\\" + _fileName, misValue);
}
return true;
}
catch (Exception ex) { throw; }
finally
{
// quit excel app process
if (_appExcel != null)
{
_appExcel.UserControl = false;
_appExcel.Quit();
_appExcel = null;
misValue = null;
}
}
}
You can simply ask for no headers. Modify your connection string, add HDR=No.
For your second issue, I found this post, Maybe you'll find it helpful.

Categories