<!--toc--> |
|
## Introduction |
|
For serving static files the web server it the best option, lighttpd, nginx or Apache would do much better job then you probably can. |
|
However, when you want to apply the same permission logic to the static files as used for other dynamic contents, |
you can't use the web server directly. |
|
When serving static files you should be aware of two very important factors: |
|
|
1. Security |
2. Performance |
|
## Security |
|
|
Let's start from the first. I assume you want to let user |
to download certain files that you are controlling. |
|
For url like |
|
/some/files/[...] <- these are allowed |
|
Things you should **never do:** |
|
class my_server { |
|
my_server(...) |
{ |
dispatcher().assign("/some/files/(.*)",&my_server::serve_file,this,1) |
} |
void serve_file(std::string file_name) |
{ |
|
std::ifstream f(("some_dir_name/" + file_name).c_str()); |
if(!f) { |
response().status(404) |
} |
else { |
response().content_type("application/octet-stream") |
response().out() << f.rdbuf(); |
} |
} |
... |
} |
|
|
Because attacker can create path |
|
/some/files/../../../etc/some_secret.txt |
|
And read it... It would be **very bad**. |
|
|
If you want to serve a file create a very strict **white** list of allowed names like: |
|
|
/some/files/([a-z_0-9\.]+\.txt) |
|
Or even better convert the name to sha1 or md5 representation and save files using names generated |
from their hash. |
|
For example: |
|
name = some_dir_per_user + "/" + cppcms::util::md5hex(file_name) + ".dat"; |
|
std::ifstream f(name.c_str()); |
|
|
## Performance |
|
For small files (several K or even MB) |
|
response().out() << f.rdbuf(); |
|
Is fine and works very well. |
|
For the big files it is better to use headers like X-Send-File that are |
supported by lighttpd, apache and nginx. |
|
<http://redmine.lighttpd.net/projects/1/wiki/X-LIGHTTPD-send-file> |
|
So instead of calling |
|
|
response().content_type("application/octet-stream") |
response().out() << f.rdbuf(); |
|
You call: |
|
response().content_type("application/octet-stream") |
response().set_header("X-Lighttpd-Send-File",pysical_file_name); |
|
|
This is specially critical for big files were the |
server may for example save the entire FastCGI output |
to temporary file and before serving it (Nginx) or |
cache entire (huge) buffer in the memory (Lighttpd) |
|
`X-SendFile` usually much more effective than |
serving files via FastCGI as it reduces the data transfer |
over IPC and sometimes does not even copy file |
data to the user space. |
<!--toc-->
|
|
## Introduction
|
|
For serving static files the web server it the best option, lighttpd, nginx or Apache would do much better job then you probably can.
|
|
However, when you want to apply the same permission logic to the static files as used for other dynamic contents,
|
you can't use the web server directly.
|
|
When serving static files you should be aware of two very important factors:
|
|
|
1. Security
|
2. Performance
|
|
## Security
|
|
|
Let's start from the first. I assume you want to let user
|
to download certain files that you are controlling.
|
|
For url like
|
|
/some/files/[...] <- these are allowed
|
|
Things you should **never do:**
|
|
class my_server {
|
|
my_server(...)
|
{
|
dispatcher().assign("/some/files/(.*)",&my_server::serve_file,this,1)
|
}
|
void serve_file(std::string file_name)
|
{
|
|
std::ifstream f(("some_dir_name/" + file_name).c_str());
|
if(!f) {
|
response().status(404)
|
}
|
else {
|
response().content_type("application/octet-stream")
|
response().out() << f.rdbuf();
|
}
|
}
|
...
|
}
|
|
|
Because attacker can create path
|
|
/some/files/../../../etc/some_secret.txt
|
|
And read it... It would be **very bad**.
|
|
|
If you want to serve a file create a very strict **white** list of allowed names like:
|
|
|
/some/files/([a-z_0-9\.]+\.txt)
|
|
Or even better convert the name to sha1 or md5 representation and save files using names generated
|
from their hash.
|
|
For example:
|
|
name = some_dir_per_user + "/" + cppcms::util::md5hex(file_name) + ".dat";
|
|
std::ifstream f(name.c_str());
|
|
|
## Performance
|
|
For small files (several K or even MB)
|
|
response().out() << f.rdbuf();
|
|
Is fine and works very well.
|
|
For the big files it is better to use headers like X-Send-File that are
|
supported by lighttpd, apache and nginx.
|
|
<http://redmine.lighttpd.net/projects/1/wiki/X-LIGHTTPD-send-file>
|
|
So instead of calling
|
|
|
response().content_type("application/octet-stream")
|
response().out() << f.rdbuf();
|
|
You call:
|
|
response().content_type("application/octet-stream")
|
response().set_header("X-Lighttpd-Send-File",pysical_file_name);
|
|
|
This is specially critical for big files were the
|
server may for example save the entire FastCGI output
|
to temporary file and before serving it (Nginx) or
|
cache entire (huge) buffer in the memory (Lighttpd)
|
|
`X-SendFile` usually much more effective than
|
serving files via FastCGI as it reduces the data transfer
|
over IPC and sometimes does not even copy file
|
data to the user space.
|
|