Websecurity through conventions and best practices
1. Websecurity
through
conventions and best practices
by Michał Szajbe (netguru.pl)
2. web apps are like
onions
• web server layer
• back-end storage layer
• application layer
Web application does not run on it’s own.
Each web app runs in a specific 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,
filesystem 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.
3. attack targets
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.
4. possible threats
• 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.
5. web frameworks
• 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.
6. Conventions
and
best practices
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
frameworks.
7. User passwords
management
Let’s start with user passwords management, as most of the apps oers such things are
registrations and signing in.
8. hash user passwords
*Authlogic plugin
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 define 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 first. 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.
9. don’t redisplay
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.
10. don’t remind passwords,
reset them
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 field 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.
11. encourage strong
passwords
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 defined that will help you
ensure that the password meets certain conditions.
You should define 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.
13. store session data on
server
...or at least verify cookie session
integrity
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
requests slower, because cookies are sent with every request to the server.
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.
14. regenerate session id
on authentication
Another thing is that you should always reset session id when user successfully signs in.
This prevents from session fixation attacks.
Session fixation 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.
15. expire sessions
...and/or encourage users to log out
manually
You should also make sure that you don’t keep old and unused sessions too long. Expire old
sessions. Again one line of configuration in Rails. You should also make the log out link
visible so that users can destroy their own session.
16. Data validation
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.
17. 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.
However we still have to validate data again on the server, because such javascript validation
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
define you own validation check, so the validation can be done really quickly and with little
eort.
18. protect sensitive data
from being overwritten
Beside validation we still need to protect critical attributes from being overwritten.
Check the last snippet first.
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 flaws however. User can set some attributed we don’t want for him to
change, even if the corresponding fields are not present in html form on the edit page.
For example he could set an admin flag to 1 and become an admin.
There are two approaches to defend from this.
In whitelist approach you tell explicitely which fields can be updated via mass-assignment,
and the blacklist approach in which you tell which fields can’t be updated via mass-
assignment.
19. 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.
20. 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 first example user makes himself an admin, in the second he signs in as ad admin with
no password.
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.
21. 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.
For example user may save some html code and include some javascript code that would get
executed in the browser it outputted normally.
So we need to encode some special characters with html entities to revome their special
meaning.
22. escape response headers
You should also escape your response headers if they contain values that can be supplied by
the user.
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.
24. 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 define which request should the server
detect.
25. verify authenticity of
POST/PUT/DELETE requests
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
generating link with some javascript defined in onclick event. This JS builds a hidden HTML
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.
26. Files
File uploads are another way to input data to an application so we should also take some
precautions here.
27. don’t trust file extensions,
validate MIME
• filename and extension can be easily
manipulated
• the same stands for content type
• file 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 files they’re supposed to.
Even if the file looks harmless it may contain some malicious code.
For example user can upload a malformed image with some JS code in file 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.
28. don’t let users choose
file save path
*Paperclip plugin
We should always decide ourselves where we want to save uploaded files. We should also
normalize filenames. This will prevent us from overwriting important files. In this example we
use a Rails Paperclip plugin that handles file uploads in Rails.
As you can see we save the uploaded avatars in public directory, with the filename changed
to user’s id.
29. store files below
Apache DocumentRoot
• everything in DocumentRoot is served by
the web server
• uploaded php or cgi file may be executed
by the web server when someone
downloads the file
If we let the users upload any file they want, we need to make sure to save the files in a path
that is not executable by the web server, so we should save the files at least one level
downwards from web server’s document root.
30. process asynchronously
• save the file
• 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 files like generating image
thumbnails or do some other file processing when we save the file.
Such operations can be time consuming especially when we’re processing media files.
And if there are many files to process at once we can run out of resources.
Solution to this is to only save file 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 files being processed at once.
31. control what can be
downloaded
Another thing is that we should also keep control on what users are downloading from our
servers.
For example we may store some private files like invoices that should be available to
download only by authorized users.
In such cases these files should not be stored in a directory that is publicly available. Instead,
they should be streamed to user after verifying his authenticity.
33. Other things...
• don’t reveal your application structure
• don’t reveal what framework, libraries,
plugins you use
• keep your externals up-to-date
34. but don’t forget that...
security is just a feature, not the product