I am trying upload multiple file using service stack. Below code is working fine for one file upload. I want to upload multiple file. Please let me know what change should be required so that below codes work for multiple files upload also.
public class Hello : IRequiresRequestStream
{
Stream RequestStream { get; set; }
}
At client side I am using 'multipart/form-data' for file upload.
See the documentation on Uploading Files, IRequiresRequestStream is only for accessing the Request Body as a Stream of Bytes, to process multiple files uploaded with multipart/form-data use the base.Request.Files property instead, e.g:
Uploading Files
You can access uploaded files independently of the Request DTO using Request.Files. e.g:
public object Post(MyFileUpload request)
{
if (this.Request.Files.Length > 0)
{
var uploadedFile = base.Request.Files[0];
uploadedFile.SaveTo(MyUploadsDirPath.CombineWith(file.FileName));
}
return HttpResult.Redirect("/");
}
ServiceStack's imgur.servicestack.net example shows how to access the byte stream of multiple uploaded files, e.g:
public object Post(Upload request)
{
foreach (var uploadedFile in base.Request.Files
.Where(uploadedFile => uploadedFile.ContentLength > 0))
{
using (var ms = new MemoryStream())
{
uploadedFile.WriteTo(ms);
WriteImage(ms);
}
}
return HttpResult.Redirect("/");
}
Related
Suppose I have an C# MVC app which has a controller method that returns one of 3 content types: image png, image jpeg, or application pdf. I have read that it is possible to have images that contain XSS payloads. What would be the best way to Encode/escape these return contents so they aren't vulnerable to XSS? The controller method looks like this:
string contentType = "image/png";
MemoryStream mem = new MemoryStream();
if (ImageFormat == null || ImageFormat == "")
{
image.Save(mem, System.Drawing.Imaging.ImageFormat.Png);
}
else
{
if (ImageFormat.ToUpper() == "PNG") image.Save(mem, System.Drawing.Imaging.ImageFormat.Png);
if (ImageFormat.ToUpper() == "JPEG")
{
image.Save(mem, System.Drawing.Imaging.ImageFormat.Jpeg);
contentType = "image/jpeg";
}
}
mem.Position = 0;
mem.Seek(0, SeekOrigin.Begin);
return this.Image(mem, contentType);
Where Image is defined the following class here:
using …
namespace x.Classes
{
public static class ControllerExtensions
{
public static ImageResult Image(this Controller controller, Stream imageStream, string contentType)
{
return new ImageResult(imageStream, contentType);
}
}
}
And the OutputStream is written to using:
using …
namespace x.Classes
{
public class ImageResult : ActionResult
{
public ImageResult(Stream imageStream, string contentType)
{
if (imageStream == null)
throw new ArgumentNullException("imageStream");
if (contentType == null)
throw new ArgumentNullException("contentType");
this.ImageStream = imageStream;
this.ContentType = contentType;
}
public Stream ImageStream { get; private set; }
public string ContentType { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = this.ContentType;
byte[] buffer = new byte[4096];
while (true)
{
int read = this.ImageStream.Read(buffer, 0, buffer.Length);
if (read == 0)
break;
response.OutputStream.Write(buffer, 0, read);
}
response.End();
}
}
}
Is there a way for me to escape/encode the buffer that is getting written to the OutputStream here:`
response.OutputStream.Write(buffer, 0, read);
To protect against XSS attacks? For example if this were HTML that was being returned:
response.OutputStream.Write(HttpUtility.HtmlEncode(buffer), 0, read);
But we know we are returning a jpeg, pdf, or png which means Html encode won't work here. So what do we use to safely escape/encode an image/pdf?
By the time you have buffer ready, it's too late. The same as with HTML, you want to context-sensitively encode any user input in those files, not the whole thing.
Now, with images this doesn't make much sense in the context of XSS, an image is rendered by an image renderer, and not as html, so there won't be any javascript to be run. The general best practice for uploaded images is to process them on the server and save them as a new image, because this removes all unnecessary things, but it has its risks as well if your processor itself is the target of an attack.
SVG for example is a different beast, SVG can have code in it, as can PDF. But again, PDFs will be open on the client with a PDF viewer, not in the context of the web application even if the PDF viewer is the browser itself (the browser hopefully separates Javascript in the PDF from the web page even if the origin is the same).
But javascript in a PDF can still be an issue for the client. Javascript running in a PDF may do harmful things, the simplest of which is consume client resources (ie. DoS of some sort), or it may try to break out of the PDF context somehow exploiting a viewer vulnerability. So the attack would be that one user uploads a malicious PDF for others to download. I think the best you can do against this is scan uploaded files for malware (which you should do anyway).
If you are generating all of this from user input (images, PDFs), then the libraries you use should take care of properly encoding values so that a malicious user can't inject code in a PDF. When the PDF is already generated, you can't "fix" it anymore, user input is mixed with code.
Also make sure to set the following header in responses (along with the correct Content-Type of course):
X-Content-Type-Options: nosniff
You do not need to encode the images themselves, you need to encode/escape the links to the images.
For example:
Link Title
where image.url.png?logout comes from user input.
You would url encode image.url.png?logout as image.url.png%3Flogout so that it is rendered useless to an attacker.
I'm using MvcRazorToPdf in a Azure website and create my PDF's and output them in the browser.
Now i'm creating a new function to directly email the PDF as attachment (without output them in the browser).
Does anybody know if it is possible to save the PDF (with MvcRazorToPdf) as a MemoryStream or Byte[]?
I think you can handle this in ResultFilter, I used below code to allow user to download file and prompt for download popup, in this way you can grab all your memory stream and store somewhere to send email afterwords.
public class ActionDownloadAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.AddHeader("content-disposition", "attachment; filename=" + "Report.pdf");
base.OnResultExecuted(filterContext);
}
}
[ActionDownload]
public ActionResult GeneratePdf()
{
List<Comment> comments = null;
using (var db = new CandidateEntities())
{
comments = db.Comments.ToList();
}
return new PdfActionResult("GeneratePdf", comments);
}
I have implemented something like that. So basically I have not been changing my method to output PDF. What I have done is used restsharp to make request at URL where I get PDF then what you have is in lines of (this is partial code only so you can get idea )
var client = new RestClient(IAPIurl);
var request = new RestRequest(String.Format(IAPIurl_generatePDF, targetID), Method.GET);
RestResponse response = (RestResponse) client.Execute(request);
// Here is your byte array
response.RawBytes
Otherwise you can use my answer from here where I discussed directly returning a file.
Hope this helps!
I implemented a POST Rest service to upload files to my server. the problem i have right now is that i want to restrict the uploaded files by its type. lets say for example i only want to allow .pdf files to be uploaded.
What I tried to do was
Task<Stream> task = this.Request.Content.ReadAsStreamAsync();
task.Wait();
FileStream requestStream = (FileStream)task.Result;
but unfortunately its not possible to cast the Stream to a FileStream and access the type via requestStream.Name.
is there an easy way (except writing the stream to the disk and check then the type) to get the filetype?
If you upload file to Web API and you want to get access to file data (Content-Disposition) you should upload the file as MIME multipart (multipart/form-data).
Here I showed some examples on how to upload from HTML form, Javascript and from .NET.
You can then do something like this, this example checks for pdf/doc files only:
public async Task<HttpResponseMessage> Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable,
"This request is not properly formatted - not multipart."));
}
var provider = new RestrictiveMultipartMemoryStreamProvider();
//READ CONTENTS OF REQUEST TO MEMORY WITHOUT FLUSHING TO DISK
await Request.Content.ReadAsMultipartAsync(provider);
foreach (HttpContent ctnt in provider.Contents)
{
//now read individual part into STREAM
var stream = await ctnt.ReadAsStreamAsync();
if (stream.Length != 0)
{
using (var ms = new MemoryStream())
{
//do something with the file memorystream
}
}
}
return Request.CreateResponse(HttpStatusCode.OK);
}
}
public class RestrictiveMultipartMemoryStreamProvider : MultipartMemoryStreamProvider
{
public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
{
var extensions = new[] {"pdf", "doc"};
var filename = headers.ContentDisposition.FileName.Replace("\"", string.Empty);
if (filename.IndexOf('.') < 0)
return Stream.Null;
var extension = filename.Split('.').Last();
return extensions.Any(i => i.Equals(extension, StringComparison.InvariantCultureIgnoreCase))
? base.GetStream(parent, headers)
: Stream.Null;
}
}
Summary
I need to retrieve attachments stored in a parent app from a link in a client of a child app. The attachments are available in the parent app via a web service call -- which returns a standard FileContentResult with content type "application/octet-stream". The best way I can think is to retrieve this via a WebRequest and pass the resulting response stream to a FileStreamResult, though I have some alternatives available.
Does anyone know if, when making a WebRequest, the response stream becomes available immediately once the first part of the response is returned or is it buffered so I don't get the response until all data has been retrieved?
Are there any other options than those listed in the full question below for doing this that I'm missing? (Other than keeping the attachments in both child and parent DBs -- I really don't want to do this since then I'd need to regularly synchronize them, too).
TLDR Version
I have two related applications which communicate through a RESTful web service. The parent application maintains a collection of entities which may have attachments. For example, a Request might have an Excel spreadsheet as an attachment. The entity and its attachment are stored in the database and access to the attachment is controlled using the same logic as access to the Request. That is, you should not be able to download an attachment if you cannot view the Request.
In the child application I maintain some integration glue for the entities assigned to a particular institution -- the app is used to communicate between our Board of Regents and each Regents school. I don't want to maintain and synchronize the full entity/attachment. I only want to maintain enough information to allow me to connect to the web service in the parent app and get the details for entities that the particular instance of the child application has access to.
This works well for the entity data itself. The amount of data is small and the overhead of buffering in the child application doesn't present a signficant delay in accessing the data. If necessary, I could cache the data locally to avoid performance penalities.
My concern is the attachments. I've considered three different mechanisms for providing access to the attachment from a client of the child application.
Generate a one-time use token and associated url that allows the client to directly download the attachment from the parent application. The token generation web service call would ensure that users of the child application should have access to the attachment. The drawback to this is that you'd only be able to click on the link once in the client. Clicking again would result in an error rather than getting the attachment.
Buffer the attachment in the child app. In this scenario I would provide a controller/action to download the attachment in the child app, then call a web service method to get the attachment and have the child app send the attachment as a FileContentResult. This removes the issue of only being able to click the link once, but the attachments could be reasonably large and buffering the data in the child application could potentially double the amount of time to download the attachment and, worse, incur a significant delay before the attachment download begins.
Link in the child app, but provide the stream from the web service request directly to a FileStreamResult. This seems, to me, to be the best option as the FileStreamResult reads in chunks rather than having to have all the data available before it is sent to the client. The only drawback that I can see here is that I can no longer dispose of the WebResponse directly as the FileStreamResult won't be executed until after my action returns.
Here is what I have for the code for API wrapper code for (2) and (3):
private class ResponseModel<T> : IDisposable
{
public T Model { get; set; }
public WebResponse Response { get; set; }
private bool Disposed { get; set; }
private void Dispose( bool disposing )
{
if (!Disposed)
{
if (disposing)
{
((IDisposable)this.Response).Dispose();
}
Disposed = true;
}
}
public void Dispose()
{
Dispose( true );
}
}
private ResponseModel<T> GetAttachmentResponse<T>( long id ) where T : IDownloadModel, new()
{
var request = GetRequest( string.Format( "{0}/api/getattachment/{1}/{2}", this.BaseUrl, this.Key, id ) );
var response = request.GetResponse();
var model = (T)Activator.CreateInstance<T>();
var contentDisposition = response.Headers["Content-Disposition"];
if (!string.IsNullOrEmpty( contentDisposition ))
{
var filename = contentDisposition.Split( new[] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries )
.SingleOrDefault( s => s.StartsWith( "filename", StringComparison.OrdinalIgnoreCase ) );
if (!string.IsNullOrEmpty( filename ))
{
model.Name = filename.Split( '=' ).Skip( 1 ).FirstOrDefault();
}
}
if (string.IsNullOrEmpty( model.Name ))
{
model.Name = "untitled";
}
return new ResponseModel<T> { Model = model, Response = response };
}
public FileDownloadModel GetAttachment( long id )
{
using (var response = GetAttachmentResponse<FileDownloadModel>( id ))
{
var reader = new BinaryReader( response.Response.GetResponseStream() );
response.Model.Content = reader.ReadBytes( (int)response.Response.ContentLength );
return response.Model;
}
}
public FileStreamDownloadModel GetAttachmentStream( long id )
{
// since we're returning the stream, we can't dispose of the response when done.
var response = GetAttachmentResponse<FileStreamDownloadModel>( id );
response.Model.Stream = response.Response.GetResponseStream();
return response.Model;
}
public interface IDownloadModel
{
string ContentType { get; }
string Name { get; set; }
}
Model classes
public class FileDownloadModel : IDownloadModel
{
public byte[] Content { get; set; }
public string Name { get; set; }
public string ContentType { get { return "application/octet-stream"; } }
}
public class FileStreamDownloadModel : IDownloadModel
{
public Stream Stream { get; set; }
public string Name { get; set; }
public string ContentType { get { return "application/octet-stream"; } }
}
I would suggest a variant on Option 1 [call it Option 1(a)].
Instead of generating a one-time token, "borrow" the MVC AntiForgeryToken classes, and have your parent application return a custom token and cookie to the child app for inclusion in the form returned to the user.
If the child application may have links for multiple documents on a single page, in the request for the token information, have the child app submit a unique identifier (identifying the page request from the user) as part of the request. You can then use this identifier in generating the tokens, and you can store the identifier as part of the verification process. This will give you a multi-use token, unique for each link on the page.
Slap an expiration time on the unique identifier, and you should be good to go.
I am uploading a file using the following code
[HttpPost]
public ActionResult ImportDeleteCourse(ImportFromExcel model)
{
var excelFile = model.ExcelFile;
if (ModelState.IsValid)
{
OrganisationServices services = new OrganisationServices();
string filePath = Path.Combine(HttpContext.Server.MapPath("../Uploads"),
Path.GetFileName(excelFile.FileName));
excelFile.SaveAs(filePath);
// ... snipped //
}
}
I do not really need to do store the uploaded excel file. Is there anyway I can process it without saving?
Note: The ImportFromExcel class is nothing but a model, which is basically:
public class ImportFromExcel
{
[Required(ErrorMessage = "Please select an Excel file to upload.")]
[DisplayName("Excel File")]
public HttpPostedFileWrapper ExcelFile { get; set; }
}
The most interesting part is that it wraps a HttpPostedFileWrapper.
Sure you can. As Patko suggested, the InputStream property can be used for another stream. For example I did this for an uploaded xml document to use with LINQ to XML:
XDocument XmlDoc = XDocument.Load(new StreamReader(viewmodel.FileUpload.InputStream))
Cheers,
Chris
The HttpPostedFileBase.InputStream property looks promising. You should be able to use that and save the data to whichever other stream you need to.