<!--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 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 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 in a way that the user does 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 the 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 a fixed period of time from 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 a very user friendly policy, but it allows you
|
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 an example:
|
|
1. Attacker goes to a bank site 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 then logs into the bank, because he's received the
|
cookie he already has the session id and this session id then receives some additional rights like to operate on the bank account.
|
4. Attacker then goes to the bank site using the same cookie
|
stored in the user's browser and performs any kind
|
of operations the 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 key for this
|
purpose.
|
|
They can be easily generated using `cppcms_make_key` tool.
|
|
Don't forget that these keys are top secret, if an 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) is one of the most dangerous problems
|
web developers have to deal with.
|
|
Most web application deal with content, sometimes it
|
may be very rich HTML content. Allowing a user to
|
set his own HTML content as-is provides lots of
|
vectors for attacking a web site via JavaScript injection and for example storing user session cookies,
|
and escalating user privileges to much higher ones,
|
defacing web sites and much more.
|
|
Many dangerous things can even be done without
|
writing JavaScript 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 rich content you will most likely need to apply some XSS filters to prevent bad things.
|
|
### Do not allow HTML
|
|
The first and most basic rule... do not allow users to
|
write HTML content on the web site.
|
|
This is done by the 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 an `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 "/blog/post/" using title %>'>
|
|
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 does not include
|
malicious code.
|
|
CppCMS provides XSS filters that allow you to filter HTML
|
content using strict white-list rules.
|
This 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
|
he wants to allow for content. Special care should be
|
given for url and style attributes.
|
|
Starting from CppCMS 0.99.10 it is possible to create a policy file using simple JSON format.
|
|
The policy can be loaded from file by simply calling:
|
|
cppcms::xss::rules rules(path_to_file);
|
|
And then content can be filtered:
|
|
safe = cppcms::xss:filter(unsafe,rules);
|
|
It would validate the input and remove all tags and
|
properties that do not fit the 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 the 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
|
above.
|
|
### Remember!
|
|
The vast majority of web attacks like session hijacking,
|
session fixation, privileges escalation, impersonating,
|
start with a simple XSS hole...
|
|
## Cross Site Request Forgery (CSRF)
|
|
Yet another dangerous and frequently used attack.
|
|
### The attack
|
|
Consider this situation. On your web site you have a
|
simple web form, that allows users 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 performs this operation is allowed to do it (a 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 tokens set within 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 a token.
|
2. Add the 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 the Referrer
|
header.
|
|
### Login CSRF
|
|
The CSRF protection depends on session validation, so in order to protect a form, a session must exist 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.
|
- Now Eve asks Bob to rate kittens and it does. Effectively logging himself in into `www.alice.com` on behalf of Eve.
|
- Now every operation that Bob does on the `www.alice.com` web site is performed on behalf 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 on C++ secure programming last mostly because C++ programmers typically know about these issues, forgetting that there are lots of dangers in web programming.
|
|
### General Notes
|
|
C++ language is often considered unsafe - it does not provide array boundary checks by default, uses pointers and many other "dangerous things".
|
|
However unlike C, C++ is a high level language that
|
provides abstraction for many things. So using
|
modern language techniques means programming in C++ is
|
no more dangerous than 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` which allow you to avoid using
|
these "dangerous" functions and write safer code.
|
|
You can use tools like `booster::locale::format` (and
|
of course many other tools that come with the Boost libraries).
|
|
Using them and avoiding "old-school" techniques will
|
help you to write safe code and forget about these problems.
|
|
### Dangling pointers
|
|
Consider situation:
|
|
foo *p=new foo();
|
...
|
delete p;
|
|
And accidentally on some later point.
|
|
p->bar();
|
|
The problem is that there is some unknown data located
|
at the pointer's target location. There are techniques that can be used to execute miscellaneous code during access to a 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 actually solve 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 the 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 definitely smaller than `sizeof(buffer)` but the next `memcpy`
|
would cause a severe buffer overflow...
|
|
How to prevent it? Always put the user supplied
|
data on its own with the comparison.
|
|
if(user_data_size <= sizeof(buffer)-sizeof(my_data)) {
|
...
|
}
|
|
Note signs 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 may 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 disaster strikes you'll be prepared.
|
|
It is important to keep logs - logs from your web
|
server and your application logs. In any case of suspicious
|
behaviour... 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 a limited user
|
|
As we all know bugs happen and software crashes, and
|
a crash may give an attacker an opportunity to execute
|
his own code.
|
|
So it is very good idea to run CppCMS under a 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 unprivileged 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 nothing other than run the process under it. This way even if an 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 |