Websecurity through conventions and best practices
conventions and best practices
by Michał Szajbe (netguru.pl)
web apps are like
• web server layer
• back-end storage layer
• application layer
Web application does not run on it’s own.
Each web app runs in a speciﬁc environment which can be divided into few layers.
A web server layer which can be Apache, ISS, Tomcat or even the whole operating system.
Back-end storage layer which is the place where application data is stored, be it database,
ﬁlesystem or some external storage system like Amazon’s S3.
The last layer I outlined here is the application itself, so the layer that glues everything
together to make it work.
As I said before, I am a web developer so I will focus only on the application layer.
according to Gartner Group’s report (2005)
There is also another reason for that. According to Gartner Group’s report that was publisher
in year 2005, web application layer was a target of 75% of all attacksin the Web.
One of the reasons for this is probably the relative ease with which such attacks can be made.
In fact, almost all needed tools for this are available for everyone to use. For example
consider Firefox browser. It has plugins for everything. You can modify contents of the site,
manipulate request headers, easily analyze server responses and so on.
As you see the report I mention is 4 years old now, but I don’t think the numbers changed
much. In fact I believe that the share of the attacks on web app layer can be even bigger now.
That’s because of constantly changing approach to application development process.
Nowadays more and more applications are released as early as possible (there is release
early, release often approach). They are often labeled beta or even alpha to underline their
unstable conditions and the fact that not all functionalities are implemented yet. So if they
aren’t, they are very ofter not secured appropriately too.
• unauthorized access to sensitive data
• user account hijacking
• bypass of access control
• malicious content injection
No matter if we’re talking about security of applications, servers or networks, consequences
of insecure systems can be pretty much the same.
• consistent application structure
• development with conventions in mind
• code reusability
• faster development
• large communities for open-source projects
• ...but no plug and play security
So how is software for web developed?
Almost every modern applications is built with some kind of web framework, be it an open-
source one or self-developed set of libraries. The main advantage of using framework is that
they standardize the whole thing. They promote certain philosophy of doing things. They
make the whole process faster. Other aspect is that for large open source frameworks like
RoR we can use other developers’ experience and put it almost directly into our work.
However even the best web framework is just a set of blocks, you need to put together. So
with web security, you get tools and need to apply them yourself.
with examples from Ruby on Rails
So let’s go to the main topic. I will cover some security issues that need to be often faced,
and I will give some examples on how to deal with them. The examples come from Ruby on
Rails but similar techniques or approaches can be also applied in other languages and
Let’s start with user passwords management, as most of the apps oers such things are
registrations and signing in.
hash user passwords
Pretty obvious thing on the beginning.
You should never store passwords of your users as plain text. Hash them instead with some
hashing function like MD5, SHA1, SHA512.
The example here shows how this can be easily done in Rails with use of Authlogic plugin.
All you need to do is to deﬁne the database table structure for storing passwords and add
one line to user model. Now every time before new password is going to be saved, it will be
hashed ﬁrst. The salt is a random value that is used during the hashing to make the same
plain-text password produce dierent hashes.
Authlogic also does the automatic hashing of password when we’re authenticating user. So
the hashes of passwords are compared, not plain text passwords.
passwords to users
Web frameworks oer many cool features. The one is that when user submits a form and the
input is incorrect, the form is redisplayed to the user with values preserved from his last
request so he does not need to supply all the values again. This is certainly great but no in all
cases. Passwords should never be redisplayed in these situations. User will see the password
as string of stars or dots, but the password will be visible as plain text is HTML source of the
page. This could be either snied or retrieved later from browser cache.
don’t remind passwords,
You should also never remind forgotten passwords to your users. Instead send them a one-
time authorization token with which they can change their password. Again this is easily done
with Rails and Authlogic. You only need to add one ﬁeld to you database table. As the name
says the token perishes right after use. In this case every time a record is updated, so after
the user changes the password, the token will be no longer valid.
You should probably also encourage your users to use not-weak passwords. Rails and most
other web dev framework have some set of default validation rules deﬁned that will help you
ensure that the password meets certain conditions.
You should deﬁne minimal requirements for passwords but you shouldn’t limit your users.
I have seen some sites that allowed only use of letters and digits in passwords. I have no idea
why, because if user wants to use some special characters, why not let him do so? This would
make his password stronger.
Since we have users, we need to handle their sessions.
store session data on
...or at least verify cookie session
You should store session data on the server instead of the cookie. Cookie should only carry
the session id. The rest should be stored server-side.
By storing all data in the cookie you not only let the client manipulate it but also make the
If you however decide that you still want to use cookie store at least try to verify session data
integrity. Calculate digest of from a session data with use of secret value stored on the server
and place that digest in the cookie.
regenerate session id
Another thing is that you should always reset session id when user successfully signs in.
This prevents from session ﬁxation attacks.
Session ﬁxation attacks work like this. Attacker navigates to the site to obtain a cookie. Then
he forces another use to use that cookie while signing in to our site. After the user signs in,
attacker has authorized cookie and can act on behalf of the other user.
So you should always assign a new id to the session when users logs in.
As you can see this is another one-liner in Rails. You just need to call reset_session method.
...and/or encourage users to log out
You should also make sure that you don’t keep old and unused sessions too long. Expire old
sessions. Again one line of conﬁguration in Rails. You should also make the log out link
visible so that users can destroy their own session.
Keeping application data integral is a critical thing.
You need to make sure that your app behaves in the way it’s supposed to, so do not let any
wrong input in.
use server-side validation
Client-side validation is nice for user experience.
It also helps to improve performance a little, because when user tries to send a form that we
know that does not pass our validation checks, we show him errors immediately without
sending anything to the server.
is easy to go round.
Rails and other frameworks have some default validation mechanism that help to check some
basic conditions and display appropriate error messages when needed. You can also easily
deﬁne you own validation check, so the validation can be done really quickly and with little
protect sensitive data
from being overwritten
Beside validation we still need to protect critical attributes from being overwritten.
Check the last snippet ﬁrst.
This is a usual method of updating records in many frameworks. We pass a set of attributes’
values to the model and the record is updated.
This has some security ﬂaws however. User can set some attributed we don’t want for him to
change, even if the corresponding ﬁelds are not present in html form on the edit page.
For example he could set an admin ﬂag to 1 and become an admin.
There are two approaches to defend from this.
In whitelist approach you tell explicitely which ﬁelds can be updated via mass-assignment,
and the blacklist approach in which you tell which ﬁelds can’t be updated via mass-
don’t correct invalid input
In most cases you should only check whether your data pass your validations rule or not. You
shouldn’t try to correct data that doesn’t. For example if you don’t want script tags, you
should deny data that contains them, you shouldn’t try to remove them.
As you can see on the example you’d have been tricked if you did so.
escape user input
When you do any queries to the database you should always make sure that you escape all
values for parameters that come from outside of the app (for example from user).
As you can see on examples above user can manipulate params in a way that somthing more
will be executed along with the normal database query.
In the ﬁrst example user makes himself an admin, in the second he signs in as ad admin with
Rails oers some standard some mechanisms that act like proxies to database queries. You
can see how the same queries are done in Rails, then the values will be automatically escaped
and no unwanted queries will be executed.
escape application output
Escaping user input is important, but it is also important that we escape the app output.
In some situations we want to allow users to save everything they want to database.
But then we need to make sure that this won’t hurt another user.
executed in the browser it outputted normally.
So we need to encode some special characters with html entities to revome their special
escape response headers
You should also escape your response headers if they contain values that can be supplied by
The common patter is that we want to redirect user to the page he came from. Or the return
page is set in the URL.
This could be easily taken advantage of. So we should limit the return pages to the adresses
that are relative to our server.
REST stays for Representative State Transfer.
read with GET,
write with POST
I like to think of it as a way of limiting the entry points to the application while keeping
application structure and interface very consistent.
Here you can see a controller that implements typical CRUD operations in restful manner.
In rest GET request are used only to retrieve the data from the server, and POST/PUT/DELETE
request are used to modify data.
In fact some browser do not support PUT and DELETE requests, so they are simulated with
POST requests with additional param _method set to deﬁne which request should the server
verify authenticity of
In Rails it is very easy to create links and forms that invoke given types of request. It is done
behind the scenes. For example if we create a html form for some object Rails detects what
request should it use to send the form. If we’re creating a new record POST will be user, if
we’re updating it will use PUT.
In HTML all links are executed as GET requests, however Rails can deal with this by
form a then sends it.
There is also a method to make sure that all AJAX and non-GET requests are authentic. Rails
automatically attach authenticity token to all non-GET and AJAX requests that is calculated
from session and secret stored on the server. If the authenticity token check fails, the request
will not be handles. It is again a one-liner to turn this behavior on.
File uploads are another way to input data to an application so we should also take some
don’t trust ﬁle extensions,
• ﬁlename and extension can be easily
• the same stands for content type
• ﬁle header can contain malicious code that
may be executed in browser when user
clicks on malformed image
As always we can’t trust users to supply only ﬁles they’re supposed to.
Even if the ﬁle looks harmless it may contain some malicious code.
For example user can upload a malformed image with some JS code in ﬁle header. That image
would render normally in the browser, however when another user clicks it, the JS will be
executed. I am not sure how big a threat it is now, but it was possible to exploit in some
older browsers for sure.
don’t let users choose
ﬁle save path
We should always decide ourselves where we want to save uploaded ﬁles. We should also
normalize ﬁlenames. This will prevent us from overwriting important ﬁles. In this example we
use a Rails Paperclip plugin that handles ﬁle uploads in Rails.
As you can see we save the uploaded avatars in public directory, with the ﬁlename changed
to user’s id.
store ﬁles below
• everything in DocumentRoot is served by
the web server
• uploaded php or cgi ﬁle may be executed
by the web server when someone
downloads the ﬁle
If we let the users upload any ﬁle they want, we need to make sure to save the ﬁles in a path
that is not executable by the web server, so we should save the ﬁles at least one level
downwards from web server’s document root.
• save the ﬁle
• schedule it for processing
• handle the processing in the background
with another process
Btw paperclip let’s us do some other cool things with our ﬁles like generating image
thumbnails or do some other ﬁle processing when we save the ﬁle.
Such operations can be time consuming especially when we’re processing media ﬁles.
And if there are many ﬁles to process at once we can run out of resources.
Solution to this is to only save ﬁle and schedule it for later processing in background.
Probably on some other machine. Otherwise there is a possibility that we can face DOS attack
when there are many ﬁles being processed at once.
control what can be
Another thing is that we should also keep control on what users are downloading from our
For example we may store some private ﬁles like invoices that should be available to
download only by authorized users.
In such cases these ﬁles should not be stored in a directory that is publicly available. Instead,
they should be streamed to user after verifying his authenticity.