---[ 0x01: Introduction ]
-----------------------------------------------------------------------------[/]
---[ 0X02: Possible Solutions ]
------[ 0x03a: Cookies Hashing ]
<?php
// Cookie value
$value = "Something from Somewhere";
// Create a cookie which expires in one hour
setcookie("cookie", $value, time()+3600);
?>
<!-- EOF -->
We use an hashing of the Cookie to make the form verified
<!-- form.php -->
<?php
// Hash the cookie
$hash = md5($_COOKIE['cookie']);
?>
<form method="POST" action="resolve.php">
<input type="text" name="first_name">
<input type="text" name="last_name">
<input type="hidden" name="check" value="<?=$hash;?>">
<input type="submit" name="submit" value="Submit">
</form>
<!-- EOF -->
And the action script would be something like following:
<!-- resolve.php -->
<?php
// Check if the "check" var exists
if(isset($_POST['check'])) {
$hash = md5($_COOKIE['cookie']);
// Check if the values coincide
if($_POST['check'] == $hash) {
do_something();
} else {
echo "Malicious Request!";
}
} else {
echo "Malicious Request!";
}
?>
<!-- EOF -->
------[ 0x04b: HTTP Referer ]
Let's see why it's not a proper solution..
The following code shows an implementing example of HTTP Referer:
<!-- check.php -->
if(eregi("www.playhack.net", $_SERVER['HTTP_REFERER'])) {
do_something();
} else {
echo "Malicious Request!";
}
<!-- EOF -->
------[ 0x05c: CAPTCHA Image ]
In the same page you can find also a web test made from Secunia Staff.
---[ 0x06: One-Time Tokens ]
<?php
function gen_token() {
// Generate the md5 hash of a randomized uniq id
$hash = md5(uniqid(rand(), true));
// Select a random number between 1 and 24 (32-8)
$n = rand(1, 24);
// Generate the token retrieving a part of the hash starting from
// the random N number with 8 of lenght
$token = substr($hash, $n, 8);
return $token;
}
?>
<!-- EOF -->
Let's now generate a Session Token which will be used later for the last check:
<!-- start function -->
<?php
function gen_stoken() {
// Call the function to generate the token
$token = gen_token();
// Destroy any eventually Session Token variable
destroy_stoken();
// Create the Session Token variable
session_register(STOKEN_NAME);
$_SESSION[STOKEN_NAME] = $token;
}
?>
<!-- EOF -->
<?php
function gen_input() {
// Call the function to generate the Session Token variable
gen_stoken();
// Generate the form input code
echo "<input type=\"hidden\" name=\"" . FTOKEN_NAME . "\"
value=\"" . $_SESSION[STOKEN_NAME] . "\">\n";
}
?>
<!-- EOF -->
<?php
function token_check() {
// Check if the Session Token exists
if(is_stoken()) {
// Check if the request has been sent
if(isset($_REQUEST[FTOKEN_NAME])) {
// If the Form Token is different from Session Token
// it's a malicious request
if($_REQUEST[FTOKEN_NAME] != $_SESSION[STOKEN_NAME]) {
gen_error(1);
destroy_stoken();
exit();
} else {
destroy_stoken();
}
// If it isn't then it's a malicious request
} else {
gen_error(2);
destroy_stoken();
exit();
}
// If it isn't then it's a malicious request
} else {
gen_error(3);
destroy_stoken();
exit();
}
}
?>
<!-- EOF -->
The use of these functions is really simple we just need to just add some additional PHP instructions.
This is the webform:
<!-- form.php -->
<?php
session_start();
include("functions.php");
?>
<form method="POST" action="resolve.php">
<input type="text" name="first_name">
<input type="text" name="last_name">
<!-- Call the function to generate the hidden input -->
<? gen_input(); ?>
<input type="submit" name="submit" value="Submit">
</FORM>
<!-- EOF -->
And this is the resolving script:
<!-- resolve.php -->
<?php
session_start();
include("functions.php");
// Call the function to make the check
token_check();
// Your code
...
?>
<!-- EOF -->
---[ 0x07 Conclusions ]
Cya!
nexus
-----------------------------------------------------------------------------[/]
\======================================[EOF]=====================================/
I already dealt with the Cross Site Request Forgery
topic, but not too deep regarding the possible solutions that web
developers should adopt.
In these days i've been deeply involved in this topic during the coding of a distributed web application which should warrant a good level of security for user and most of all for the system's administrators (who are not that smart though their tasks :P).
Considering this situation i had to consider each aspect and each possible attack attempts that the applications could eventually suffer.
The one that gave me most problems to apply has been the Session Riding (or CSRF, call it as you prefere) because there is no 100% certain way to prevent that due to the concept that the attack fully stands on the users' credentials.
If you don't really know what Session Riding is you should read the previous paper reachable at the following link:
http://www.playhack.net/view.php?id=30In these days i've been deeply involved in this topic during the coding of a distributed web application which should warrant a good level of security for user and most of all for the system's administrators (who are not that smart though their tasks :P).
Considering this situation i had to consider each aspect and each possible attack attempts that the applications could eventually suffer.
The one that gave me most problems to apply has been the Session Riding (or CSRF, call it as you prefere) because there is no 100% certain way to prevent that due to the concept that the attack fully stands on the users' credentials.
If you don't really know what Session Riding is you should read the previous paper reachable at the following link:
-----------------------------------------------------------------------------[/]
---[ 0X02: Possible Solutions ]
Ok, from here i must assume that you quite deeply know how a Session Riding attack should work out :P
Let's just have a little and fresh resume..
Let's just have a little and fresh resume..
Consider that a trusted user is logged into a
website which permits him to accomplish some important or confidential
actions, an attacker wants to get over a possible login attack (which
often will be voided) and disfrut the opened session of the user in
order to realize some crafted actions.
The attacker in order to hijack the user's session will craft an appropriate web page which will hide a javascript function that re-create an original form but with fixed values, then he'll make the victim visit that page which on load will submit the form to the remote action page which will accomplish the request secretely (without the victim's aknowledge) confying on the user's trusted credentials.
This is a as quick as simple explaining of how a Session Riding attack would work out, the important question now is "How do i prevent my users to be victims of that?".
Now you would probably consider the following solutions:
- Check some cookies credentials
- Check the HTTP Referer
- Use a CAPTCHA
With some tryouts you'll realize that these are not the most proper solutions to adopt, let's see why one by one.
-----------------------------------------------------------------------------[/]The attacker in order to hijack the user's session will craft an appropriate web page which will hide a javascript function that re-create an original form but with fixed values, then he'll make the victim visit that page which on load will submit the form to the remote action page which will accomplish the request secretely (without the victim's aknowledge) confying on the user's trusted credentials.
This is a as quick as simple explaining of how a Session Riding attack would work out, the important question now is "How do i prevent my users to be victims of that?".
Now you would probably consider the following solutions:
- Check some cookies credentials
- Check the HTTP Referer
- Use a CAPTCHA
With some tryouts you'll realize that these are not the most proper solutions to adopt, let's see why one by one.
------[ 0x03a: Cookies Hashing ]
This first one could be a very simple and quick
solutions to this problem because the attacker should not be aware of
the victim's cookies contents, and cannot craft then a proper
workaround.
An implementation of this solutions would work out in a way like following.
In some login file we create the Cookie related to the current session:
<!-- login.php -->An implementation of this solutions would work out in a way like following.
In some login file we create the Cookie related to the current session:
<?php
// Cookie value
$value = "Something from Somewhere";
// Create a cookie which expires in one hour
setcookie("cookie", $value, time()+3600);
?>
<!-- EOF -->
We use an hashing of the Cookie to make the form verified
<!-- form.php -->
<?php
// Hash the cookie
$hash = md5($_COOKIE['cookie']);
?>
<form method="POST" action="resolve.php">
<input type="text" name="first_name">
<input type="text" name="last_name">
<input type="hidden" name="check" value="<?=$hash;?>">
<input type="submit" name="submit" value="Submit">
</form>
<!-- EOF -->
And the action script would be something like following:
<!-- resolve.php -->
<?php
// Check if the "check" var exists
if(isset($_POST['check'])) {
$hash = md5($_COOKIE['cookie']);
// Check if the values coincide
if($_POST['check'] == $hash) {
do_something();
} else {
echo "Malicious Request!";
}
} else {
echo "Malicious Request!";
}
?>
<!-- EOF -->
Actually this would be a fine solution to CSRF if
we wouldn't consider the fact that the user's cookie are a way easy to
retrieve disfruting some XSS flaw in the website (that we previousy saw
they are not a rarity :D). It would be more secure if we create and
destroy a random cookie for each form request that the user makes, but
it wouldn't be very comfortable.
-----------------------------------------------------------------------------[/]------[ 0x04b: HTTP Referer ]
The easiest way to check if the incoming requests
are trusted and allowed, is to disfrut the HTTP Referer and check if
they come from the same website or from a remote malicious page: this
would be a great solution, but it falls due to the possibility to spoof
the referers and make them appear to be corrects.
Let's see why it's not a proper solution..
The following code shows an implementing example of HTTP Referer:
<!-- check.php -->
if(eregi("www.playhack.net", $_SERVER['HTTP_REFERER'])) {
do_something();
} else {
echo "Malicious Request!";
}
<!-- EOF -->
This check can be easily bypassed forging a fake HTTP Referer from the attacker's script using something like:
header("Referer: www.playhack.net");
Or any other way to forge the Headers to be sent from the malicious script.
Since the HTTP Referer is sent by the browser and not controlled by the server you should never consider this variable as a trusted source!
-----------------------------------------------------------------------------[/]Since the HTTP Referer is sent by the browser and not controlled by the server you should never consider this variable as a trusted source!
------[ 0x05c: CAPTCHA Image ]
Another idea that came out in order to solve this
issue is to use a random CAPTCHA image in every form which require the
user to type in a textbox the generated string and with that verify the
integrity of the submitted datas and the credentials of the user.
This solutions has been discarded some time ago due
to the possibility to retrieve the captcha image using the so called
MHTML bug, which affected several versions of Microsoft Internet
Explorer.
You can find all the specific informations about this vulnerability from the Secuniawebsite:
http://secunia.com/advisories/19738/
Here's an extract from the Secunia's explanation of the bug:
"The vulnerability is caused due to an error in the handling of redirections for URLs with the 'mhtml:' URI handler. This can be exploited to access documents served from another web site."
"The vulnerability is caused due to an error in the handling of redirections for URLs with the 'mhtml:' URI handler. This can be exploited to access documents served from another web site."
In the same page you can find also a web test made from Secunia Staff.
Actually this bug is known to be solved with
several patches released by Microsoft for Windows XP and Windows Vista
and with the release 6.0 of their own browser Internet Explorer.
Even if actually it appears to be secure, the times brought to others possible and theoretically more reliable solutions.
-----------------------------------------------------------------------------[/]---[ 0x06: One-Time Tokens ]
Now let's explain the final solution i decided to
adopt for my job: after having dealt with all of these unreliable
techniques i tried to write something different and probably more
effective.
In order to make the webforms safe from Session Riding (CSRF) i decided to make the checking free from any kind of item that could be spoofed, retrieved or faked.
So i needed to create some one-time tokens that cannot be guessed or retrived in any way and that after having accomplished their task would have been destroyed.
Let's start from the token value generation:
<!-- start function -->In order to make the webforms safe from Session Riding (CSRF) i decided to make the checking free from any kind of item that could be spoofed, retrieved or faked.
So i needed to create some one-time tokens that cannot be guessed or retrived in any way and that after having accomplished their task would have been destroyed.
Let's start from the token value generation:
<?php
function gen_token() {
// Generate the md5 hash of a randomized uniq id
$hash = md5(uniqid(rand(), true));
// Select a random number between 1 and 24 (32-8)
$n = rand(1, 24);
// Generate the token retrieving a part of the hash starting from
// the random N number with 8 of lenght
$token = substr($hash, $n, 8);
return $token;
}
?>
<!-- EOF -->
The uniqid() PHP function allows the web developer
to get a unique ID from the current time in microseconds, which is quite
good in order to retrieve a value that won't be repeated again.
We retrieve the MD5 hashing of that ID, and then we select 8 characters from that hashing starting from a random number <=24 (strlen($hash)-8).
The returned $token variable will retrieve an 8-long randomized token.
We retrieve the MD5 hashing of that ID, and then we select 8 characters from that hashing starting from a random number <=24 (strlen($hash)-8).
The returned $token variable will retrieve an 8-long randomized token.
Let's now generate a Session Token which will be used later for the last check:
<!-- start function -->
<?php
function gen_stoken() {
// Call the function to generate the token
$token = gen_token();
// Destroy any eventually Session Token variable
destroy_stoken();
// Create the Session Token variable
session_register(STOKEN_NAME);
$_SESSION[STOKEN_NAME] = $token;
}
?>
<!-- EOF -->
With this function we call the gen_token() function and use the returned token to copy his value into a new $_SESSION variable.
Now let's see the function that will start the whole mechanism and that generates the hidden input for our form:
<!-- start function -->Now let's see the function that will start the whole mechanism and that generates the hidden input for our form:
<?php
function gen_input() {
// Call the function to generate the Session Token variable
gen_stoken();
// Generate the form input code
echo "<input type=\"hidden\" name=\"" . FTOKEN_NAME . "\"
value=\"" . $_SESSION[STOKEN_NAME] . "\">\n";
}
?>
<!-- EOF -->
As we can see this function just call the
gen_stoken() function and create the HTML code for the hidden input that
will be included in the web form.
Let's now take a look to the function that make the check of the Session Token with the submitted hidden input:
<!-- start function -->Let's now take a look to the function that make the check of the Session Token with the submitted hidden input:
<?php
function token_check() {
// Check if the Session Token exists
if(is_stoken()) {
// Check if the request has been sent
if(isset($_REQUEST[FTOKEN_NAME])) {
// If the Form Token is different from Session Token
// it's a malicious request
if($_REQUEST[FTOKEN_NAME] != $_SESSION[STOKEN_NAME]) {
gen_error(1);
destroy_stoken();
exit();
} else {
destroy_stoken();
}
// If it isn't then it's a malicious request
} else {
gen_error(2);
destroy_stoken();
exit();
}
// If it isn't then it's a malicious request
} else {
gen_error(3);
destroy_stoken();
exit();
}
}
?>
<!-- EOF -->
This function check the existence of the
$_SESSION[STOKEN_NAME] and of $_REQUEST[FTOKEN_NAME] (i used the
$_REQUEST method in order to accept both GET and POST methods from the
form) and check if their values are the same: if they are the submitted
form is authorized.
The important point of this function is that at
every concluding step the tokens get destroyed and will be recreated
only at next web form page call.
The use of these functions is really simple we just need to just add some additional PHP instructions.
This is the webform:
<!-- form.php -->
<?php
session_start();
include("functions.php");
?>
<form method="POST" action="resolve.php">
<input type="text" name="first_name">
<input type="text" name="last_name">
<!-- Call the function to generate the hidden input -->
<? gen_input(); ?>
<input type="submit" name="submit" value="Submit">
</FORM>
<!-- EOF -->
And this is the resolving script:
<!-- resolve.php -->
<?php
session_start();
include("functions.php");
// Call the function to make the check
token_check();
// Your code
...
?>
<!-- EOF -->
As you can see is really simple to implement such a
check and it should protect your users from being hijacked by attackers
and avoid to get your data compromised.
-----------------------------------------------------------------------------[/]---[ 0x07 Conclusions ]
Let's conclude this short paper saying that there
is no 100% way to be secure that your web application is completely
safe, but you can start avoiding the most common attacking techniques.
Another point that i'd like to make you focus on is that a web developer SHOULD NOT forget of general applications faults (like XSS Browser Bugs ecc..), it would be a great mistake not considering them as a potential threat for your users: you should always keep in mind everything that could compromise your application's integrity, security and interoperability.
Another point that i'd like to make you focus on is that a web developer SHOULD NOT forget of general applications faults (like XSS Browser Bugs ecc..), it would be a great mistake not considering them as a potential threat for your users: you should always keep in mind everything that could compromise your application's integrity, security and interoperability.
Cya!
nexus
(The above PHP codes were taken from the Seride project, hosted at http://projects.playhack.net/project.php?id=3)
-----------------------------------------------------------------------------[/]
\======================================[EOF]=====================================/
0 comments:
Post a Comment