I have a dotnet site which contains a virtual directory (/ArticleImages) which maps to a file share on another server. The file share is accessible to a large number of people so, as a security measure, I do not want any asp.net pages to execute in this folder (e.g. putting default.aspx in the file share and browsing to site.com/ArticleImages/default.aspx should either not serve or, preferably, serve as a simple download rather than executing).
I'm using IIS 6.0 and added the virtual directory. If I remove the application from this folder, it uses the parent application and complains that it can't read web.config. If I add an application to this folder, even if I remove all application extensions, it complains that svc-xyzzy (the account used to access the share) doesn't have access to 'C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files'.
How do I have a sub folder of an application which does not execute dotnet code?
If the file share is readable by the user that your app pool is running under (Network Service by default) you can remove the virtual directory completely and create an ASP.NET application that will stream the files to the browser. If you're using MVC it's simply returning a file result. This has an added benefit in that you will be able to restrict the users from downloading the files. i.e. You can require that a user is logged in or has certain permissions to download the files. Also make sure you test for path traversal, you would not want a user entering ../../filename to download files they are not permitted to.
Option 1: ASP.NET MVC
public ActionResult Download(string file)
{
// Check for directory traversal attack
if(file.IndexOf('\\') > -1 || file.IndexOf('/') > -1)
{
return new HttpNotFoundResult();
}
file = System.IO.Path.Combine("\\FILE_SHARE_FOLDER\\", file);
if(!System.IO.File.Exists(file))
{
return new HttpNotFoundResult();
}
return this.File(file, GetMimeType(file));
}
Option 2: Webforms
private void DownloadFile(string file)
{
// Check for directory traversal attack
if(file.IndexOf('\\') > -1 || file.IndexOf('/') > -1)
{
Response.StatusCode = 404;
Response.End();
}
file = System.IO.Path.Combine("\\FILE_SHARE_FOLDER\\", file);
if (!System.IO.File.Exists(file))
{
Response.StatusCode = 404;
Response.End();
}
Response.ContentType = GetMimeType(file);
Response.TransmitFile(file);
}
Note You will need a method to get the MIME Types for both MVC and Webforms (MIME Type Method From KodeSharp)
private string GetMimeType(string fileName)
{
string mimeType = "application/unknown";
string ext = System.IO.Path.GetExtension(fileName).ToLower();
Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
if (regKey != null && regKey.GetValue("Content Type") != null)
mimeType = regKey.GetValue("Content Type").ToString();
return mimeType;
}
You can check on global.asax for the request, and if is coming from the directories that you not allow, then stop the processing as:
protected void Application_BeginRequest(Object sender, EventArgs e)
{
string cTheFile = HttpContext.Current.Request.Path;
if(cTheFile.StartsWith("/articleimages", StringComparison.CurrentCultureIgnoreCase)
{
HttpContext.Current.Response.TrySkipIisCustomErrors = true;
HttpContext.Current.Response.Write("Please start from home page");
HttpContext.Current.Response.StatusCode = 403;
HttpContext.Current.Response.End();
return ;
}
}
Of course you can simple place one extra web.config on the directory with this inside:
<configuration>
<system.web>
<authorization>
<deny users="*" />
</authorization>
</system.web>
</configuration>
but if they can delete it is not useful as the code.
Related
I use fileUpload control and i can save the image but when i try to delete it gives a security error like this :
Request for the permission of type 'System.Security.Permissions.FileIOPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
it works in my localhost but not in web.
I tried to add many kind of stuff to web config file but it didnt work i dont know why.
Why i can save file but cant delete. It might be about System.Security.Permissions.FileIOPermission maybe... here is my code :
protected void btnDelete_Click(object sender, EventArgs e)
{
try
{
if (!String.IsNullOrEmpty(imgLogo.ImageUrl))
{
int index = imgLogo.ImageUrl.LastIndexOf('.');
string fileExt = imgLogo.ImageUrl.Substring(index);
string defPath = Business.DefinitionsData.getDefaultLogoPath();
string entId = ((xOrgProject.DataAccess.EnterpriseUserTable)Session["Enterprise"]).EnterpriseUserId.ToString();
string FullPath = Server.MapPath(defPath) + entId + fileExt;
FileInfo file = new FileInfo(FullPath);
if (file.Exists)
{
file.GetAccessControl();
file.Delete();
Business.DefinitionsData.UpdateEntLogoPath(int.Parse(entId), null);
imgLogo.ImageUrl = null;
imgLogo.Visible = false;
btnDelete.Visible = false;
btnUpload.Visible = true;
Fu1.Enabled = true;
StatusLabel.Text = "Kaldırıldı.";
}
}
}
catch (Exception ex)
{ StatusLabel.Text = ex.Message; }
}
As it runs fine locally the issue is most likely due to the configuration on the web server. Or in my experience this has often been the case.
Have you tried modifying the trust level in the machine.config file on the web server?
Also what authentication are you using on the web server?
Running it locaally you will have access to your machine but if you are using impersonation on the web server that anonymous account ID may not have the relevant server permissions to delete files which will throw a security exception.
thank you for your answer, i got my solution. I have wrote this code in uploading button click event then its solved. I wasnt disposing before. but now its good. thanks again.
System.Drawing.Image img = System.Drawing.Image.FromFile(save);
img.Dispose();
I need to copy the images from C:/images folder to my web application folder which is running in the server.I used the following code which work well in local application but not work in server
string sourcePath = #"D:\images";
//string destinationPath = #"D:\a";
string destinationPath = Server.MapPath("SMSImages") + "\\";
if (System.IO.Directory.Exists(sourcePath))
{
string[] files = System.IO.Directory.GetFiles(sourcePath);
foreach (string s in files)
{
fileName = Path.GetFileName(s);
destFile = Path.Combine(destinationPath, fileName);
File.Copy(s, destFile, true);
}
how to copy
Servers often have a lot of security limitations for the IIS user.
Check if the user under which you are running your asp.net process has authorization to access this path.
You can log the exceptions that are occurring in this code to see if it is causing an access violation.
The following code can help you check if code if you have access
UserFileAccessRights rights = new UserFileAccessRights(sourcePath);
if (rights.canWrite() && rights.canRead()) {
lblLogMsg.Text = "R/W access";
} else {
if (rights.canWrite()) {
lblLogMsg.Text = "Only Write access";
} else if (rights.canRead()) {
lblLogMsg.Text = "Only Read access";
} else {
lblLogMsg.Text = rights.ToString();
}
}
It doesn't work because the program search a D:\ path in server not in local system.
I'm trying to connect to a Report (rdlc file) using ASP.NET Web Applications. I'm working with VS2010 and the Report Server is version 2008.
I have the following URL to the report which works fine:
http://server url/Products/_layouts/ReportServer/RSViewerPage.aspx?rv:RelativeReportUrl=/Products/Dashboards/Product_tool.rdl&Source=Server Url/Products/Dashboards/Forms/AllItems.aspx&DefaultItemOpen=1
When i enter that URL in my browser it first asks for a username password. When i log in then the Report shows up just fine.
Now i need to display this report in a Report Viewer. So i added a Report Viewer control to my aspx page. I configured the URls for it like so:
Report Server:** http://server url/Products/_layouts/ReportServer
Report Path:** /Products/Dashboards/Product_tool.rdl
I'm not really sure if that is even correct..?
In any case, in my PageLoad i have the following line of code:
eportViewer1.ServerReport.ReportServerCredentials = new ReportCredentials("myuser", "mypass");
The ReposrtCredentials class is taken from: http://social.msdn.microsoft.com/Forums/en-US/vsreportcontrols/thread/c65abca7-0fdb-40fb-aabe-718f63377a55/ (from Phil)
Now when i run my Web Application i get the following error:
The attempt to connect to the report server failed. Check your
connection information and that the report server is a compatible
version.
Now i'm not sure if the URL i supplied to the Report Viewer is right? Or what the problem else could be.
Anyone any idea..?
In order to Integrate SSRS Reports into an ASP.NET application, follow these steps.
Firstly, Implement IReportServerConnection2 interface. I did something like this:
public sealed class CustomReportServerConnection : IReportServerConnection2
{
public WindowsIdentity ImpersonationUser
{
get
{
// Use the default Windows user. Credentials will be
// provided by the NetworkCredentials property.
return null;
}
}
public ICredentials NetworkCredentials
{
get
{
// Read the user information from the web.config file.
// By reading the information on demand instead of
// storing it, the credentials will not be stored in
// session, reducing the vulnerable surface area to the
// web.config file, which can be secured with an ACL.
// User name
string userName = ConfigurationManager.AppSettings[Utility.Constants.AppConst.REPORT_USER].ToString();
if (string.IsNullOrEmpty(userName))
throw new Exception(Utility.Constants.AppConst.MESSAGE_MISSING_USER_NAME);
// Password
string password = ConfigurationManager.AppSettings[Utility.Constants.AppConst.REPORT_PASSWORD].ToString();
if (string.IsNullOrEmpty(password))
throw new Exception(Utility.Constants.AppConst.MESSAGE_MISSING_PWD);
// Domain
string domain = ConfigurationManager.AppSettings[Utility.Constants.AppConst.REPORTS_DOMAIN].ToString();
if (string.IsNullOrEmpty(domain))
throw new Exception(Utility.Constants.AppConst.MESSAGE_MISSING_DOMAIN);
return new NetworkCredential(userName, password, domain);
}
}
public bool GetFormsCredentials(out Cookie authCookie, out string userName, out string password, out string authority)
{
authCookie = null;
userName = null;
password = null;
authority = null;
// Not using form credentials
return false;
}
public Uri ReportServerUrl
{
get
{
string url = ConfigurationManager.AppSettings[Utility.Constants.AppConst.REPORT_SERVER_URL].ToString();
if (string.IsNullOrEmpty(url))
throw new Exception(Utility.Constants.AppConst.MESSAGE_MISSING_URL);
return new Uri(url);
}
}
public int Timeout
{
get
{
return int.Parse(ConfigurationManager.AppSettings[Utility.Constants.AppConst.REPORT_SERVER_TIME_OUT].ToString());
// return 60000; // 60 seconds
}
}
public IEnumerable<Cookie> Cookies
{
get
{
// No custom cookies
return null;
}
}
public IEnumerable<string> Headers
{
get
{
// No custom headers
return null;
}
}
}
Now in your Configuration AppSettings place following keys ( or provide these values from wherever you want):
<add key="ReportServerUrl" value="http://sqlServerURL/ReportServer_SQL2008R2"/>
<!--Development TargetReportFolder-->
<add key="TargetReportFolder" value="/AppReporting/"/>
<add key="ReportServerTimeOut" value="600000"/>
<add key="ReportViewerServerConnection" value="FullyQualified Name of ur CustomReportServerConnection,ProjectName"/>
<add key="ReportsUser" value="ReportUser"/>
<add key="ReportsPassword" value="reportPassword"/>
<add key="ReportsDomain" value="myDomain"/>
Now , in your .aspx page, drag a reportViewer something like this:
<rsweb:ReportViewer ID="RptViewer" runat="server" AsyncRendering="False" SizeToReportContent="true"
ProcessingMode="Remote" Width="100%" BackColor="#F7F8F9" OnReportError="RptViewer_ReportError"
OnReportRefresh="RptViewer_ReportRefresh1" Height="">
</rsweb:ReportViewer>
and configure your ReportViewer in the codeBehind..
place your ReportParameter properly.
it shud give you an idea...
point is, you need to authenticate properly, hence writing your custom ReportServerConnection
When you configure your report viewer,check whether the account you use has permission to view the report because it is necessary that you have access when using server report.
Check out this link too. They will be of help : http://forums.asp.net/t/1562624.aspx/1
I uploaded nopcommerce solution to appharbor (using this method Can't build notcommerce project under appharbor) and solution succesfully builded, but I receiving 403 error - Forbidden: Access is denied when trying to open page(Allow write-access to file system is set to true).
Thanks and hope for your help
The problem is that the standard NopCommerce solution contains two Web Projects. AppHarbor only deploys one web project per application, and in this case, we happen to deploy Nop.Admin which is not what you want.
To resolve this, you should take advantage of the AppHarbor solution file convention and create an AppHarbor.sln solution file that only references the Nop.Web project.
We use a wrapper in our base controller to ensure that all of our code is oblivious to appharbor port changing.
First, fix in Webhelper.cs:75
public virtual string GetThisPageUrl(bool includeQueryString, bool useSsl)
{
string url = string.Empty;
if (_httpContext == null)
return url;
if (includeQueryString)
{
string storeHost = GetStoreHost(useSsl);
if (storeHost.EndsWith("/"))
storeHost = storeHost.Substring(0, storeHost.Length - 1);
url = storeHost + _httpContext.Request.RawUrl;
}
else
{
#if DEBUG
var uri = _httpContext.Request.Url;
#else
//Since appharbor changes port number due to multiple servers, we need to ensure port = 80 as in AppHarborRequesWrapper.cs
var uri = new UriBuilder
{
Scheme = _httpContext.Request.Url.Scheme,
Host = _httpContext.Request.Url.Host,
Port = 80,
Path = _httpContext.Request.Url.AbsolutePath,
Fragment = _httpContext.Request.Url.Fragment,
Query = _httpContext.Request.Url.Query.Replace("?", "")
}.Uri;
#endif
url = uri.GetLeftPart(UriPartial.Path);
}
url = url.ToLowerInvariant();
return url;
}
So what we did is simply add files from https://gist.github.com/1158264 into Nop.Core\AppHarbor
and modified base controllers:
nopcommerce\Presentation\Nop.Web\Controllers\BaseNopController.cs
public class BaseNopController : Controller
{
protected override void Initialize(RequestContext requestContext)
{
//Source: https://gist.github.com/1158264
base.Initialize(new RequestContext(new AppHarborHttpContextWrapper(System.Web.HttpContext.Current),
requestContext.RouteData));
}
//Same file from here downwards...
}
nopcommerce\Presentation\Nop.Web.Admin\Controllers\BaseNopController.cs
public class BaseNopController : Controller
{
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
//set work context to admin mode
EngineContext.Current.Resolve<IWorkContext>().IsAdmin = true;
//Source: https://gist.github.com/1158264
base.Initialize(new RequestContext(new AppHarborHttpContextWrapper(System.Web.HttpContext.Current), requestContext.RouteData));
//base.Initialize(requestContext);
}
//Same file from here downwards...
}
Enable the Directory Browsing feature in IIS Express
Note This method is for the web developers who experience the issue when they use IIS Express.
To do this, follow these steps:
Open a command prompt, and then go to the IIS Express folder on your computer. For example, go to the following folder in a command prompt:
C:\Program Files\IIS Express
Type the following command, and then press Enter:
appcmd set config /section:directoryBrowse /enabled:true
refrence :https://support.microsoft.com/en-us/kb/942062
I have a ASP.NET SOAP web service whose web method creates a PDF file, writes it to the "Download" directory of the applicaton, and returns the URL to the user. Code:
//Create the map images (MapPrinter) and insert them on the PDF (PagePrinter).
MemoryStream mstream = null;
FileStream fs = null;
try
{
//Create the memorystream storing the pdf created.
mstream = pgPrinter.GenerateMapImage();
//Convert the memorystream to an array of bytes.
byte[] byteArray = mstream.ToArray();
//return byteArray;
//Save PDF file to site's Download folder with a unique name.
System.Text.StringBuilder sb = new System.Text.StringBuilder(Global.PhysicalDownloadPath);
sb.Append("\\");
string fileName = Guid.NewGuid().ToString() + ".pdf";
sb.Append(fileName);
string filePath = sb.ToString();
fs = new FileStream(filePath, FileMode.CreateNew);
fs.Write(byteArray, 0, byteArray.Length);
string requestURI = this.Context.Request.Url.AbsoluteUri;
string virtPath = requestURI.Remove(requestURI.IndexOf("Service.asmx")) + "Download/" + fileName;
return virtPath;
}
catch (Exception ex)
{
throw new Exception("An error has occurred creating the map pdf.", ex);
}
finally
{
if (mstream != null) mstream.Close();
if (fs != null) fs.Close();
//Clean up resources
if (pgPrinter != null) pgPrinter.Dispose();
}
Then in the Global.asax file of the web service, I set up a Timer in the Application_Start event listener. In the Timer's ElapsedEvent listener I look for any files in the Download directory that are older than the Timer interval (for testing = 1 min., for deployment ~20 min.) and delete them. Code:
//Interval to check for old files (milliseconds), also set to delete files older than now minus this interval.
private static double deleteTimeInterval;
private static System.Timers.Timer timer;
//Physical path to Download folder. Everything in this folder will be checked for deletion.
public static string PhysicalDownloadPath;
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
deleteTimeInterval = Convert.ToDouble(System.Configuration.ConfigurationManager.AppSettings["FileDeleteInterval"]);
//Create timer with interval (milliseconds) whose elapse event will trigger the delete of old files
//in the Download directory.
timer = new System.Timers.Timer(deleteTimeInterval);
timer.Enabled = true;
timer.AutoReset = true;
timer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimedEvent);
PhysicalDownloadPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "Download";
}
private static void OnTimedEvent(object source, System.Timers.ElapsedEventArgs e)
{
//Delete the files older than the time interval in the Download folder.
var folder = new System.IO.DirectoryInfo(PhysicalDownloadPath);
System.IO.FileInfo[] files = folder.GetFiles();
foreach (var file in files)
{
if (file.CreationTime < DateTime.Now.AddMilliseconds(-deleteTimeInterval))
{
string path = PhysicalDownloadPath + "\\" + file.Name;
System.IO.File.Delete(path);
}
}
}
This works perfectly, with one exception. When I publish the web service application to inetpub\wwwroot (Windows 7, IIS7) it does not delete the old files in the Download directory. The app works perfect when I publish to IIS from a physical directory not in wwwroot. Obviously, it seems IIS places some sort of lock on files in the web root. I have tested impersonating an admin user to run the app and it still does not work. Any tips on how to circumvent the lock programmatically when in wwwroot? The client will probably want the app published to the root directory.
Your problem may be related to the fact that IIS reloads the Web Service Application if the directory or files contained in the main folder changes.
Try creating / deleting files in a temporary folder which is outside the root folder of your application (be aware of permissions on the folder to allow IIS to read/write files).
Instead of writing directly to the file system, why not use isolated storage?
http://msdn.microsoft.com/en-us/library/system.io.isolatedstorage.isolatedstorage.aspx
This should solve any location or permission based issues that you are having
I forgot to come back and answer my question.
I had to give the IIS_IUSRS group Modify permissions to the directory where I was reading/writing files.
Thanks to all those who answered.