Prevent direct access to folder file by typing URL

3k views Asked by At

There is a asp.net webform application hosted on IIS. There are pdf and image files in the project folder called upload.

Those files in the upload folder can be accessed through the link in the web application. Users are being Authenticated via username and password which are stored in the database.

Anyone can access the pdf and image files by typing the URL (example: https//myapplication.com/uploads/11%20pics.pdf) in the browser without logging into the system.

Is there a way to restrict the URL so that only logged in users can access them?

I have tried to Deny the Read access for IUSR user on the folder but it stops logged in users to view the file.

enter image description here

1

There are 1 answers

0
Albert D. Kallal On

Actually, a much simple way to do this?

since you NEVER want any user or anyone to type in that URL?

Then just use internet security settings on that folder. Remember, code behind does NOT use the web based URL's, and MORE important does not respect the IIS (web based) URL security settings either!!!

Code behind always uses plane jane simple windows file names.

You can try and mess around with actaul folder rights, but you really don't need to.

All you have to do is "deny" any and all users by using the Web based secuirty settings. You can easy do that by dropping in a web config file into the same folder as the UpLoadFiles folder.

Hence, just drop in a web config like you do to secure all folders based on say role, and drop in this:

  <?xml version="1.0" encoding="utf-8"?>
  <configuration>
    <system.web>
      <authorization>
        <deny users="*" />
      </authorization>
    </system.web>
  </configuration>

So, now, if any one try's to type in a url - they will be denied access to that folder.

but, your code behind does NOT care, does NOT use, and is obvious to the URL's and that IIS security (web.config) file you use.

Your code behind 100% ignores the logon rights - including role memebership etc.

So, in code behind, I can still freely open + read, and do whatever.

    Dim strFileURL As String = "~/UpLoadFiles/def.txt"
    Dim strInternalFile As String = Server.MapPath(strFileURL)


    Dim strbuf As String

    strbuf = File.ReadAllText(strInternalFile)

    Debug.Print(Len(strbuf))

However, you don't mention how those supposed "links" you have. Since if we secure that folder from all users, then of course users can't use a hyper link.

but, that's not really a big deal, since if you driving the up-loaded files in a database - say by users, then in place of say a "link" to download the file, just read and stream out the file to the user.

So, say we have this:

I don't know if you save JUST the file name, or the whole path name in the database.

But, say a simple grid like this:

<asp:GridView ID="Gfiles" runat="server" 
    DataKeyNames="ID" AutoGenerateColumns="False"  CssClass="table" >
    <Columns>
        <asp:BoundField  DataField="FileName" HeaderText="FileName"  />
        <asp:BoundField  DataField="Size" HeaderText="Size"  />
        <asp:BoundField  DataField="UpLoadTime" HeaderText="UpLoadTime"  />

        <asp:TemplateField HeaderText ="Download" ItemStyle-HorizontalAlign="Center">
            <ItemTemplate>
                <asp:Button ID="cmdDownLoad" runat="server" 
                    Text="Download" CssClass="btn"
                    OnClick="cmdDownLoad_Click"
                    />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

And say load up like this:

Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    Dim strSQL As String = "SELECT * FROM MyUpLoadFiles ORDER BY UpLoadTime"
    Dim cmdSQL As New SqlCommand(strSQL)

    Gfiles.DataSource = MyRstP(cmdSQL)
    Gfiles.DataBind()
    ' hide up-loader
    AjaxFileUpload1.Visible = False

End Sub

Public Function MyRstP(cmdSQL As SqlCommand) As DataTable

    Dim rstData As New DataTable
    Using cmdSQL
        Using conn = New SqlConnection(My.Settings.TEST4)
            cmdSQL.Connection = conn
            conn.Open()
            rstData.Load(cmdSQL.ExecuteReader)
        End Using
    End Using
    Return rstData

End Function

And we now have this:

enter image description here

So, we do NOT use a hyper-link for the download.

We fetch the file name - from that database.

Say, like this buttion click event for the grid view row

Protected Sub cmdDownLoad_Click(sender As Object, e As EventArgs)

    Dim btn As Button = sender
    Dim gRow As GridViewRow = btn.NamingContainer

    Dim sFile As String = gRow.Cells(0).Text

    Dim sInternalFile = Server.MapPath("~/UpLoadFiles/" & sFile)
    ' now stream file down to browser
    If File.Exists(sInternalFile) Then

        Dim strConType As String = MimeMapping.GetMimeMapping(sInternalFile)

        Dim binFile As Byte() = File.ReadAllBytes(sInternalFile)
        Response.ContentType = strConType
        Response.AppendHeader("Content-Disposition", "attachment; filename=" + sFile)
        Response.BinaryWrite(binFile)
        Response.End()

    End If

End Sub

So, now we prevent url's - and in fact NEVER have to even expose where we tucked away the files. We only ever display the file name, but the path name and folder? Never exposed.

Of course, you would load the grid of files based on "user id" or some such.

And note that you DO have to include the "mine" mapping type. This does require .net 4.5 or later. (or you can save the mine type in a database column).