<!--toc--> |
|
## Introduction |
|
Security is one of the major concerns in the development of web applications as they are instantly exposed to the general public where there are a lot of potential attackers. |
|
Writing web applications today is very similar to walking |
over a minefield. If you don't have proper tools and knowledge you are most likely going to loose a "virtual leg". |
|
## State of Mind |
|
There are several golden rules in writing secure web applications: |
|
1. Never trust user input. |
2. Think - "How can I attack my application" |
3. Learn the common vulnerabilities and methods to prevent them. |
|
So when you're writing web applications, always remember that there *is* somebody who would actually try |
to do something bad to your site. |
|
What is even more important is that at some point, you |
_will_ make a mistake and your web site |
may be cracked. Then you'll have to do the hard work |
of restoring it. So keep backups and even more importantly |
check your [restore procedures](http://www.joelonsoftware.com/items/2009/12/14.html) work. |
|
## SQL Injections |
|
This is one of the most basic problems that |
web developer should be familiar with. |
|
Have you ever written code like: |
|
std::string query= |
"SELECT 1 from users " |
"WHERE username='" + username +"' AND " |
" password='" + password "'; |
|
mysql_query(conn,query.c_str()); |
... |
|
What's the problem? |
|
Assume that the password value is |
|
' OR ''=' |
|
So the query would look like |
|
SELECT 1 from users |
WHERE username='user' AND |
password='' OR ''='' |
|
Which would always return 1... |
|
So you get authenticated for knowing SQL `:-)` and not for |
having a proper password |
|
If you write code like this then you should not continue |
web development until you've read this article: |
|
[SQL Injection](http://en.wikipedia.org/wiki/SQL_injection) |
|
How do you prevent SQL injections? |
|
Use prepared statements. [CppDB library](http://art-blog.no-ip.info/sql/cppdb/) provides all the tools you need. |
|
Just rewrite the example above as |
|
sql << "SELECT 1 FROM users " |
"WHERE username=? AND password=?" |
<< username << password; |
|
Now the values of ? would be _properly_ substituted |
with the correct values. |
|
The query and values would be physically separated. |
|
## Sessions |
|
The HTTP protocol is stateless so in order to keep track of the users on our web site we store information in the session. For example: |
|
session().set("username",username); |
|
Now we can know who the user is. Now if we have |
the `username` in the session we know what he can |
do for example. |
|
There are two ways to store the data in the session: |
|
1. Store all the data server side, with a _unique_ |
session id in the client side cookie. |
2. Store all the data in a client side cookie and |
we ensure its validity by digitally signing it. |
|
See: [CppCMS Configuration: session](http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_config#session) |
|
This cookie content is very valuable information, as if an attacker gets this cookie he/she can impersonate another user. |
|
### What to store in the session? |
|
It's OK to store low value information like user name, user id, and maybe some properties that the user set themselves. |
|
However there are several things you should not |
keep in session: user permissions and rights. |
|
Remember that each user can have more then one active |
session. Consider you store his rights in the session, |
now we want to drop his right and we can't as we don't |
know which sessions belong to the user. |
|
|
### Session handling is cookie based |
|
In CppCMS session handing is cookie based and |
only cookie based, so trust CppCMS and don't |
reinvent your own methods. |
|
There are many popular methods to store session |
id that have design flaws. For example, consider |
a popular method of keeping session id in the query string: |
|
http://www.mybank.com/?sid=1234564435 |
|
Now publish a link `http://attackers_site.com/` on the |
`www.mybank.com` web site. The innocent user |
clicks on this link the attacker get user's session id |
in the Referrer's HTTP header. |
Now the attacker can go to `www.mybank.com` add `?sid=1234564435` to the URL and transfer all of the user's money to his account. |
|
So don't reinvent the wheel, use CppCMS's session |
mechanism that handles this safely for you. |
|
### Client side storage considerations (Replay attack) |
|
Client side storage is very useful as it allows you to |
keep the user data client side without needing to keep track of file system, or use a database for session storage. |
|
This is especially useful for high performance |
applications. |
|
However client side storage has one important |
security problem - we have no way to invalidate it |
by anything way other than a timeout. |
|
Consider a situation where we're playing a game where |
on each level we need to select one answer from four. |
|
Now consider the user has made a mistake. He is not |
pleased and he restores his previous cookie and goes back to the same level again - he 'winds back time'. |
|
There is no way to reset his session to level 1 because |
he can always go back in time. |
|
So client side storage is a very powerful and useful tool, but you should be very careful when using it. |
|
### Server side storage |
|
When using server side storage on file system, |
it is very good idea to change the default directory |
When using server side storage on a file system, |
it is a very good idea to change the default directory |
that CppCMS stores sessions in `/tmp/cppcms_sessions` |
to some other location so it would be harder to |
to access this data in case of security exploits |
on maybe some other parts of program. |
to some other location so it would be harder to access this data in case of security exploits on maybe some other part of the program. |
|
For example if the attacker gets the listing of this |
directory he would know all session ids in the system. |
|
|
### Hiding session data. |
|
Sometimes it is useful to store some data in the |
session the way the user would not know what it contains. |
|
There is no problem when you are using server side storage, |
however when you use client side storage you need |
to encrypt the data. |
|
CppCMS provides an option to use encryption such that |
the data stored on the client side would be [automatically |
encrypted](http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_config#session.client.cbc) |
|
Use it if you don't want user to see the data you |
store in its session. |
|
### Session expiration |
|
There are two basic methods used for session expiration: fixed and renew. See [session.expire](http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_config#session.expire) |
|
1. `fixed` - the session would expire in fixed period of time since its creation. Use this for all critical parts. Because if the session is hijacked it would limit the amount |
of time the attacker can spend on the web site. |
2. `renew` - the session is automatically renewed on every visit. It is very user friendly policy, but it allows |
to keep the session virtually forever. |
|
Now when bad things happen and we want to invalidate |
all existing sessions. |
|
- Server side storage: just clear the directory that |
keeps the sessions. By default it is `/tmp/cppcms_sessions/` |
- Client side storage: change the secret key of the |
the hmac signature. |
|
### Session Fixation |
|
This is a kind of attack that is used for impersonating |
other users. |
|
Consider example: |
|
1. Attacker goes to a bank side and receives a session id - special cookie. |
2. Attacker injects this cookie to the innocent user using |
some kind of XSS attack (see below). |
3. User logs in into bank, because the had received the |
cookie he already has the session id and this session id |
receives some additional rights like to do operations |
on the bank account. |
4. Attacker goes to the bank site using the same cookie |
stored in the user's browser and performs any kind |
of operations user can do. |
|
How to prevent this? |
|
cppcms::session_interface::reset_session(); |
|
By calling this function during the critical operations |
like user log-in the session id would be replaced with |
a new one: |
|
if(authentication_ok()) { |
session().reset_session(); |
session().set("user_id",id); |
} |
|
This simple operation would prevent session fixation attacks. |
|
### Client side session private keys |
|
In order to use client side session storage |
you need to create a secret private keys for this |
purpose. |
|
They can be easily generated using `cppcms_make_key` tool. |
|
Don't forget that these keys are top secret, if |
attacker discovers them he would be able to escalate |
his rights to any level and forge any session data. |
|
|
## Cross Site Scripting (XSS) |
|
[Cross Site Scripting](http://en.wikipedia.org/wiki/Cross-site_scripting) in one of the most dangerous problems |
web developers should deal with. |
|
Most web application deal with content, sometimes it |
may be very rich HTML content. Allowing user to |
set his own HTML content as-is provides lots of |
tool for attacking web site via injecting JavaScript |
and for example storing uses session cookies, |
and escalating user privileges to much higher ones, |
defacing web sites and much more |
|
Many dangerous things can be done even without |
writing Java Script code but only using simple |
CSS: [Clickjacking](http://en.wikipedia.org/wiki/Clickjacking), [defacement](http://en.wikipedia.org/wiki/Website_defacement) and more. |
|
So if you want to display a rich content you are |
most likely need to apply some XSS filters to prevent |
bad things. |
|
### Do not allow HTML |
|
First and most basic rule... Do not allow user to |
write HTML content on the web site. |
|
This is done by template system by default. Any value |
that is displayed on the web site is escaped by default |
such that HTTP attributes like `<` are |
converted to the save values like `<`. |
|
However you should notice two things: |
|
1. This does not makes it safe if you, |
for example write the value inside `href` attribute. |
|
You should use `urlencode` filter (done automatically |
with `<% url %>` url-mapper. |
|
Consider: |
|
<a href='/blog/post/<%= title %>'> |
|
Now user creates a title |
|
' onmouseover='alert(XSS)' ' |
|
And even after escaping he gets: |
|
<a href='/blog/post/' onmouseover='alert(XSS)' > |
|
Which allow code execution. |
|
Using `<% url %>` tag or urlencode filter would solve the problem: |
|
<a href='<% url "/post/" using tile %>'> |
|
or |
|
<a href='/blog/post/<%= title | urlencode %>'> |
|
2. When you inject some content to the attributes, |
always use double quotes `"` as they are escaped |
by the default filter, while. the single quotes |
are untouched. |
|
This is correct code: |
|
<img alt="<%= alternative %>" ... > |
|
This one may lead to XSS: |
|
<img alt='<%= alternative %>' ... > |
|
If alternative is `text' onclick="alert(xss)" class='`. |
would generate a code like: |
|
<img alt='text' onclick="alert(xss)" class='' ... |
|
2. The escape filter is applied only when no other |
filters are used so if you use stuff like this: |
|
<h1><%= title | to_title %></h1> |
|
The content would not be escaped as one filter is already used. So you need to add `escape` filter explicitly |
|
|
<h1><%= title | to_title | escape %></h1> |
|
|
### XSS Filter |
|
Sometimes we do need rich HTML content. In such cases |
we must ensure that the generated HTML do not include |
malicious code. |
|
CppCMS provides XSS filters that allow to filter HTML |
content using strict white-list rules. |
Then can be found under [`cppcms::xss`](http://art-blog.no-ip.info/cppcms_ref_v0_99/namespacecppcms_1_1xss.html) namespace. |
|
The filter uses a predefined set of rules that define |
what HTML tags and attributes can be used. |
|
The web developer should carefully select the tags |
ones allow to use for content. Special care should be |
given for url and style attributes. |
|
Starting from CppCMS 0.99.10 it is possible to create |
policy file using simple JSON format. |
|
The policy can be loaded from file by simple calling: |
|
cppcms::xss::rules rules(path_to_file); |
|
And then content can be filtered as: |
|
safe = cppcms::xss:filter(unsafe,rules); |
|
It would validate the input and remove all tags and |
properties that do not fit this policy. |
|
For example, this JSON file defiles the simple rule that |
can be used with basic TinyMCE WYSIWYG editor: |
|
{ |
"encoding" : "UTF-8", |
"entities" : [ "nbsp" ], |
"tags" : { |
"opening_and_closing" : [ |
"p", |
"ul","ol","li", |
"strong", "em", |
"span", |
"a" |
], |
"stand_alone" : [ "br" , "hr" ] |
}, |
"attributes" : [ |
{ |
"tags" : [ "a" ], |
"attributes" : [ "href" ], |
"type" : "absolute_uri" |
}, |
{ |
"tags" : [ "span" ], |
"attributes" : [ "style" ], |
"type" : "regex", |
"expression" : |
"(\\s*text-decoration\\s*:\\s*(underline|line-through)\\s*;)*" |
} |
] |
} |
|
|
Of course more complex input requires more complex rules. So |
when you write such rules make sure: |
|
1. You use only special `uri` attributes for URL tags like `href` or `src` - the primary candidate for Javascipt code injection. |
2. Be very careful and strict with allowed CSS styles - best - do not allow them or use very strict set of |
used styles. Remember CSS allows XSS injection as well using `url()` tags and is very "helpful" in creation of |
click-jacking attacks. |
|
|
### CSS Styles |
|
CSS styles can be very dangerous. Best practice - do |
not allow them at all! |
|
The simplest example would be a click-jacking. |
|
<a href="http://example.com/attack.html" |
style="display: block; z-index: 100000; |
opacity: 0.5; position: fixed; |
top: 0px; left: 0; |
width: 1000000px; height: 100000px; |
background-color: red;"> </a> |
|
Such style that does not include any URL or unsafe |
JavaScript code allows to steal clicks for users. |
|
So if you do allow CSS styles specify the exact |
attributes that you allow as it is shown in the example |
abive. |
|
### Remember! |
|
Vast majority of web attacks like session hijacking, |
session fixation, privileges escalation, impersonating, |
start with a simple XSS hole... |
|
## Cross Site Request Forgery (CSRF) |
|
### The attack |
Is yet another dangerous and frequently used attack. |
|
Consider a situation. On your web site you have a |
simple web form, that allows for example to update some |
content: |
|
<form method="post" action="/update"> |
<input type="hidden" name="id" value="1434"> |
<textarea name="content">...</textarea> |
<input type="submit" value="Update"> |
</form> |
|
It is placed on www.mysite.com. |
|
Now the controller code validates that the user |
that perform this operation allows to do it (has |
valid user in session with valid privileges) |
|
Now the attacker creates a small form on his site |
www.foe-cats.com that rates cute kittens and writes a |
simple form: |
|
<form method="post" action="http://mysite.com/update"> |
<input type="hidden" name="id" value="1434"> |
<input type="hidden" name="conent" value="My Site Sucks"> |
<input type="checkbox" ... > ... |
<input type="submit" value="Rate Kittens"> |
</form> |
|
The only task that remains for the attacker is to |
make sure that somebody with correct rights |
registered on the mysite.com would rate the |
kittens. |
|
Of course much more dangerous things could be done |
this way. |
|
### Prevention |
|
There are many different ways to do this, like |
never updating data on GET requests or |
checking referrer had been proven to have some |
security faults. |
|
Currently the best known technique is to use |
session specific secret token set withing the |
form request. |
|
In order to use CSRF prevention feature you need |
to do two things: |
|
1. Set [`security.csrf.enable`](/wikipp/en/page/cppcms_1x_config#security.csrf.enable) option to true, such that every session would have such token. |
2. Add hidden token to every POST form using `<% csrf %>` tag. |
|
For example: |
|
<form action="/url" method="post"><% csrf %> |
<% form as_p my_form %> |
</form> |
|
It would automatically inject the hidden input that would |
include session specific CSRF token: |
|
<input type="hidden" name="_csrf" value="df_d482"> |
|
And this token would be validated upon `cppcms::form::load` |
call automatically. |
|
This efficient and simple technique would prevent |
CSRF attacks. |
|
_Remember:_ it should be done for "POST" forms only as |
"GET" forms may disclose the secret token in Referrer |
header. |
|
### Login CSRF |
|
The CSRF protection depends on session validation, so in order to protect a form, a session must exists when the page is rendered (even for forms that have no other need for session values - like login forms.) |
|
This opens an opportunity for Login CSRF attack. |
|
Consider: |
|
- Bob uses Alice's web site `www.alice.com` |
- Eve creates an account on `www.alice.com` |
- Eve creates a web site `www.eves-kittens.com` and creates a simple form for rating cute kittens: |
|
<form action="http://www.alice.com/login" method="post" > |
<input type="hidden" name="user" value="Eve" > |
<input type="hidden" name="pass" value="xyz" > |
<input type="submit" value="Cute!"> |
</form> |
- Now Eve asks Bob to rate kittens and it does. Effectively logging himself in into `www.alice.com` on behave of Eve. |
- Now every operation that Bob does on the `www.alice.com` web site is performed on behave of Eve and now Eve can for example spy on Bob. Or trick Bob to transfer money to himself effectively transferring them to Eve. |
|
To protect the login method from Login CSRF attacks, the login() method should |
look something similar to this (this application pattern should be applied |
to all forms where a session isn't present upon the initial rendering of a |
form): |
|
void login() |
{ |
login_content c; |
|
if(request().request_method() == "POST" && session().is_set().("prelogin")) { |
c.myform.load(context()); |
if(c.myform.validate() && correct_login(myform))) { // perform form validation and then user validation |
session().reset_session(); |
session().erase("prelogin"); |
session().set("id",some_id_I_created); // or some other login id stored in the session |
response().set_redirect_header("/some/path"); |
return; |
} |
} |
|
session.set("prelogin",""); |
render("loginform",c); |
} |
|
This makes sure that the session is actually created |
prior to logging in into the web site and thus the |
CSRF token is created and checked. |
|
|
## Random numbers generation |
|
At some point you'll need to deal with random |
numbers generation. And [this sample](http://xkcd.com/221/) |
would be very good example how not to do things. |
|
However even if you thing that the code like: |
|
int getRandomNumber() |
{ |
return rand(); |
} |
|
|
Is better then your are wrong. |
|
Built in C and C++ library pseudo-random number generators |
are good for mathematical purposes but they are predictable. |
|
If you want truly random numbers (as for generating some |
UUIDs or generating some secret unguessable keys) use |
`cppcms::urandom_device` that uses OS services for |
generation of proper, cryptographically safe numbers |
that can be used for these purposes. |
|
## Character Set Considerations |
|
One of the things that can be used for exploits is |
messing up-with character set, providing invalid |
character set. |
|
There are many exploits that use invalid character |
set to do bad things, for example XSS. This is |
especially frequent with non-ASCII compatible |
encodings like Shift-JIS. |
|
So the recommendation is to use Unicode UTF-8 encoding |
anywhere and validate the input. |
|
CppCMS text widgets forms, JSON parser do this by default. |
However if for some reason you prefer to use POST/GET |
data directly like: |
|
msg = request().post("message"); |
|
Do yourself a favor and validate or filter text encoding using even very simple: |
|
msg = request().post("message"); |
msg = booster::locale::conv::utf_to_utf<char>( |
msg, |
booster::locale::conv::skip); |
|
That would remove all invalid UTF-8 sequences. |
|
## C and C++ Security |
|
I put this section C++ secure programming the |
last one mostly because most of C++ programmers |
know about these issues forgetting that there |
are lots of danger in web programming. |
|
### General Notes |
|
C++ language is considered unsafe - it does not provide |
array boundary checks by default, uses pointer and many |
other "dangerous stuff" |
|
However unlike C, C++ is high level language that |
provides abstraction for many things. So using |
modern language techniques programming in C++ is |
not more dangerous then programming in Java, Python or Ruby. |
|
### Buffer Overflow |
|
Is one of the classic problems: |
|
sprintf(small_buff,"%d",huge_number); |
strcpy(small_buffer,huge_string) |
|
These are probably the classic examples of buffer |
overflows... |
|
However, in C++ you have `std::string` and `std::stringstream` and allow you to prevent using |
these "dangerous" functions and write safer code. |
|
You can use tools like `booster::locale::format` and |
of course many tools that come with Boost libraries. |
|
Use them and not "old-school" tools, they will |
help you to write safe code and forget about |
this problem. |
|
### Dangling pointers |
|
Consider situation: |
|
foo *p=new foo(); |
... |
delete p; |
|
And accidentally on some later point. |
|
p->bar(); |
|
The problem that there is some unknown data located |
at the pointer. There are techniques can be used |
to execute miscellaneous during access to dangling pointer. |
|
The standard solution: |
|
delete p; |
p=0; |
|
Is good but it should be done manually and so somebody |
at some point will forget it. |
|
How to deal with it right? Use smart pointers. |
|
- `booster::shared_ptr` - smart reference counting pointer (or `std::shared_ptr` for C++0x compilers) |
- `std::auto_ptr` or `std::unique_ptr` for C++0x compilers |
- Other smart pointers with different copy policies in |
booster namespace `hold_ptr`, `clone_ptr`, `copy_ptr` and |
more. |
|
They will solve you actually 3 problems not 1. |
|
- Dangling pointers - they will reset them to 0 automatically. |
- Memory leaks - manage memory automatically for you |
- Make code exception safe such that, if the exception |
is thrown between `new` and `delete` the object will |
be properly destroyed by smart pointer. |
|
### Integer overflows |
|
Consider you are accepting from the user some number like |
a size of data chunk he is going to send and then |
add some data you add and ensure that it fits the |
required limits: |
|
unsigned user_data_size = // input from user |
char *user_data = ...; // some input |
unsigned my_data_size = sizeof(my_data); |
|
char buffer[SOME_LIMIT]; |
|
if(user_data_size + sizeof(my_data) <= sizeof(buffer)) { |
memcpy(buffer,some_data,user_data_size); |
memcpy(buffer+user_data_size, |
my_data,sizeof(my_data)); |
} |
|
What is the problem? The code looks perfectly correct |
but... lets set `user_data_size=0xFFFFFFFF` and lets |
assume that `sizeof(my_data)` is 4 then `user_data_size+sizeof(my_data)` would be 3 and deferentially |
smaller then `sizeof(buffer)` but the next `memcpy` |
would cause severe buffer overflow... |
|
How to prevent it? Always put the user supplied |
data on its own the comparison side. |
|
if(user_data_size <= sizeof(buffer)-sizeof(my_data)) { |
... |
} |
|
Note signess of the numbers compare signed with signed |
and unsigned with unsigned. |
|
|
## What happens if we still fail? |
|
Even if we take all precautions, we still, sooner |
or later will fail. |
|
The question is how to minimize the possible damage. |
|
### After Failure |
|
The most important thing you should care about is backups. |
|
You need to do _backups_ of the software, data |
files and of course the data base. |
|
But what is even more important is to check that |
you can restore your web site easily from the last |
backup. So make regular checks that your restore procedures |
work properly. So on the day the bad thing happens you |
will be prepared. |
|
It is important to keep logs - logs from your web |
server your application logs. In any case of suspicious |
operation... Report it to the log. It would be |
very helpful to understand what happened. |
|
If the application crashes in production environment |
it is very hard to understand what exactly happened. |
|
So when you deploy the application compile it with |
debug information. Use CMake's flag `-DCMAKE_BUILD_TYPE=RelWithDebInfo` or just use GCC's |
flags `-O2 -g`. Enable core dumps - if the program |
crashes you'll be able to open it with debugger |
and see what exactly had happened. |
|
|
### Running as limited user |
|
As we all know bugs happen and software crashes and |
crash may give an attacker an opportunity to execute |
his own code. |
|
So it is very good idea to run CppCMS under limited user. |
|
If you use a web server to control CppCMS's process life |
time for you, then most likely your process will run |
under underprivileged user, however if you use |
your own service don't forget these two configuration |
sections: |
|
- [daemon](/wikipp/en/page/cppcms_1x_config#daemon) - for Unix daemon |
- [winservice](/wikipp/en/page/cppcms_1x_config#winservice) - for Windows service. |
|
Create a limited user that can do almost anything and run |
the process under it. This way even if attacker succeeds |
to execute some code he will not be able to do too much. |
|
CppCMS's daemon allows to provide a `chroot` jail for |
the process significantly limiting the parts of the |
file system the process can access. The [daemon.chroot](/wikipp/en/page/cppcms_1x_config#daemon.chroot) option |
would make it much harder to exploit the security holes |
in the application. |
|
|
## Links |
|
- [Ruby On Rails Security Guide](http://guides.rubyonrails.org/security.html) - very good security programming guide. |
- [Open Web Application Security Project](https://www.owasp.org/index.php/Main_Page) |
- [XSS Cheat Sheet](http://ha.ckers.org/xss.html) - a wide set of examples of XSS attacks |